diff --git a/.gitignore b/.gitignore index eeae0f84e381ea20a85a0db09edaa252672b1a11..43a4a5f2298f7d8e224b4a83620e81983ab295f2 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ # Ignore tests generated files *.db *.wot + +# Ignore coverage report +*-report.html diff --git a/tarpaulin-report.html b/tarpaulin-report.html deleted file mode 100644 index 0be72caaf4219a325f5cdb10e0d071d3f66035d5..0000000000000000000000000000000000000000 --- a/tarpaulin-report.html +++ /dev/null @@ -1,352 +0,0 @@ -<!doctype html> -<html> -<head> - <meta charset="utf-8"> - <style>html, body { - margin: 0; - padding: 0; -} - -.app { - margin: 10px; - padding: 0; -} - -.files-list { - margin: 10px 0 0; - width: 100%; - border-collapse: collapse; -} -.files-list__head { - border: 1px solid #999; -} -.files-list__head > tr > th { - padding: 10px; - border: 1px solid #999; - text-align: left; - font-weight: normal; - background: #ddd; -} -.files-list__body { -} -.files-list__file { - cursor: pointer; -} -.files-list__file:hover { - background: #ccf; -} -.files-list__file > td { - padding: 10px; - border: 1px solid #999; -} -.files-list__file > td:first-child::before { - content: '\01F4C4'; - margin-right: 1em; -} -.files-list__file_low { - background: #fcc; -} -.files-list__file_medium { - background: #ffc; -} -.files-list__file_high { - background: #cfc; -} -.files-list__file_folder > td:first-child::before { - content: '\01F4C1'; - margin-right: 1em; -} - -.file-header { - border: 1px solid #999; - display: flex; - justify-content: space-between; - align-items: center; -} - -.file-header__back { - margin: 10px; - cursor: pointer; - flex-shrink: 0; - flex-grow: 0; - text-decoration: underline; - color: #338; -} - -.file-header__name { - margin: 10px; - flex-shrink: 2; - flex-grow: 2; -} - -.file-header__stat { - margin: 10px; - flex-shrink: 0; - flex-grow: 0; -} - -.file-content { - margin: 10px 0 0; - border: 1px solid #999; - padding: 10px; -} - -.code-line { - margin: 0; - padding: 0.3em; - height: 1em; -} -.code-line_covered { - background: #cfc; -} -.code-line_uncovered { - background: #fcc; -} -</style> -</head> -<body> - <div id="root"></div> - <script>var data = {"files":[{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","bin","dunitrust-server","src","cli.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Command line options for classic Dunitrust nodes (no specialization).\n\nuse durs_core::commands::dbex::DbExOpt;\nuse durs_core::commands::keys::KeysOpt;\nuse durs_core::commands::modules::{DisableOpt, EnableOpt, ListModulesOpt};\nuse durs_core::commands::reset::ResetOpt;\nuse durs_core::commands::start::StartOpt;\nuse durs_core::commands::{\n DursCommand, DursCommandEnum, DursCoreCommand, DursCoreOptions, ExecutableModuleCommand,\n};\nuse durs_core::errors::DursCoreError;\nuse durs_core::DursCore;\nuse durs_network::cli::sync::SyncOpt;\nuse durs_ws2p_v1_legacy::{WS2POpt, WS2Pv1Module};\nuse log::Level;\nuse std::path::PathBuf;\nuse structopt::StructOpt;\n\n#[derive(StructOpt, Debug)]\n#[structopt(\n name = \"durs\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// Dunitrust command line options\npub struct DursCliOpt {\n /// Dunitrust subcommand\n #[structopt(subcommand)]\n cmd: DursCliSubCommand,\n /// Path where user profiles are persisted\n #[structopt(long = \"profiles-path\", parse(from_os_str))]\n profiles_path: Option\u003cPathBuf\u003e,\n /// Keypairs file path\n #[structopt(long = \"keypairs-file\", parse(from_os_str))]\n keypairs_file: Option\u003cPathBuf\u003e,\n /// Set log level. (Defaults to INFO).\n /// Available levels: [ERROR, WARN, INFO, DEBUG, TRACE]\n #[structopt(short = \"l\", long = \"logs\", raw(next_line_help = \"true\"))]\n logs_level: Option\u003cLevel\u003e,\n /// Print logs in standard output\n #[structopt(long = \"log-stdout\")]\n log_stdout: bool,\n /// Set a custom user profile name\n #[structopt(short = \"p\", long = \"profile-name\")]\n profile_name: Option\u003cString\u003e,\n}\n\nimpl ExecutableModuleCommand for DursCliOpt {\n /// Execute command\n fn execute_module_command(self, options: DursCoreOptions) -\u003e Result\u003c(), DursCoreError\u003e {\n match self.cmd {\n DursCliSubCommand::Ws2p1(module_opts) =\u003e {\n DursCore::execute_module_command::\u003cWS2Pv1Module\u003e(\n options,\n module_opts,\n env!(\"CARGO_PKG_NAME\"),\n env!(\"CARGO_PKG_VERSION\"),\n )\n }\n _ =\u003e unreachable!(),\n }\n }\n}\n\nimpl DursCliOpt {\n /// Into Dunitrust command\n pub fn into_durs_command(self) -\u003e DursCommand\u003cDursCliOpt\u003e {\n let options = DursCoreOptions {\n keypairs_file: self.keypairs_file.clone(),\n logs_level: self.logs_level,\n log_stdout: self.log_stdout,\n profile_name: self.profile_name.clone(),\n profiles_path: self.profiles_path.clone(),\n };\n\n match self.cmd {\n DursCliSubCommand::DbExOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::DbExOpt(opts)),\n },\n DursCliSubCommand::DisableOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::DisableOpt(opts)),\n },\n DursCliSubCommand::EnableOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::EnableOpt(opts)),\n },\n DursCliSubCommand::KeysOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::KeysOpt(opts)),\n },\n DursCliSubCommand::ListModulesOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::ListModulesOpt(opts)),\n },\n DursCliSubCommand::ResetOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::ResetOpt(opts)),\n },\n DursCliSubCommand::StartOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::StartOpt(opts)),\n },\n DursCliSubCommand::SyncOpt(opts) =\u003e DursCommand {\n options,\n command: DursCommandEnum::Core(DursCoreCommand::SyncOpt(opts)),\n },\n _ =\u003e DursCommand {\n options,\n command: DursCommandEnum::Other(self),\n },\n }\n }\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// Classic Dunitrust nodes subcommand\npub enum DursCliSubCommand {\n /// Database explorer\n #[structopt(\n name = \"dbex\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n DbExOpt(DbExOpt),\n /// Disable a module\n #[structopt(name = \"disable\")]\n DisableOpt(DisableOpt),\n /// Enable a module\n #[structopt(name = \"enable\")]\n EnableOpt(EnableOpt),\n /// Keys operations\n #[structopt(\n name = \"keys\",\n author = \"inso \u003cinso@tuta.io\u003e\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n KeysOpt(KeysOpt),\n /// List available modules\n #[structopt(name = \"modules\")]\n ListModulesOpt(ListModulesOpt),\n /// Reset data or conf or all\n #[structopt(\n name = \"reset\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n ResetOpt(ResetOpt),\n /// Start node\n #[structopt(name = \"start\")]\n StartOpt(StartOpt),\n /// Synchronize\n #[structopt(name = \"sync\")]\n SyncOpt(SyncOpt),\n /// WS2P1 module subcommand\n #[structopt(name = \"ws2p1\")]\n Ws2p1(WS2POpt),\n}\n","traces":[{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":24},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","bin","dunitrust-server","src","init.rs"],"content":"//use human_panic::setup_panic;\n\npub fn init() {\n //setup_panic!();\n}\n","traces":[{"line":3,"address":4396560,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":1},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","bin","dunitrust-server","src","main.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Main function for classic Dunitrust nodes (no specialization).\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\npub mod cli;\nmod init;\n\nuse crate::cli::DursCliOpt;\nuse crate::init::init;\nuse durs_core::durs_plug;\nuse log::error;\nuse structopt::StructOpt;\n\n#[cfg(unix)]\npub use durs_tui::TuiModule;\n//pub use durs_skeleton::SkeletonModule;\npub use durs_ws2p::WS2PModule;\npub use durs_ws2p_v1_legacy::WS2Pv1Module;\n\n/// Dunitrust cli main macro\nmacro_rules! durs_cli_main {\n ( $closure_plug:expr ) =\u003e {{\n init();\n if let Err(err) = DursCliOpt::from_args().into_durs_command().execute(\n env!(\"CARGO_PKG_NAME\"),\n env!(\"CARGO_PKG_VERSION\"),\n $closure_plug,\n ) {\n println!(\"{}\", err);\n error!(\"{}\", err);\n }\n }};\n}\n\n/// Dunitrust command line edition, main function\n#[cfg(unix)]\n#[cfg(not(target_arch = \"arm\"))]\nfn main() {\n durs_cli_main!(durs_plug!(\n [WS2Pv1Module, WS2PModule],\n [TuiModule /*, SkeletonModule ,DasaModule*/]\n ))\n}\n#[cfg(unix)]\n#[cfg(target_arch = \"arm\")]\nfn main() {\n durs_cli_main!(durs_plug!(\n [WS2Pv1Module, WS2PModule],\n [TuiModule /*, SkeletonModule*/]\n ))\n}\n#[cfg(windows)]\nfn main() {\n durs_cli_main!(durs_plug!([WS2Pv1Module, WS2PModule], []))\n}\n","traces":[{"line":1,"address":4223602,"length":1,"stats":{"Line":0}},{"line":63,"address":4632896,"length":1,"stats":{"Line":0}},{"line":64,"address":4632903,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":3},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","conf","src","keys.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Dunitrust keys configuration module\n\n#![deny(\n missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,\n trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,\n unused_qualifications\n)]\n\nuse crate::*;\nuse std::io;\n\n#[derive(Debug, Copy, Clone)]\n/// Errors encountered by the wizard\npub enum WizardError {\n /// Canceled\n Canceled,\n\n /// Bad input\n BadInput,\n}\n\nimpl From\u003cstd::io::Error\u003e for WizardError {\n fn from(_e: std::io::Error) -\u003e Self {\n WizardError::BadInput\n }\n}\n\n/// Modify network keys command\npub fn modify_network_keys(\n salt: \u0026str,\n password: \u0026str,\n mut key_pairs: DuniterKeyPairs,\n) -\u003e DuniterKeyPairs {\n let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();\n key_pairs.network_keypair =\n KeyPairEnum::Ed25519(generator.generate(salt.as_bytes(), password.as_bytes()));\n key_pairs\n}\n\n/// Modify member keys command\npub fn modify_member_keys(\n salt: \u0026str,\n password: \u0026str,\n mut key_pairs: DuniterKeyPairs,\n) -\u003e DuniterKeyPairs {\n let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();\n key_pairs.member_keypair = Some(KeyPairEnum::Ed25519(\n generator.generate(salt.as_bytes(), password.as_bytes()),\n ));\n key_pairs\n}\n\n/// Clear keys command\npub fn clear_keys(network: bool, member: bool, mut key_pairs: DuniterKeyPairs) -\u003e DuniterKeyPairs {\n if network {\n key_pairs.network_keypair = generate_random_keypair(KeysAlgo::Ed25519);\n }\n if member {\n key_pairs.member_keypair = None;\n }\n if !network \u0026\u0026 !member {\n println!(\"No key was cleared. Please specify a key to clear.\")\n }\n key_pairs\n}\n\n/// Show keys command\npub fn show_keys(key_pairs: DuniterKeyPairs) {\n println!(\"Network key: {}\", key_pairs.network_keypair);\n match key_pairs.member_keypair {\n None =\u003e println!(\"No member key configured\"),\n Some(key) =\u003e println!(\"Member key: {}\", key),\n }\n}\n\n/// Save keys after a command run\npub fn save_keypairs(\n profile_path: PathBuf,\n keypairs_file_path: \u0026Option\u003cPathBuf\u003e,\n key_pairs: DuniterKeyPairs,\n) -\u003e Result\u003c(), std::io::Error\u003e {\n let conf_keys_path: PathBuf = if let Some(keypairs_file_path) = keypairs_file_path {\n keypairs_file_path.to_path_buf()\n } else {\n let mut conf_keys_path = profile_path;\n conf_keys_path.push(crate::constants::KEYPAIRS_FILENAME);\n conf_keys_path\n };\n write_keypairs_file(\u0026conf_keys_path, \u0026key_pairs)?;\n Ok(())\n}\n\nfn question_prompt(question: \u0026str, answers: Vec\u003cString\u003e) -\u003e Result\u003cString, WizardError\u003e {\n let mut buf = String::new();\n\n println!(\"{} ({}):\", question, answers.join(\"/\"));\n let res = io::stdin().read_line(\u0026mut buf);\n\n match res {\n Ok(_) =\u003e {\n let answer = answers.into_iter().find(|x| x == buf.trim());\n match answer {\n Some(value) =\u003e Ok(value),\n None =\u003e Err(WizardError::Canceled),\n }\n }\n Err(_) =\u003e Err(WizardError::Canceled),\n }\n}\n\nfn salt_password_prompt() -\u003e Result\u003cKeyPairEnum, WizardError\u003e {\n let salt = rpassword::prompt_password_stdout(\"Salt: \")?;\n if !salt.is_empty() {\n let password = rpassword::prompt_password_stdout(\"Password: \")?;\n if !password.is_empty() {\n let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();\n let key_pairs = KeyPairEnum::Ed25519(generator.generate(\n salt.into_bytes().as_slice(),\n password.into_bytes().as_slice(),\n ));\n Ok(key_pairs)\n } else {\n Err(WizardError::BadInput)\n }\n } else {\n Err(WizardError::BadInput)\n }\n}\n\n/// The wizard key function\npub fn key_wizard(mut key_pairs: DuniterKeyPairs) -\u003e Result\u003cDuniterKeyPairs, WizardError\u003e {\n let mut answer = question_prompt(\n \"Modify your network keypair?\",\n vec![\"y\".to_string(), \"n\".to_string()],\n )?;\n if answer == \"y\" {\n key_pairs.network_keypair = salt_password_prompt()?;\n }\n\n answer = question_prompt(\n \"Modify your member keypair?\",\n vec![\"y\".to_string(), \"n\".to_string(), \"d\".to_string()],\n )?;\n if answer == \"y\" {\n key_pairs.member_keypair = Some(salt_password_prompt()?);\n } else if answer == \"d\" {\n println!(\"Deleting member keypair!\");\n key_pairs.member_keypair = None;\n }\n\n Ok(key_pairs)\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n static BASE58_SEC_INIT: \u0026'static str =\n \"4iXXx5GgRkZ85BVPwn8vFXvztdXAAa5yB573ErcAnngAgSVEknNqc16xRnCmsuHFAJ3j3XArB4mv8UVpvrG32vLV\";\n static BASE58_PUB_INIT: \u0026'static str = \"otDgSpKvKAPPmE1MUYxc3UQ3RtEnKYz4iGD3BmwKPzM\";\n //static SALT_INIT: \u0026'static str = \"initsalt\";\n //static PASSWORD_INIT: \u0026'static str = \"initpassword\";\n\n static BASE58_SEC_TEST: \u0026'static str =\n \"4xr2CFHWQtDUQiPCon3FhEAvSpXEoFZHeEPiBzDUtEbt2wnrFS9ZTtAvUyZypbDvw8wmYhHrJgBVo6GidMrpwoQq\";\n static BASE58_PUB_TEST: \u0026'static str = \"6sewkaNWyEMqkEa2PVRWrDb3hxWtjPdUSB1zXVCqhdWV\";\n static SALT_TEST: \u0026'static str = \"testsalt\";\n static PASSWORD_TEST: \u0026'static str = \"testpassword\";\n\n #[test]\n fn test_modify_member_keys() {\n let key_pairs = DuniterKeyPairs {\n network_keypair: KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n }),\n member_keypair: None,\n };\n let result_key_pairs = modify_member_keys(SALT_TEST, PASSWORD_TEST, key_pairs);\n // We expect network key not to change\n assert_eq!(\n result_key_pairs.network_keypair.public_key(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"Wrong data in BASE58_PUB_TEST\")\n )\n );\n assert_eq!(\n result_key_pairs.network_keypair.private_key(),\n PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"Wrong data in BASE58_SEC_TEST\")\n )\n );\n\n // We expect member key to update as intended\n assert_eq!(\n result_key_pairs.member_keypair.unwrap().public_key(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(BASE58_PUB_TEST)\n .expect(\"Wrong data in BASE58_PUB_TEST\")\n )\n );\n assert_eq!(\n result_key_pairs.member_keypair.unwrap().private_key(),\n PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(BASE58_SEC_TEST)\n .expect(\"Wrong data in BASE58_SEC_TEST\")\n )\n );\n }\n\n #[test]\n fn test_modify_network_keys() {\n let key_pairs = DuniterKeyPairs {\n network_keypair: KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n }),\n member_keypair: None,\n };\n let result_key_pairs = modify_network_keys(SALT_TEST, PASSWORD_TEST, key_pairs);\n // We expect network key to update\n assert_eq!(\n result_key_pairs.network_keypair.public_key(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(BASE58_PUB_TEST)\n .expect(\"Wrong data in BASE58_PUB_TEST\")\n )\n );\n assert_eq!(\n result_key_pairs.network_keypair.private_key(),\n PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(BASE58_SEC_TEST)\n .expect(\"Wrong data in BASE58_SEC_TEST\")\n )\n );\n // We expect member key not to change\n assert_eq!(result_key_pairs.member_keypair, None);\n }\n\n #[test]\n fn test_clear_network_keys() {\n let key_pairs = DuniterKeyPairs {\n network_keypair: KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n }),\n member_keypair: Some(KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n })),\n };\n let result_key_pairs = clear_keys(true, false, key_pairs);\n // We expect network key to be reset to a new random key\n assert_ne!(\n result_key_pairs.network_keypair.public_key(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"Wrong data in BASE58_PUB_TEST\")\n )\n );\n assert_ne!(\n result_key_pairs.network_keypair.private_key(),\n PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"Wrong data in BASE58_SEC_TEST\")\n )\n );\n\n // We expect member key not to change\n assert_eq!(\n result_key_pairs.member_keypair.unwrap().public_key(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"Wrong data in BASE58_PUB_TEST\")\n )\n );\n assert_eq!(\n result_key_pairs.member_keypair.unwrap().private_key(),\n PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"Wrong data in BASE58_SEC_TEST\")\n )\n );\n }\n\n #[test]\n fn test_clear_member_keys() {\n let key_pairs = DuniterKeyPairs {\n network_keypair: KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n }),\n member_keypair: Some(KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n })),\n };\n let result_key_pairs = clear_keys(false, true, key_pairs);\n // We expect network key not to change\n assert_eq!(\n result_key_pairs.network_keypair.public_key(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(BASE58_PUB_INIT)\n .expect(\"Wrong data in BASE58_PUB_TEST\")\n )\n );\n assert_eq!(\n result_key_pairs.network_keypair.private_key(),\n PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(BASE58_SEC_INIT)\n .expect(\"Wrong data in BASE58_SEC_TEST\")\n )\n );\n\n // We expect member key to change\n assert_eq!(result_key_pairs.member_keypair, None);\n assert_eq!(result_key_pairs.member_keypair, None);\n }\n}\n","traces":[{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":4300816,"length":1,"stats":{"Line":1}},{"line":49,"address":4300846,"length":1,"stats":{"Line":1}},{"line":50,"address":4301082,"length":1,"stats":{"Line":1}},{"line":51,"address":4300875,"length":1,"stats":{"Line":1}},{"line":52,"address":4301120,"length":1,"stats":{"Line":1}},{"line":56,"address":4301152,"length":1,"stats":{"Line":1}},{"line":61,"address":4301182,"length":1,"stats":{"Line":1}},{"line":62,"address":4301418,"length":1,"stats":{"Line":1}},{"line":63,"address":4301211,"length":1,"stats":{"Line":1}},{"line":65,"address":4301502,"length":1,"stats":{"Line":1}},{"line":69,"address":4301536,"length":1,"stats":{"Line":1}},{"line":70,"address":4301566,"length":1,"stats":{"Line":1}},{"line":71,"address":4301587,"length":1,"stats":{"Line":1}},{"line":73,"address":4301643,"length":1,"stats":{"Line":1}},{"line":74,"address":4301655,"length":1,"stats":{"Line":1}},{"line":76,"address":4301659,"length":1,"stats":{"Line":1}},{"line":77,"address":4301723,"length":1,"stats":{"Line":0}},{"line":79,"address":4301769,"length":1,"stats":{"Line":1}},{"line":83,"address":4301808,"length":1,"stats":{"Line":0}},{"line":84,"address":4301822,"length":1,"stats":{"Line":0}},{"line":85,"address":4302072,"length":1,"stats":{"Line":0}},{"line":86,"address":4301971,"length":1,"stats":{"Line":0}},{"line":87,"address":4302089,"length":1,"stats":{"Line":0}},{"line":92,"address":4302288,"length":1,"stats":{"Line":0}},{"line":97,"address":4302309,"length":1,"stats":{"Line":0}},{"line":98,"address":4302396,"length":1,"stats":{"Line":0}},{"line":100,"address":4302450,"length":1,"stats":{"Line":0}},{"line":101,"address":4302483,"length":1,"stats":{"Line":0}},{"line":102,"address":4302513,"length":1,"stats":{"Line":0}},{"line":104,"address":4302581,"length":1,"stats":{"Line":0}},{"line":105,"address":4302883,"length":1,"stats":{"Line":0}},{"line":108,"address":4303136,"length":1,"stats":{"Line":0}},{"line":109,"address":4303162,"length":1,"stats":{"Line":0}},{"line":111,"address":4303237,"length":1,"stats":{"Line":0}},{"line":112,"address":4303624,"length":1,"stats":{"Line":0}},{"line":114,"address":4304365,"length":1,"stats":{"Line":0}},{"line":115,"address":4303739,"length":1,"stats":{"Line":0}},{"line":116,"address":4303783,"length":1,"stats":{"Line":0}},{"line":117,"address":4304165,"length":1,"stats":{"Line":0}},{"line":118,"address":4303960,"length":1,"stats":{"Line":0}},{"line":119,"address":4304167,"length":1,"stats":{"Line":0}},{"line":122,"address":4304235,"length":1,"stats":{"Line":0}},{"line":126,"address":4304592,"length":1,"stats":{"Line":0}},{"line":127,"address":4304609,"length":1,"stats":{"Line":0}},{"line":128,"address":4305023,"length":1,"stats":{"Line":0}},{"line":129,"address":4305050,"length":1,"stats":{"Line":0}},{"line":130,"address":4305429,"length":1,"stats":{"Line":0}},{"line":131,"address":4305456,"length":1,"stats":{"Line":0}},{"line":132,"address":4305707,"length":1,"stats":{"Line":0}},{"line":133,"address":4305475,"length":1,"stats":{"Line":0}},{"line":134,"address":4305591,"length":1,"stats":{"Line":0}},{"line":136,"address":4305888,"length":1,"stats":{"Line":0}},{"line":138,"address":4305985,"length":1,"stats":{"Line":0}},{"line":140,"address":4305406,"length":1,"stats":{"Line":0}},{"line":141,"address":4306034,"length":1,"stats":{"Line":0}},{"line":146,"address":4306560,"length":1,"stats":{"Line":0}},{"line":147,"address":4306570,"length":1,"stats":{"Line":0}},{"line":149,"address":4306596,"length":1,"stats":{"Line":0}},{"line":151,"address":4307211,"length":1,"stats":{"Line":0}},{"line":152,"address":4307242,"length":1,"stats":{"Line":0}},{"line":155,"address":4307776,"length":1,"stats":{"Line":0}},{"line":157,"address":4307535,"length":1,"stats":{"Line":0}},{"line":159,"address":4308110,"length":1,"stats":{"Line":0}},{"line":160,"address":4308141,"length":1,"stats":{"Line":0}},{"line":161,"address":4308406,"length":1,"stats":{"Line":0}},{"line":162,"address":4308442,"length":1,"stats":{"Line":0}},{"line":163,"address":4308508,"length":1,"stats":{"Line":0}},{"line":166,"address":4308514,"length":1,"stats":{"Line":0}},{"line":186,"address":4309376,"length":1,"stats":{"Line":2}},{"line":187,"address":4309812,"length":1,"stats":{"Line":1}},{"line":188,"address":4309638,"length":1,"stats":{"Line":1}},{"line":189,"address":4309511,"length":1,"stats":{"Line":1}},{"line":191,"address":4309574,"length":1,"stats":{"Line":1}},{"line":194,"address":4309804,"length":1,"stats":{"Line":1}},{"line":196,"address":4309905,"length":1,"stats":{"Line":1}},{"line":198,"address":4310223,"length":1,"stats":{"Line":1}},{"line":199,"address":4310062,"length":1,"stats":{"Line":1}},{"line":200,"address":4310151,"length":1,"stats":{"Line":1}},{"line":201,"address":4310087,"length":1,"stats":{"Line":1}},{"line":205,"address":4310781,"length":1,"stats":{"Line":1}},{"line":206,"address":4310638,"length":1,"stats":{"Line":1}},{"line":207,"address":4310726,"length":1,"stats":{"Line":1}},{"line":208,"address":4310663,"length":1,"stats":{"Line":1}},{"line":214,"address":4311402,"length":1,"stats":{"Line":1}},{"line":215,"address":4311182,"length":1,"stats":{"Line":1}},{"line":216,"address":4311330,"length":1,"stats":{"Line":1}},{"line":217,"address":4311266,"length":1,"stats":{"Line":1}},{"line":221,"address":4311986,"length":1,"stats":{"Line":1}},{"line":222,"address":4311787,"length":1,"stats":{"Line":1}},{"line":223,"address":4311934,"length":1,"stats":{"Line":1}},{"line":224,"address":4311871,"length":1,"stats":{"Line":1}},{"line":231,"address":4309408,"length":1,"stats":{"Line":2}},{"line":232,"address":4312692,"length":1,"stats":{"Line":1}},{"line":233,"address":4312518,"length":1,"stats":{"Line":1}},{"line":234,"address":4312391,"length":1,"stats":{"Line":1}},{"line":236,"address":4312454,"length":1,"stats":{"Line":1}},{"line":239,"address":4312684,"length":1,"stats":{"Line":1}},{"line":241,"address":4312785,"length":1,"stats":{"Line":1}},{"line":243,"address":4313103,"length":1,"stats":{"Line":1}},{"line":244,"address":4312942,"length":1,"stats":{"Line":1}},{"line":245,"address":4313031,"length":1,"stats":{"Line":1}},{"line":246,"address":4312967,"length":1,"stats":{"Line":1}},{"line":250,"address":4313634,"length":1,"stats":{"Line":1}},{"line":251,"address":4313494,"length":1,"stats":{"Line":1}},{"line":252,"address":4313582,"length":1,"stats":{"Line":1}},{"line":253,"address":4313519,"length":1,"stats":{"Line":1}},{"line":258,"address":4314021,"length":1,"stats":{"Line":1}},{"line":262,"address":4309440,"length":1,"stats":{"Line":2}},{"line":263,"address":4315061,"length":1,"stats":{"Line":1}},{"line":264,"address":4314550,"length":1,"stats":{"Line":1}},{"line":265,"address":4314423,"length":1,"stats":{"Line":1}},{"line":267,"address":4314486,"length":1,"stats":{"Line":1}},{"line":270,"address":4314834,"length":1,"stats":{"Line":1}},{"line":271,"address":4314705,"length":1,"stats":{"Line":1}},{"line":273,"address":4314768,"length":1,"stats":{"Line":1}},{"line":277,"address":4315146,"length":1,"stats":{"Line":1}},{"line":279,"address":4315383,"length":1,"stats":{"Line":1}},{"line":280,"address":4315222,"length":1,"stats":{"Line":1}},{"line":281,"address":4315311,"length":1,"stats":{"Line":1}},{"line":282,"address":4315247,"length":1,"stats":{"Line":1}},{"line":286,"address":4315939,"length":1,"stats":{"Line":1}},{"line":287,"address":4315796,"length":1,"stats":{"Line":1}},{"line":288,"address":4315884,"length":1,"stats":{"Line":1}},{"line":289,"address":4315821,"length":1,"stats":{"Line":1}},{"line":295,"address":4316558,"length":1,"stats":{"Line":1}},{"line":296,"address":4316338,"length":1,"stats":{"Line":1}},{"line":297,"address":4316486,"length":1,"stats":{"Line":1}},{"line":298,"address":4316422,"length":1,"stats":{"Line":1}},{"line":302,"address":4317142,"length":1,"stats":{"Line":1}},{"line":303,"address":4316943,"length":1,"stats":{"Line":1}},{"line":304,"address":4317090,"length":1,"stats":{"Line":1}},{"line":305,"address":4317027,"length":1,"stats":{"Line":1}},{"line":312,"address":4309472,"length":1,"stats":{"Line":2}},{"line":313,"address":4318181,"length":1,"stats":{"Line":1}},{"line":314,"address":4317670,"length":1,"stats":{"Line":1}},{"line":315,"address":4317543,"length":1,"stats":{"Line":1}},{"line":317,"address":4317606,"length":1,"stats":{"Line":1}},{"line":320,"address":4317954,"length":1,"stats":{"Line":1}},{"line":321,"address":4317825,"length":1,"stats":{"Line":1}},{"line":323,"address":4317888,"length":1,"stats":{"Line":1}},{"line":327,"address":4318266,"length":1,"stats":{"Line":1}},{"line":329,"address":4318503,"length":1,"stats":{"Line":1}},{"line":330,"address":4318342,"length":1,"stats":{"Line":1}},{"line":331,"address":4318431,"length":1,"stats":{"Line":1}},{"line":332,"address":4318367,"length":1,"stats":{"Line":1}},{"line":336,"address":4319058,"length":1,"stats":{"Line":1}},{"line":337,"address":4318918,"length":1,"stats":{"Line":1}},{"line":338,"address":4319006,"length":1,"stats":{"Line":1}},{"line":339,"address":4318943,"length":1,"stats":{"Line":1}},{"line":345,"address":4319445,"length":1,"stats":{"Line":1}},{"line":346,"address":4319832,"length":1,"stats":{"Line":1}}],"covered":100,"coverable":153},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","conf","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Dunitrust configuration files properties module\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde_derive;\n\npub mod constants;\npub mod keys;\n\nuse crate::constants::MODULES_DATAS_FOLDER;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_module::{\n DursConfTrait, DursGlobalConfTrait, ModuleName, RequiredKeys, RequiredKeysContent,\n};\nuse failure::Fail;\nuse rand::Rng;\nuse serde::ser::{Serialize, SerializeStruct, Serializer};\nuse std::collections::HashSet;\nuse std::fs;\nuse std::fs::File;\nuse std::io::prelude::*;\nuse std::path::{Path, PathBuf};\n\n#[derive(Debug, Clone)]\n/// User request on global conf\npub enum ChangeGlobalConf {\n /// Change currency\n ChangeCurrency(CurrencyName),\n /// Disable module\n DisableModule(ModuleName),\n /// Enable module\n EnableModule(ModuleName),\n /// None\n None(),\n}\n\n#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]\n/// Modules conf\npub struct ModulesConf(pub serde_json::Value);\n\nimpl Default for ModulesConf {\n #[inline]\n fn default() -\u003e Self {\n ModulesConf(serde_json::Value::Null)\n }\n}\n\nimpl ModulesConf {\n /// Change module conf\n pub fn set_module_conf(\u0026mut self, module_name: ModuleName, new_module_conf: serde_json::Value) {\n if self.0.is_null() {\n let mut new_modules_conf = serde_json::Map::with_capacity(1);\n new_modules_conf.insert(module_name.0, new_module_conf);\n self.0 = serde_json::value::to_value(new_modules_conf)\n .expect(\"Fail to create map of new modules conf !\");\n } else {\n self.0\n .as_object_mut()\n .expect(\"Conf file currupted !\")\n .insert(module_name.0, new_module_conf);\n }\n }\n}\n\n#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]\n/// Duniter configuration v1\npub struct DuRsConfV1 {\n /// Currency name\n pub currency: CurrencyName,\n /// Duniter node unique identifier\n pub my_node_id: u32,\n /// Configuration of modules in json format (obtained from the conf.json file)\n pub modules: ModulesConf,\n /// Disabled modules\n pub disabled: HashSet\u003cModuleName\u003e,\n /// Enabled modules\n pub enabled: HashSet\u003cModuleName\u003e,\n}\n\nimpl Default for DuRsConfV1 {\n fn default() -\u003e Self {\n DuRsConfV1 {\n currency: CurrencyName(String::from(constants::DEFAULT_CURRENCY)),\n my_node_id: generate_random_node_id(),\n modules: ModulesConf::default(),\n disabled: HashSet::with_capacity(0),\n enabled: HashSet::with_capacity(0),\n }\n }\n}\n\n#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)]\n/// Ressource usage\npub enum ResourceUsage {\n /// Minimal use of the resource, to the detriment of performance\n Minimal,\n /// Trade-off between resource use and performance\n Medium,\n /// A performance-oriented trade-off, the use of the resource is slightly limited\n Large,\n /// No restrictions on the use of the resource, maximizes performance\n Infinite,\n}\n#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Serialize)]\n/// Ressources usage\npub struct ResourcesUsage {\n /// Cpu usage\n pub cpu_usage: ResourceUsage,\n /// Network usage\n pub network_usage: ResourceUsage,\n /// Memory usage\n pub memory_usage: ResourceUsage,\n /// Disk space usage\n pub disk_space_usage: ResourceUsage,\n}\n\nimpl Default for ResourcesUsage {\n fn default() -\u003e Self {\n ResourcesUsage {\n cpu_usage: ResourceUsage::Large,\n network_usage: ResourceUsage::Large,\n memory_usage: ResourceUsage::Large,\n disk_space_usage: ResourceUsage::Large,\n }\n }\n}\n\n#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]\n/// Duniter configuration v2\npub struct DuRsConfV2 {\n /// Currency name\n pub currency: CurrencyName,\n /// Duniter node unique identifier\n pub my_node_id: u32,\n /// Name of the module used by default for synchronization\n pub default_sync_module: ModuleName,\n /// Ressources usage\n pub ressources_usage: ResourcesUsage,\n /// Disabled modules\n pub disabled: HashSet\u003cModuleName\u003e,\n /// Enabled modules\n pub enabled: HashSet\u003cModuleName\u003e,\n}\n\nimpl Default for DuRsConfV2 {\n fn default() -\u003e Self {\n DuRsConfV2 {\n currency: CurrencyName(String::from(constants::DEFAULT_CURRENCY)),\n my_node_id: generate_random_node_id(),\n default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)),\n ressources_usage: ResourcesUsage::default(),\n disabled: HashSet::with_capacity(0),\n enabled: HashSet::with_capacity(0),\n }\n }\n}\n\nimpl From\u003cDuRsConfV1\u003e for DuRsConfV2 {\n fn from(conf_v1: DuRsConfV1) -\u003e Self {\n DuRsConfV2 {\n currency: conf_v1.currency,\n my_node_id: conf_v1.my_node_id,\n default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)),\n ressources_usage: ResourcesUsage::default(),\n disabled: conf_v1.disabled,\n enabled: conf_v1.enabled,\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]\n/// Dunitrust node configuration\npub enum DuRsConf {\n /// Dunitrust node configuration v1\n V1(DuRsConfV1),\n /// Dunitrust node configuration v2\n V2 {\n /// Global configuration\n global_conf: DuRsConfV2,\n /// Modules configuration\n modules_conf: ModulesConf,\n },\n}\n\n#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]\n/// Dunitrust global configuration (without modules configuration)\npub enum DuRsGlobalConf {\n /// Dunitrust global configuration v1\n V1(DuRsConfV1),\n /// Dunitrust global configuration v2\n V2(DuRsConfV2),\n}\n\nimpl DursGlobalConfTrait for DuRsGlobalConf {\n fn my_node_id(\u0026self) -\u003e u32 {\n match *self {\n DuRsGlobalConf::V1(ref conf_v1) =\u003e conf_v1.my_node_id,\n DuRsGlobalConf::V2(ref conf_v2) =\u003e conf_v2.my_node_id,\n }\n }\n fn default_sync_module(\u0026self) -\u003e ModuleName {\n match *self {\n DuRsGlobalConf::V1(_) =\u003e {\n fatal_error!(\"Feature default_sync_module not exist in durs conf v1 !\")\n }\n DuRsGlobalConf::V2(ref conf_v2) =\u003e conf_v2.default_sync_module.clone(),\n }\n }\n}\n\nimpl Default for DuRsConf {\n #[inline]\n fn default() -\u003e Self {\n DuRsConf::V2 {\n global_conf: DuRsConfV2::default(),\n modules_conf: ModulesConf::default(),\n }\n }\n}\n\nimpl DursConfTrait for DuRsConf {\n type GlobalConf = DuRsGlobalConf;\n\n fn get_global_conf(\u0026self) -\u003e Self::GlobalConf {\n match *self {\n DuRsConf::V1(ref conf_v1) =\u003e DuRsGlobalConf::V1(conf_v1.clone()),\n DuRsConf::V2 {\n ref global_conf, ..\n } =\u003e DuRsGlobalConf::V2(global_conf.clone()),\n }\n }\n fn upgrade(self) -\u003e (Self, bool) {\n if let DuRsConf::V1(conf_v1) = self {\n let modules_conf = conf_v1.modules.clone();\n (\n DuRsConf::V2 {\n global_conf: DuRsConfV2::from(conf_v1),\n modules_conf,\n },\n true,\n )\n } else {\n (self, false)\n }\n }\n fn version(\u0026self) -\u003e usize {\n match *self {\n DuRsConf::V1(_) =\u003e 1,\n DuRsConf::V2 { .. } =\u003e 2,\n }\n }\n fn set_currency(\u0026mut self, new_currency: CurrencyName) {\n match *self {\n DuRsConf::V1(ref mut conf_v1) =\u003e conf_v1.currency = new_currency,\n DuRsConf::V2 {\n ref mut global_conf,\n ..\n } =\u003e global_conf.currency = new_currency,\n }\n }\n fn disable(\u0026mut self, module: ModuleName) {\n match *self {\n DuRsConf::V1(ref mut conf_v1) =\u003e {\n conf_v1.disabled.insert(module.clone());\n conf_v1.enabled.remove(\u0026module);\n }\n DuRsConf::V2 {\n ref mut global_conf,\n ..\n } =\u003e {\n global_conf.disabled.insert(module.clone());\n global_conf.enabled.remove(\u0026module);\n }\n }\n }\n fn enable(\u0026mut self, module: ModuleName) {\n match *self {\n DuRsConf::V1(ref mut conf_v1) =\u003e {\n conf_v1.disabled.remove(\u0026module);\n conf_v1.enabled.insert(module);\n }\n DuRsConf::V2 {\n ref mut global_conf,\n ..\n } =\u003e {\n global_conf.disabled.remove(\u0026module);\n global_conf.enabled.insert(module);\n }\n }\n }\n fn disabled_modules(\u0026self) -\u003e HashSet\u003cModuleName\u003e {\n match *self {\n DuRsConf::V1(ref conf_v1) =\u003e conf_v1.disabled.clone(),\n DuRsConf::V2 {\n ref global_conf, ..\n } =\u003e global_conf.disabled.clone(),\n }\n }\n fn enabled_modules(\u0026self) -\u003e HashSet\u003cModuleName\u003e {\n match *self {\n DuRsConf::V1(ref conf_v1) =\u003e conf_v1.enabled.clone(),\n DuRsConf::V2 {\n ref global_conf, ..\n } =\u003e global_conf.enabled.clone(),\n }\n }\n fn modules(\u0026self) -\u003e serde_json::Value {\n match *self {\n DuRsConf::V1(ref conf_v1) =\u003e conf_v1.modules.0.clone(),\n DuRsConf::V2 {\n ref modules_conf, ..\n } =\u003e modules_conf.0.clone(),\n }\n }\n fn set_module_conf(\u0026mut self, module_name: ModuleName, new_module_conf: serde_json::Value) {\n match *self {\n DuRsConf::V1(ref mut conf_v1) =\u003e conf_v1\n .modules\n .set_module_conf(module_name, new_module_conf),\n DuRsConf::V2 {\n ref mut modules_conf,\n ..\n } =\u003e modules_conf.set_module_conf(module_name, new_module_conf),\n }\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n/// Keypairs filled in by the user (via a file or by direct entry in the terminal).\npub struct DuniterKeyPairs {\n /// Keypair used by the node to sign its communications with other nodes. This keypair is mandatory, if it's not filled in, a random keypair is generated.\n pub network_keypair: KeyPairEnum,\n /// Keypair used to sign the blocks forged by this node. If this keypair is'nt filled in, the node will not calculate blocks.\n pub member_keypair: Option\u003cKeyPairEnum\u003e,\n}\n\nimpl Serialize for DuniterKeyPairs {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n let member_sec = if let Some(member_keypair) = self.member_keypair {\n member_keypair.private_key().to_string()\n } else {\n String::from(\"\")\n };\n let member_pub = if let Some(member_keypair) = self.member_keypair {\n member_keypair.public_key().to_string()\n } else {\n String::from(\"\")\n };\n let mut state = serializer.serialize_struct(\"DuniterKeyPairs\", 4)?;\n state.serialize_field(\n \"network_sec\",\n \u0026self.network_keypair.private_key().to_string().as_str(),\n )?;\n state.serialize_field(\n \"network_pub\",\n \u0026self.network_keypair.public_key().to_string().as_str(),\n )?;\n state.serialize_field(\"member_sec\", member_sec.as_str())?;\n state.serialize_field(\"member_pub\", member_pub.as_str())?;\n state.end()\n }\n}\n\nimpl DuniterKeyPairs {\n /// Returns only the keys indicated as required\n pub fn get_required_keys_content(\n required_keys: RequiredKeys,\n keypairs: DuniterKeyPairs,\n ) -\u003e RequiredKeysContent {\n match required_keys {\n RequiredKeys::MemberKeyPair() =\u003e {\n RequiredKeysContent::MemberKeyPair(keypairs.member_keypair)\n }\n RequiredKeys::MemberPublicKey() =\u003e {\n RequiredKeysContent::MemberPublicKey(if let Some(keys) = keypairs.member_keypair {\n Some(keys.public_key())\n } else {\n None\n })\n }\n RequiredKeys::NetworkKeyPair() =\u003e {\n RequiredKeysContent::NetworkKeyPair(keypairs.network_keypair)\n }\n RequiredKeys::NetworkPublicKey() =\u003e {\n RequiredKeysContent::NetworkPublicKey(keypairs.network_keypair.public_key())\n }\n RequiredKeys::None() =\u003e RequiredKeysContent::None(),\n }\n }\n}\n\n// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !\nfn generate_random_keypair(algo: KeysAlgo) -\u003e KeyPairEnum {\n let mut rng = rand::thread_rng();\n match algo {\n KeysAlgo::Ed25519 =\u003e {\n let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();\n KeyPairEnum::Ed25519(generator.generate(\u0026[rng.gen::\u003cu8\u003e(); 8], \u0026[rng.gen::\u003cu8\u003e(); 8]))\n }\n KeysAlgo::Schnorr =\u003e panic!(\"Schnorr algo not yet supported !\"),\n }\n}\n\nfn generate_random_node_id() -\u003e u32 {\n let mut rng = rand::thread_rng();\n rng.gen::\u003cu32\u003e()\n}\n\n/// Return the user datas folder name\npub fn get_user_datas_folder() -\u003e \u0026'static str {\n constants::USER_DATAS_FOLDER\n}\n\n/// Returns the path to the folder containing the modules datas of the running profile\n#[inline]\npub fn get_datas_path(profile_path: PathBuf) -\u003e PathBuf {\n let mut datas_path = profile_path;\n datas_path.push(MODULES_DATAS_FOLDER);\n if !datas_path.as_path().exists() {\n if let Err(io_error) = fs::create_dir(datas_path.as_path()) {\n if io_error.kind() != std::io::ErrorKind::AlreadyExists {\n fatal_error!(\"Impossible to create currency dir !\");\n }\n }\n }\n datas_path\n}\n\n#[inline]\n/// Return path to configuration file\npub fn get_conf_path(profile_path: \u0026PathBuf) -\u003e PathBuf {\n let mut conf_path = profile_path.clone();\n conf_path.push(constants::CONF_FILENAME);\n conf_path\n}\n\n/// Returns the path to the folder containing the user data of the running profile\n// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !\npub fn get_profile_path(profiles_path: \u0026Option\u003cPathBuf\u003e, profile_name: \u0026str) -\u003e PathBuf {\n // Define and create datas directory if not exist\n let profiles_path: PathBuf = if let Some(profiles_path) = profiles_path {\n profiles_path.clone()\n } else {\n let mut user_config_path = match dirs::config_dir() {\n Some(path) =\u003e path,\n None =\u003e panic!(\"Impossible to get user config directory !\"),\n };\n user_config_path.push(constants::USER_DATAS_FOLDER);\n user_config_path\n };\n if !profiles_path.as_path().exists() {\n fs::create_dir(profiles_path.as_path()).unwrap_or_else(|_| {\n panic!(\n \"Impossible to create profiles directory: {:?} !\",\n profiles_path\n )\n });\n }\n let mut profile_path = profiles_path;\n profile_path.push(profile_name);\n if !profile_path.as_path().exists() {\n fs::create_dir(profile_path.as_path()).expect(\"Impossible to create your profile dir !\");\n }\n profile_path\n}\n\n/// Get keypairs file path\npub fn keypairs_filepath(profiles_path: \u0026Option\u003cPathBuf\u003e, profile: \u0026str) -\u003e PathBuf {\n let profile_path = get_profile_path(profiles_path, profile);\n let mut conf_keys_path = profile_path.clone();\n conf_keys_path.push(constants::KEYPAIRS_FILENAME);\n conf_keys_path\n}\n\n/// Load configuration.\npub fn load_conf(\n profile_path: PathBuf,\n keypairs_file_path: \u0026Option\u003cPathBuf\u003e,\n) -\u003e Result\u003c(DuRsConf, DuniterKeyPairs), DursConfFileError\u003e {\n // Load conf\n let (conf, keypairs) = load_conf_at_path(profile_path.clone(), keypairs_file_path)?;\n\n // Return conf and keypairs\n Ok((conf, keypairs))\n}\n\n/// Error with configuration file\n#[derive(Debug, Fail)]\npub enum DursConfFileError {\n /// Read error\n #[fail(display = \"fail to read configuration file: {}\", _0)]\n ReadError(std::io::Error),\n /// Parse error\n #[fail(display = \"fail to parse configuration file: {}\", _0)]\n ParseError(serde_json::Error),\n /// Write error\n #[fail(display = \"fail to write configuration file: {}\", _0)]\n WriteError(std::io::Error),\n}\n\n/// Load configuration. at specified path\n// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !\npub fn load_conf_at_path(\n profile_path: PathBuf,\n keypairs_file_path: \u0026Option\u003cPathBuf\u003e,\n) -\u003e Result\u003c(DuRsConf, DuniterKeyPairs), DursConfFileError\u003e {\n // Get KeyPairs\n let keypairs_path = if let Some(ref keypairs_file_path) = keypairs_file_path {\n keypairs_file_path.clone()\n } else {\n let mut keypairs_path = profile_path.clone();\n keypairs_path.push(constants::KEYPAIRS_FILENAME);\n keypairs_path\n };\n let keypairs = if keypairs_path.as_path().exists() {\n if let Ok(mut f) = File::open(keypairs_path.as_path()) {\n let mut contents = String::new();\n if f.read_to_string(\u0026mut contents).is_ok() {\n let json_conf: serde_json::Value =\n serde_json::from_str(\u0026contents).expect(\"Conf: Fail to parse keypairs file !\");\n\n if let Some(network_sec) = json_conf.get(\"network_sec\") {\n if let Some(network_pub) = json_conf.get(\"network_pub\") {\n let network_sec = network_sec\n .as_str()\n .expect(\"Conf: Fail to parse keypairs file !\");\n let network_pub = network_pub\n .as_str()\n .expect(\"Conf: Fail to parse keypairs file !\");\n let network_keypair = KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(network_sec)\n .expect(\"conf : keypairs file : fail to parse network_sec !\"),\n pubkey: ed25519::PublicKey::from_base58(network_pub)\n .expect(\"conf : keypairs file : fail to parse network_pub !\"),\n });\n\n let member_keypair = if let Some(member_sec) = json_conf.get(\"member_sec\") {\n if let Some(member_pub) = json_conf.get(\"member_pub\") {\n let member_sec = member_sec\n .as_str()\n .expect(\"Conf: Fail to parse keypairs file !\");\n let member_pub = member_pub\n .as_str()\n .expect(\"Conf: Fail to parse keypairs file !\");\n if member_sec.is_empty() || member_pub.is_empty() {\n None\n } else {\n Some(KeyPairEnum::Ed25519(ed25519::KeyPair {\n privkey: ed25519::PrivateKey::from_base58(member_sec)\n .expect(\n \"conf : keypairs file : fail to parse member_sec !\",\n ),\n pubkey: ed25519::PublicKey::from_base58(member_pub).expect(\n \"conf : keypairs file : fail to parse member_pub !\",\n ),\n }))\n }\n } else {\n panic!(\"Fatal error : keypairs file wrong format : no field salt !\")\n }\n } else {\n panic!(\"Fatal error : keypairs file wrong format : no field password !\")\n };\n\n // Create keypairs file with random keypair\n DuniterKeyPairs {\n network_keypair,\n member_keypair,\n }\n } else {\n panic!(\"Fatal error : keypairs file wrong format : no field salt !\")\n }\n } else {\n panic!(\"Fatal error : keypairs file wrong format : no field password !\")\n }\n } else {\n panic!(\"Fail to read keypairs file !\");\n }\n } else {\n panic!(\"Fail to open keypairs file !\");\n }\n } else {\n // Create keypairs file with random keypair\n let keypairs = DuniterKeyPairs {\n network_keypair: generate_random_keypair(KeysAlgo::Ed25519),\n member_keypair: None,\n };\n write_keypairs_file(\u0026keypairs_path, \u0026keypairs).unwrap_or_else(|_| {\n panic!(dbg!(\"Fatal error : fail to write default keypairs file !\"))\n });\n keypairs\n };\n\n // Open conf file\n let mut conf_path = profile_path;\n conf_path.push(constants::CONF_FILENAME);\n let conf = if conf_path.as_path().exists() {\n match File::open(conf_path.as_path()) {\n Ok(mut f) =\u003e {\n let mut contents = String::new();\n f.read_to_string(\u0026mut contents)\n .map_err(DursConfFileError::ReadError)?;\n // Parse conf file\n let conf: DuRsConf =\n serde_json::from_str(\u0026contents).map_err(DursConfFileError::ParseError)?;\n // Upgrade conf to latest version\n let (conf, upgraded) = conf.upgrade();\n // If conf is upgraded, rewrite conf file\n if upgraded {\n write_conf_file(conf_path.as_path(), \u0026conf)\n .map_err(DursConfFileError::WriteError)?;\n }\n conf\n }\n Err(e) =\u003e return Err(DursConfFileError::ReadError(e)),\n }\n } else {\n // Create conf file with default conf\n let conf = DuRsConf::default();\n write_conf_file(conf_path.as_path(), \u0026conf)\n .unwrap_or_else(|_| panic!(dbg!(\"Fatal error : fail to write default conf file!\")));\n conf\n };\n\n // Return conf and keypairs\n Ok((conf, keypairs))\n}\n\n/// Save keypairs in profile folder\n// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !\npub fn write_keypairs_file(\n file_path: \u0026PathBuf,\n keypairs: \u0026DuniterKeyPairs,\n) -\u003e Result\u003c(), std::io::Error\u003e {\n let mut f = File::create(file_path.as_path())?;\n f.write_all(\n serde_json::to_string_pretty(keypairs)\n .unwrap_or_else(|_| panic!(dbg!(\"Fatal error : fail to deserialize keypairs !\")))\n .as_bytes(),\n )?;\n f.sync_all()?;\n Ok(())\n}\n\n/// Write new module conf\npub fn write_new_module_conf\u003cDC: DursConfTrait\u003e(\n conf: \u0026mut DC,\n profile_path: PathBuf,\n module_name: ModuleName,\n new_module_conf: serde_json::Value,\n) {\n conf.set_module_conf(module_name, new_module_conf);\n let mut conf_path = profile_path;\n conf_path.push(crate::constants::CONF_FILENAME);\n write_conf_file(conf_path.as_path(), conf).expect(\"Fail to write new conf file ! \");\n}\n\n/// Save configuration in profile folder\npub fn write_conf_file\u003cDC: DursConfTrait\u003e(\n conf_path: \u0026Path,\n conf: \u0026DC,\n) -\u003e Result\u003c(), std::io::Error\u003e {\n let mut f = File::create(conf_path)?;\n f.write_all(\n serde_json::to_string_pretty(conf)\n .expect(\"Fatal error : fail to write default conf file !\")\n .as_bytes(),\n )?;\n f.sync_all()?;\n Ok(())\n}\n\n/// Returns the path to the database containing the blockchain\npub fn get_blockchain_db_path(profile_path: PathBuf) -\u003e PathBuf {\n let mut db_path = get_datas_path(profile_path);\n db_path.push(\"blockchain/\");\n if !db_path.as_path().exists() {\n if let Err(io_error) = fs::create_dir(db_path.as_path()) {\n if io_error.kind() != std::io::ErrorKind::AlreadyExists {\n fatal_error!(\"Impossible to create blockchain dir !\");\n }\n }\n }\n db_path\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use serde_json::json;\n\n #[inline]\n fn save_old_conf(profile_path: PathBuf) -\u003e std::io::Result\u003c()\u003e {\n let mut conf_path = profile_path.clone();\n conf_path.push(constants::CONF_FILENAME);\n let mut conf_sav_path = profile_path;\n conf_sav_path.push(\"conf-sav.json\");\n std::fs::copy(conf_path.as_path(), conf_sav_path.as_path())?;\n Ok(())\n }\n\n fn restore_old_conf_and_save_upgraded_conf(profile_path: PathBuf) -\u003e std::io::Result\u003c()\u003e {\n let mut conf_path = profile_path.clone();\n conf_path.push(constants::CONF_FILENAME);\n let mut conf_sav_path = profile_path.clone();\n conf_sav_path.push(\"conf-sav.json\");\n let mut conf_upgraded_path = profile_path;\n conf_upgraded_path.push(\"conf-upgraded.json\");\n std::fs::copy(conf_path.as_path(), \u0026conf_upgraded_path.as_path())?;\n std::fs::copy(conf_sav_path.as_path(), \u0026conf_path.as_path())?;\n std::fs::remove_file(conf_sav_path.as_path())?;\n Ok(())\n }\n\n #[test]\n fn load_conf_file_v1() -\u003e Result\u003c(), DursConfFileError\u003e {\n let profile_path = PathBuf::from(\"./test/v1/\");\n save_old_conf(PathBuf::from(profile_path.clone()))\n .map_err(DursConfFileError::WriteError)?;\n let (conf, _keys) = load_conf_at_path(profile_path.clone(), \u0026None)?;\n assert_eq!(\n conf.modules()\n .get(\"ws2p\")\n .expect(\"Not found ws2p conf\")\n .clone(),\n json!({\n \"sync_endpoints\": [\n {\n \"endpoint\": \"WS2P c1c39a0a i3.ifee.fr 80 /ws2p\",\n \"pubkey\": \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\"\n },\n {\n \"endpoint\": \"WS2P 15af24db g1.ifee.fr 80 /ws2p\",\n \"pubkey\": \"BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG\"\n },\n {\n \"endpoint\": \"WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p\",\n \"pubkey\": \"7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef\"\n }\n ]\n })\n );\n restore_old_conf_and_save_upgraded_conf(profile_path)\n .map_err(DursConfFileError::WriteError)?;\n\n Ok(())\n }\n\n #[test]\n fn load_conf_file_v2() -\u003e Result\u003c(), DursConfFileError\u003e {\n let profile_path = PathBuf::from(\"./test/v2/\");\n let (conf, _keys) = load_conf_at_path(profile_path, \u0026None)?;\n assert_eq!(\n conf.modules()\n .get(\"ws2p\")\n .expect(\"Not found ws2p conf\")\n .clone(),\n json!({\n \"sync_endpoints\": [\n {\n \"endpoint\": \"WS2P c1c39a0a i3.ifee.fr 80 /ws2p\",\n \"pubkey\": \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\"\n },\n {\n \"endpoint\": \"WS2P 15af24db g1.ifee.fr 80 /ws2p\",\n \"pubkey\": \"BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG\"\n },\n {\n \"endpoint\": \"WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p\",\n \"pubkey\": \"7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef\"\n }\n ]\n })\n );\n Ok(())\n }\n}\n","traces":[{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":231,"address":null,"length":0,"stats":{"Line":0}},{"line":232,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":0}},{"line":234,"address":null,"length":0,"stats":{"Line":0}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":255,"address":null,"length":0,"stats":{"Line":0}},{"line":256,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":259,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":1}},{"line":263,"address":null,"length":0,"stats":{"Line":1}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":266,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":268,"address":null,"length":0,"stats":{"Line":0}},{"line":270,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":0}},{"line":273,"address":null,"length":0,"stats":{"Line":1}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":277,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":279,"address":null,"length":0,"stats":{"Line":0}},{"line":282,"address":null,"length":0,"stats":{"Line":0}},{"line":283,"address":null,"length":0,"stats":{"Line":0}},{"line":284,"address":null,"length":0,"stats":{"Line":0}},{"line":285,"address":null,"length":0,"stats":{"Line":0}},{"line":286,"address":null,"length":0,"stats":{"Line":0}},{"line":287,"address":null,"length":0,"stats":{"Line":0}},{"line":288,"address":null,"length":0,"stats":{"Line":0}},{"line":291,"address":null,"length":0,"stats":{"Line":0}},{"line":292,"address":null,"length":0,"stats":{"Line":0}},{"line":293,"address":null,"length":0,"stats":{"Line":0}},{"line":294,"address":null,"length":0,"stats":{"Line":0}},{"line":295,"address":null,"length":0,"stats":{"Line":0}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":0}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":306,"address":null,"length":0,"stats":{"Line":0}},{"line":307,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":313,"address":null,"length":0,"stats":{"Line":0}},{"line":314,"address":null,"length":0,"stats":{"Line":0}},{"line":315,"address":null,"length":0,"stats":{"Line":0}},{"line":316,"address":null,"length":0,"stats":{"Line":0}},{"line":317,"address":null,"length":0,"stats":{"Line":0}},{"line":321,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":0}},{"line":324,"address":null,"length":0,"stats":{"Line":0}},{"line":325,"address":null,"length":0,"stats":{"Line":0}},{"line":326,"address":null,"length":0,"stats":{"Line":0}},{"line":329,"address":null,"length":0,"stats":{"Line":0}},{"line":330,"address":null,"length":0,"stats":{"Line":0}},{"line":331,"address":null,"length":0,"stats":{"Line":0}},{"line":332,"address":null,"length":0,"stats":{"Line":0}},{"line":333,"address":null,"length":0,"stats":{"Line":0}},{"line":334,"address":null,"length":0,"stats":{"Line":0}},{"line":337,"address":null,"length":0,"stats":{"Line":1}},{"line":338,"address":null,"length":0,"stats":{"Line":0}},{"line":339,"address":null,"length":0,"stats":{"Line":1}},{"line":340,"address":null,"length":0,"stats":{"Line":0}},{"line":341,"address":null,"length":0,"stats":{"Line":1}},{"line":342,"address":null,"length":0,"stats":{"Line":1}},{"line":345,"address":null,"length":0,"stats":{"Line":0}},{"line":346,"address":null,"length":0,"stats":{"Line":0}},{"line":347,"address":null,"length":0,"stats":{"Line":0}},{"line":349,"address":null,"length":0,"stats":{"Line":0}},{"line":350,"address":null,"length":0,"stats":{"Line":0}},{"line":351,"address":null,"length":0,"stats":{"Line":0}},{"line":352,"address":null,"length":0,"stats":{"Line":0}},{"line":353,"address":null,"length":0,"stats":{"Line":0}},{"line":368,"address":4236592,"length":1,"stats":{"Line":0}},{"line":372,"address":null,"length":0,"stats":{"Line":0}},{"line":373,"address":null,"length":0,"stats":{"Line":0}},{"line":374,"address":null,"length":0,"stats":{"Line":0}},{"line":375,"address":null,"length":0,"stats":{"Line":0}},{"line":377,"address":null,"length":0,"stats":{"Line":0}},{"line":378,"address":null,"length":0,"stats":{"Line":0}},{"line":379,"address":null,"length":0,"stats":{"Line":0}},{"line":380,"address":null,"length":0,"stats":{"Line":0}},{"line":382,"address":null,"length":0,"stats":{"Line":0}},{"line":383,"address":null,"length":0,"stats":{"Line":0}},{"line":384,"address":null,"length":0,"stats":{"Line":0}},{"line":385,"address":null,"length":0,"stats":{"Line":0}},{"line":387,"address":null,"length":0,"stats":{"Line":0}},{"line":388,"address":null,"length":0,"stats":{"Line":0}},{"line":389,"address":null,"length":0,"stats":{"Line":0}},{"line":391,"address":null,"length":0,"stats":{"Line":0}},{"line":392,"address":null,"length":0,"stats":{"Line":0}},{"line":393,"address":null,"length":0,"stats":{"Line":0}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":403,"address":null,"length":0,"stats":{"Line":0}},{"line":404,"address":null,"length":0,"stats":{"Line":0}},{"line":405,"address":null,"length":0,"stats":{"Line":0}},{"line":407,"address":null,"length":0,"stats":{"Line":0}},{"line":408,"address":null,"length":0,"stats":{"Line":0}},{"line":409,"address":null,"length":0,"stats":{"Line":0}},{"line":410,"address":null,"length":0,"stats":{"Line":0}},{"line":411,"address":null,"length":0,"stats":{"Line":0}},{"line":414,"address":null,"length":0,"stats":{"Line":0}},{"line":415,"address":null,"length":0,"stats":{"Line":0}},{"line":417,"address":null,"length":0,"stats":{"Line":0}},{"line":418,"address":null,"length":0,"stats":{"Line":0}},{"line":426,"address":4331600,"length":1,"stats":{"Line":1}},{"line":427,"address":4331629,"length":1,"stats":{"Line":1}},{"line":429,"address":4331657,"length":1,"stats":{"Line":1}},{"line":430,"address":4331702,"length":1,"stats":{"Line":1}},{"line":431,"address":4331768,"length":1,"stats":{"Line":1}},{"line":433,"address":4331980,"length":1,"stats":{"Line":0}},{"line":437,"address":4332048,"length":1,"stats":{"Line":0}},{"line":438,"address":4332052,"length":1,"stats":{"Line":0}},{"line":443,"address":4332160,"length":1,"stats":{"Line":0}},{"line":444,"address":4332160,"length":1,"stats":{"Line":0}},{"line":449,"address":null,"length":0,"stats":{"Line":0}},{"line":450,"address":null,"length":0,"stats":{"Line":0}},{"line":451,"address":null,"length":0,"stats":{"Line":0}},{"line":452,"address":null,"length":0,"stats":{"Line":0}},{"line":453,"address":null,"length":0,"stats":{"Line":0}},{"line":454,"address":null,"length":0,"stats":{"Line":0}},{"line":455,"address":null,"length":0,"stats":{"Line":0}},{"line":459,"address":null,"length":0,"stats":{"Line":0}},{"line":464,"address":null,"length":0,"stats":{"Line":0}},{"line":465,"address":null,"length":0,"stats":{"Line":0}},{"line":466,"address":null,"length":0,"stats":{"Line":0}},{"line":467,"address":null,"length":0,"stats":{"Line":0}},{"line":472,"address":4334224,"length":1,"stats":{"Line":0}},{"line":474,"address":4334261,"length":1,"stats":{"Line":0}},{"line":475,"address":4334333,"length":1,"stats":{"Line":0}},{"line":477,"address":4334367,"length":1,"stats":{"Line":0}},{"line":478,"address":4334381,"length":1,"stats":{"Line":0}},{"line":479,"address":4334566,"length":1,"stats":{"Line":0}},{"line":481,"address":4335159,"length":1,"stats":{"Line":0}},{"line":482,"address":4334635,"length":1,"stats":{"Line":0}},{"line":484,"address":4334691,"length":1,"stats":{"Line":0}},{"line":485,"address":4239792,"length":1,"stats":{"Line":0}},{"line":486,"address":4239809,"length":1,"stats":{"Line":0}},{"line":488,"address":4239804,"length":1,"stats":{"Line":0}},{"line":492,"address":4334852,"length":1,"stats":{"Line":0}},{"line":493,"address":4334892,"length":1,"stats":{"Line":0}},{"line":494,"address":4334927,"length":1,"stats":{"Line":0}},{"line":495,"address":4335011,"length":1,"stats":{"Line":0}},{"line":497,"address":4335098,"length":1,"stats":{"Line":0}},{"line":501,"address":4335344,"length":1,"stats":{"Line":0}},{"line":502,"address":4335366,"length":1,"stats":{"Line":0}},{"line":503,"address":4335427,"length":1,"stats":{"Line":0}},{"line":504,"address":4335434,"length":1,"stats":{"Line":0}},{"line":505,"address":4335476,"length":1,"stats":{"Line":0}},{"line":509,"address":4335568,"length":1,"stats":{"Line":0}},{"line":514,"address":4335596,"length":1,"stats":{"Line":0}},{"line":517,"address":4336116,"length":1,"stats":{"Line":0}},{"line":536,"address":4336432,"length":1,"stats":{"Line":1}},{"line":541,"address":4336461,"length":1,"stats":{"Line":1}},{"line":542,"address":4336601,"length":1,"stats":{"Line":0}},{"line":544,"address":4336642,"length":1,"stats":{"Line":1}},{"line":545,"address":4336649,"length":1,"stats":{"Line":1}},{"line":546,"address":4336682,"length":1,"stats":{"Line":1}},{"line":548,"address":4336750,"length":1,"stats":{"Line":1}},{"line":549,"address":4336853,"length":1,"stats":{"Line":1}},{"line":550,"address":4336988,"length":1,"stats":{"Line":1}},{"line":551,"address":4336995,"length":1,"stats":{"Line":1}},{"line":553,"address":4337148,"length":1,"stats":{"Line":1}},{"line":555,"address":4337241,"length":1,"stats":{"Line":1}},{"line":556,"address":4337363,"length":1,"stats":{"Line":1}},{"line":557,"address":4337466,"length":1,"stats":{"Line":1}},{"line":560,"address":4337588,"length":1,"stats":{"Line":1}},{"line":563,"address":4337846,"length":1,"stats":{"Line":1}},{"line":564,"address":4337710,"length":1,"stats":{"Line":1}},{"line":566,"address":4337776,"length":1,"stats":{"Line":1}},{"line":570,"address":4338046,"length":1,"stats":{"Line":1}},{"line":571,"address":4338149,"length":1,"stats":{"Line":1}},{"line":572,"address":4338252,"length":1,"stats":{"Line":1}},{"line":575,"address":4338374,"length":1,"stats":{"Line":1}},{"line":578,"address":4338496,"length":1,"stats":{"Line":1}},{"line":579,"address":4338609,"length":1,"stats":{"Line":1}},{"line":581,"address":4338758,"length":1,"stats":{"Line":0}},{"line":582,"address":4338622,"length":1,"stats":{"Line":0}},{"line":586,"address":4338688,"length":1,"stats":{"Line":0}},{"line":592,"address":4339414,"length":1,"stats":{"Line":0}},{"line":595,"address":4339447,"length":1,"stats":{"Line":0}},{"line":599,"address":4339176,"length":1,"stats":{"Line":1}},{"line":600,"address":4338956,"length":1,"stats":{"Line":1}},{"line":601,"address":4339066,"length":1,"stats":{"Line":1}},{"line":604,"address":4339480,"length":1,"stats":{"Line":0}},{"line":607,"address":4339513,"length":1,"stats":{"Line":0}},{"line":609,"address":4337329,"length":1,"stats":{"Line":0}},{"line":610,"address":4339561,"length":1,"stats":{"Line":0}},{"line":612,"address":4337076,"length":1,"stats":{"Line":0}},{"line":613,"address":4339617,"length":1,"stats":{"Line":0}},{"line":615,"address":4337030,"length":1,"stats":{"Line":0}},{"line":617,"address":4339721,"length":1,"stats":{"Line":0}},{"line":618,"address":4339682,"length":1,"stats":{"Line":0}},{"line":619,"address":4339713,"length":1,"stats":{"Line":0}},{"line":621,"address":4239984,"length":1,"stats":{"Line":0}},{"line":622,"address":4239991,"length":1,"stats":{"Line":0}},{"line":624,"address":4339987,"length":1,"stats":{"Line":0}},{"line":628,"address":4340019,"length":1,"stats":{"Line":1}},{"line":629,"address":4340058,"length":1,"stats":{"Line":1}},{"line":630,"address":4340091,"length":1,"stats":{"Line":1}},{"line":631,"address":4340194,"length":1,"stats":{"Line":1}},{"line":632,"address":4340280,"length":1,"stats":{"Line":1}},{"line":633,"address":4340364,"length":1,"stats":{"Line":1}},{"line":634,"address":4340371,"length":1,"stats":{"Line":1}},{"line":635,"address":4340506,"length":1,"stats":{"Line":0}},{"line":638,"address":4340814,"length":1,"stats":{"Line":1}},{"line":640,"address":4341259,"length":1,"stats":{"Line":1}},{"line":642,"address":4341311,"length":1,"stats":{"Line":1}},{"line":643,"address":4341341,"length":1,"stats":{"Line":0}},{"line":644,"address":4341465,"length":1,"stats":{"Line":0}},{"line":646,"address":4341747,"length":1,"stats":{"Line":1}},{"line":648,"address":4341823,"length":1,"stats":{"Line":0}},{"line":650,"address":4340406,"length":1,"stats":{"Line":0}},{"line":652,"address":4342027,"length":1,"stats":{"Line":0}},{"line":653,"address":4342034,"length":1,"stats":{"Line":0}},{"line":654,"address":4240656,"length":1,"stats":{"Line":0}},{"line":655,"address":4342129,"length":1,"stats":{"Line":0}},{"line":659,"address":4342161,"length":1,"stats":{"Line":1}},{"line":664,"address":4343440,"length":1,"stats":{"Line":0}},{"line":668,"address":4343463,"length":1,"stats":{"Line":0}},{"line":669,"address":4343881,"length":1,"stats":{"Line":0}},{"line":670,"address":4343834,"length":1,"stats":{"Line":0}},{"line":671,"address":4241328,"length":1,"stats":{"Line":0}},{"line":674,"address":4344202,"length":1,"stats":{"Line":0}},{"line":675,"address":4344487,"length":1,"stats":{"Line":0}},{"line":679,"address":4663584,"length":1,"stats":{"Line":0}},{"line":685,"address":4663596,"length":1,"stats":{"Line":0}},{"line":686,"address":4663695,"length":1,"stats":{"Line":0}},{"line":687,"address":4663725,"length":1,"stats":{"Line":0}},{"line":688,"address":4663755,"length":1,"stats":{"Line":0}},{"line":692,"address":4242000,"length":1,"stats":{"Line":0}},{"line":696,"address":4242025,"length":1,"stats":{"Line":0}},{"line":697,"address":4242428,"length":1,"stats":{"Line":0}},{"line":698,"address":4242353,"length":1,"stats":{"Line":0}},{"line":702,"address":4242746,"length":1,"stats":{"Line":0}},{"line":703,"address":4243027,"length":1,"stats":{"Line":0}},{"line":707,"address":4344816,"length":1,"stats":{"Line":0}},{"line":708,"address":4344826,"length":1,"stats":{"Line":0}},{"line":709,"address":4344934,"length":1,"stats":{"Line":0}},{"line":710,"address":4344965,"length":1,"stats":{"Line":0}},{"line":711,"address":4345067,"length":1,"stats":{"Line":0}},{"line":712,"address":4345204,"length":1,"stats":{"Line":0}},{"line":713,"address":4345348,"length":1,"stats":{"Line":0}},{"line":717,"address":4346439,"length":1,"stats":{"Line":0}},{"line":726,"address":null,"length":0,"stats":{"Line":1}},{"line":727,"address":null,"length":0,"stats":{"Line":1}},{"line":728,"address":null,"length":0,"stats":{"Line":1}},{"line":729,"address":null,"length":0,"stats":{"Line":1}},{"line":730,"address":null,"length":0,"stats":{"Line":1}},{"line":731,"address":null,"length":0,"stats":{"Line":1}},{"line":732,"address":null,"length":0,"stats":{"Line":1}},{"line":735,"address":4512832,"length":1,"stats":{"Line":1}},{"line":736,"address":4512842,"length":1,"stats":{"Line":1}},{"line":737,"address":4512915,"length":1,"stats":{"Line":1}},{"line":738,"address":4512964,"length":1,"stats":{"Line":1}},{"line":739,"address":4512989,"length":1,"stats":{"Line":1}},{"line":740,"address":4513020,"length":1,"stats":{"Line":1}},{"line":741,"address":4513059,"length":1,"stats":{"Line":1}},{"line":742,"address":4513108,"length":1,"stats":{"Line":1}},{"line":743,"address":4513577,"length":1,"stats":{"Line":1}},{"line":744,"address":4513940,"length":1,"stats":{"Line":1}},{"line":745,"address":4514257,"length":1,"stats":{"Line":1}},{"line":749,"address":4369376,"length":1,"stats":{"Line":2}},{"line":750,"address":4514647,"length":1,"stats":{"Line":1}},{"line":751,"address":4514792,"length":1,"stats":{"Line":1}},{"line":752,"address":4514895,"length":1,"stats":{"Line":0}},{"line":753,"address":4515202,"length":1,"stats":{"Line":1}},{"line":754,"address":4515722,"length":1,"stats":{"Line":0}},{"line":755,"address":4515657,"length":1,"stats":{"Line":1}},{"line":759,"address":4515765,"length":1,"stats":{"Line":1}},{"line":776,"address":4517909,"length":1,"stats":{"Line":1}},{"line":777,"address":4518022,"length":1,"stats":{"Line":0}},{"line":779,"address":4518290,"length":1,"stats":{"Line":1}},{"line":783,"address":4369424,"length":1,"stats":{"Line":2}},{"line":784,"address":4519377,"length":1,"stats":{"Line":1}},{"line":785,"address":4519491,"length":1,"stats":{"Line":1}},{"line":786,"address":4520044,"length":1,"stats":{"Line":0}},{"line":787,"address":4519979,"length":1,"stats":{"Line":1}},{"line":791,"address":4520087,"length":1,"stats":{"Line":1}},{"line":808,"address":4522234,"length":1,"stats":{"Line":1}}],"covered":82,"coverable":325},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","change_conf.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Crate containing Duniter-rust core.\n\nuse crate::errors::DursCoreError;\nuse durs_conf::ChangeGlobalConf;\nuse durs_module::DursConfTrait;\nuse std::path::PathBuf;\n\n/// Change global configuration\npub fn change_global_conf\u003cDC: DursConfTrait\u003e(\n profile_path: \u0026PathBuf,\n conf: \u0026mut DC,\n user_request: ChangeGlobalConf,\n) -\u003e Result\u003c(), DursCoreError\u003e {\n match user_request {\n ChangeGlobalConf::ChangeCurrency(_) =\u003e {}\n ChangeGlobalConf::DisableModule(module_id) =\u003e conf.disable(module_id),\n ChangeGlobalConf::EnableModule(module_id) =\u003e conf.enable(module_id),\n ChangeGlobalConf::None() =\u003e {}\n }\n\n // Write new conf\n durs_conf::write_conf_file(\u0026durs_conf::get_conf_path(profile_path), conf)\n .map_err(DursCoreError::FailUpdateConf)\n}\n","traces":[{"line":24,"address":4242368,"length":1,"stats":{"Line":0}},{"line":29,"address":4242588,"length":1,"stats":{"Line":0}},{"line":30,"address":4242388,"length":1,"stats":{"Line":0}},{"line":31,"address":4242495,"length":1,"stats":{"Line":0}},{"line":32,"address":4242590,"length":1,"stats":{"Line":0}},{"line":37,"address":4242685,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":6},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","commands","dbex.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Durs-core cli : dbex subcommands.\n\nuse crate::commands::DursExecutableCoreCommand;\nuse crate::dbex;\nuse crate::errors::DursCoreError;\nuse crate::DursCore;\nuse durs_blockchain::{DBExQuery, DBExTxQuery, DBExWotQuery};\nuse durs_conf::DuRsConf;\n\n#[derive(StructOpt, Debug, Clone)]\n#[structopt(\n name = \"dbex\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// durs databases explorer\npub struct DbExOpt {\n #[structopt(short = \"c\", long = \"csv\")]\n /// csv output\n pub csv: bool,\n #[structopt(subcommand)]\n /// DbExSubCommand\n pub subcommand: DbExSubCommand,\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// dbex subcommands\npub enum DbExSubCommand {\n #[structopt(\n name = \"distance\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Web of Trust distances explorer\n DistanceOpt(DistanceOpt),\n #[structopt(\n name = \"members\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Members explorer\n MembersOpt(MembersOpt),\n #[structopt(\n name = \"member\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Member explorer\n MemberOpt(MemberOpt),\n #[structopt(\n name = \"balance\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Pubkeys’ balances explorer\n BalanceOpt(BalanceOpt),\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n/// DistanceOpt\npub struct DistanceOpt {\n #[structopt(short = \"r\", long = \"reverse\")]\n /// reverse order\n pub reverse: bool,\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n/// MembersOpt\npub struct MembersOpt {\n #[structopt(short = \"r\", long = \"reverse\")]\n /// reverse order\n pub reverse: bool,\n #[structopt(short = \"e\", long = \"expire\")]\n /// show members expire date\n pub expire: bool,\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// MemberOpt\npub struct MemberOpt {\n /// choose member uid\n pub uid: String,\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// BalanceOpt\npub struct BalanceOpt {\n /// public key or uid\n pub address: String,\n}\n\nimpl DursExecutableCoreCommand for DbExOpt {\n fn execute(self, durs_core: DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e {\n let profile_path = durs_core.soft_meta_datas.profile_path;\n\n match self.subcommand {\n DbExSubCommand::DistanceOpt(distance_opts) =\u003e dbex(\n profile_path,\n self.csv,\n \u0026DBExQuery::WotQuery(DBExWotQuery::AllDistances(distance_opts.reverse)),\n ),\n DbExSubCommand::MemberOpt(member_opts) =\u003e dbex(\n profile_path,\n self.csv,\n \u0026DBExQuery::WotQuery(DBExWotQuery::MemberDatas(member_opts.uid)),\n ),\n DbExSubCommand::MembersOpt(members_opts) =\u003e {\n if members_opts.expire {\n dbex(\n profile_path,\n self.csv,\n \u0026DBExQuery::WotQuery(DBExWotQuery::ExpireMembers(members_opts.reverse)),\n );\n } else {\n dbex(\n profile_path,\n self.csv,\n \u0026DBExQuery::WotQuery(DBExWotQuery::ListMembers(members_opts.reverse)),\n );\n }\n }\n DbExSubCommand::BalanceOpt(balance_opts) =\u003e dbex(\n profile_path,\n self.csv,\n \u0026DBExQuery::TxQuery(DBExTxQuery::Balance(balance_opts.address)),\n ),\n }\n\n Ok(())\n }\n}\n","traces":[{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":22},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","commands","keys.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Durs-core cli : keys subcommands.\n\nuse crate::commands::DursExecutableCoreCommand;\nuse crate::errors::DursCoreError;\nuse crate::DursCore;\nuse durs_conf::keys::*;\nuse durs_conf::DuRsConf;\n\n#[derive(StructOpt, Debug, Clone)]\n#[structopt(\n name = \"keys\",\n author = \"inso \u003cinso@tuta.io\u003e\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// keys management\npub struct KeysOpt {\n #[structopt(subcommand)]\n /// KeysSubCommand\n pub subcommand: KeysSubCommand,\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// keys subcommands\npub enum KeysSubCommand {\n /// Modify keys\n #[structopt(\n name = \"modify\",\n author = \"inso \u003cinso@tuta.io\u003e\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Modify(ModifyOpt),\n\n /// Clear keys\n #[structopt(\n name = \"clear\",\n author = \"inso \u003cinso@tuta.io\u003e\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Clear(ClearOpt),\n\n /// Show keys\n #[structopt(\n name = \"show\",\n author = \"inso \u003cinso@tuta.io\u003e\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Show(ShowOpt),\n\n #[structopt(\n name = \"wizard\",\n author = \"inso \u003cinso@tuta.io\u003e\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Keys generator wizard\n Wizard(WizardOpt),\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// ModifyOpt\npub struct ModifyOpt {\n #[structopt(subcommand)]\n /// Modify sub commands\n pub subcommand: ModifySubCommand,\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// keys modify subcommands\npub enum ModifySubCommand {\n #[structopt(\n name = \"member\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Salt and password of member key\n MemberSaltPassword(SaltPasswordOpt),\n\n #[structopt(\n name = \"network\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n /// Salt and password of network key \n NetworkSaltPassword(SaltPasswordOpt),\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n/// ClearOpt\npub struct ClearOpt {\n #[structopt(short = \"m\", long = \"member\")]\n /// True if we change member key\n pub member: bool,\n\n #[structopt(short = \"n\", long = \"network\")]\n /// True if we change network key\n pub network: bool,\n\n #[structopt(short = \"a\", long = \"all\")]\n /// True if we change member and network key\n pub all: bool,\n}\n\n#[derive(StructOpt, Debug, Clone)]\n/// SaltPasswordOpt\npub struct SaltPasswordOpt {\n #[structopt(long = \"salt\")]\n /// Salt of key generator\n pub salt: String,\n\n #[structopt(long = \"password\")]\n /// Password of key generator\n pub password: String,\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n/// WizardOpt\npub struct WizardOpt {}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n/// ShowOpt\npub struct ShowOpt {}\n\nimpl DursExecutableCoreCommand for KeysOpt {\n fn execute(self, durs_core: DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e {\n let profile_path = durs_core.soft_meta_datas.profile_path;\n let keypairs_file = durs_core.options.keypairs_file;\n let keypairs = durs_core.keypairs;\n\n match self.subcommand {\n KeysSubCommand::Wizard(_) =\u003e {\n let new_keypairs = key_wizard(keypairs).unwrap();\n save_keypairs(profile_path, \u0026keypairs_file, new_keypairs)\n .map_err(DursCoreError::FailWriteKeypairsFile)\n }\n KeysSubCommand::Modify(modify_opt) =\u003e match modify_opt.subcommand {\n ModifySubCommand::NetworkSaltPassword(network_opt) =\u003e {\n let new_keypairs =\n modify_network_keys(\u0026network_opt.salt, \u0026network_opt.password, keypairs);\n save_keypairs(profile_path, \u0026keypairs_file, new_keypairs)\n .map_err(DursCoreError::FailWriteKeypairsFile)\n }\n ModifySubCommand::MemberSaltPassword(member_opt) =\u003e {\n let new_keypairs =\n modify_member_keys(\u0026member_opt.salt, \u0026member_opt.password, keypairs);\n save_keypairs(profile_path, \u0026keypairs_file, new_keypairs)\n .map_err(DursCoreError::FailWriteKeypairsFile)\n }\n },\n KeysSubCommand::Clear(clear_opt) =\u003e {\n let new_keypairs = clear_keys(\n clear_opt.network || clear_opt.all,\n clear_opt.member || clear_opt.all,\n keypairs,\n );\n save_keypairs(profile_path, \u0026keypairs_file, new_keypairs)\n .map_err(DursCoreError::FailWriteKeypairsFile)\n }\n KeysSubCommand::Show(_) =\u003e {\n show_keys(keypairs);\n Ok(())\n }\n }\n }\n}\n","traces":[{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":30},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","commands","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Define durs-core cli subcommands options.\n\npub mod dbex;\npub mod keys;\npub mod modules;\npub mod reset;\npub mod start;\n\nuse crate::errors::DursCoreError;\nuse crate::DursCore;\npub use dbex::*;\nuse durs_conf::DuRsConf;\npub use durs_network::cli::sync::SyncOpt;\npub use keys::KeysOpt;\nuse log::Level;\npub use modules::*;\npub use reset::*;\npub use start::*;\nuse std::path::PathBuf;\n\n/// Dunitrust core options\npub struct DursCoreOptions {\n /// Keypairs file path\n pub keypairs_file: Option\u003cPathBuf\u003e,\n /// Set log level.\n pub logs_level: Option\u003cLevel\u003e,\n /// Print logs in standard output\n pub log_stdout: bool,\n /// Set a custom user profile name\n pub profile_name: Option\u003cString\u003e,\n /// Path where user profiles are persisted\n pub profiles_path: Option\u003cPathBuf\u003e,\n}\n\n/// Dunitrust executable command\npub trait DursExecutableCoreCommand {\n /// Execute Dunitrust command\n fn execute(self, durs_core: DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e;\n}\n\n/// Executable module command\npub trait ExecutableModuleCommand {\n /// Execute module command\n fn execute_module_command(self, options: DursCoreOptions) -\u003e Result\u003c(), DursCoreError\u003e;\n}\n\n/// Dunitrust command with options\npub struct DursCommand\u003cT: ExecutableModuleCommand\u003e {\n /// Dunitrust core options\n pub options: DursCoreOptions,\n /// Dunitrust command\n pub command: DursCommandEnum\u003cT\u003e,\n}\n\n/// Dunitrust command\npub enum DursCommandEnum\u003cT: ExecutableModuleCommand\u003e {\n /// Core command\n Core(DursCoreCommand),\n /// Other command\n Other(T),\n}\n\nimpl\u003cT: ExecutableModuleCommand\u003e DursCommand\u003cT\u003e {\n /// Execute Dunitrust command\n pub fn execute\u003cPlugFunc\u003e(\n self,\n soft_name: \u0026'static str,\n soft_version: \u0026'static str,\n plug_modules: PlugFunc,\n ) -\u003e Result\u003c(), DursCoreError\u003e\n where\n PlugFunc: FnMut(\u0026mut DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e,\n {\n match self.command {\n DursCommandEnum::Core(core_cmd) =\u003e DursCore::execute_core_command(\n core_cmd,\n self.options,\n vec![],\n plug_modules,\n soft_name,\n soft_version,\n ),\n DursCommandEnum::Other(cmd) =\u003e cmd.execute_module_command(self.options),\n }\n }\n}\n\n#[derive(StructOpt, Debug)]\n/// Core cli subcommands\npub enum DursCoreCommand {\n /// Enable a module\n EnableOpt(EnableOpt),\n /// Disable a module\n DisableOpt(DisableOpt),\n /// List available modules\n ListModulesOpt(ListModulesOpt),\n /// Start node\n StartOpt(StartOpt),\n /// Synchronize\n SyncOpt(SyncOpt),\n /// Reset data or conf or all\n ResetOpt(ResetOpt),\n /// Database explorer\n DbExOpt(DbExOpt),\n /// Keys operations\n KeysOpt(KeysOpt),\n}\n\n/// InvalidInput\n#[derive(Debug, Copy, Clone)]\npub struct InvalidInput(\u0026'static str);\n\nimpl ToString for InvalidInput {\n fn to_string(\u0026self) -\u003e String {\n String::from(self.0)\n }\n}\n","traces":[{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","commands","modules.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Durs-core cli : modules manager subcommands.\n\nuse crate::commands::DursExecutableCoreCommand;\nuse crate::errors::DursCoreError;\nuse crate::DursCore;\nuse durs_conf::{ChangeGlobalConf, DuRsConf};\nuse durs_module::*;\nuse std::collections::HashSet;\n\n#[derive(StructOpt, Debug, Clone)]\n#[structopt(\n name = \"enable\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// Enable some module\npub struct EnableOpt {\n #[structopt(parse(from_str))]\n /// The module name to enable\n pub module_name: ModuleName,\n}\n\nimpl DursExecutableCoreCommand for EnableOpt {\n #[inline]\n fn execute(self, mut durs_core: DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e {\n crate::change_conf::change_global_conf(\n \u0026durs_core.soft_meta_datas.profile_path.clone(),\n \u0026mut durs_core.soft_meta_datas.conf,\n ChangeGlobalConf::EnableModule(self.module_name),\n )\n }\n}\n\n#[derive(StructOpt, Debug, Clone)]\n#[structopt(\n name = \"disable\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// Disable some module\npub struct DisableOpt {\n #[structopt(parse(from_str))]\n /// The module name to disable\n pub module_name: ModuleName,\n}\n\nimpl DursExecutableCoreCommand for DisableOpt {\n #[inline]\n fn execute(self, mut durs_core: DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e {\n crate::change_conf::change_global_conf(\n \u0026durs_core.soft_meta_datas.profile_path.clone(),\n \u0026mut durs_core.soft_meta_datas.conf,\n ChangeGlobalConf::DisableModule(self.module_name),\n )\n }\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n#[structopt(\n name = \"modules\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// list module\npub struct ListModulesOpt {\n #[structopt(short = \"d\")]\n /// list only disabled modules\n pub disabled: bool,\n #[structopt(short = \"e\")]\n /// list only enabled modules\n pub enabled: bool,\n #[structopt(short = \"n\")]\n /// list only network modules\n pub network: bool,\n #[structopt(short = \"s\")]\n /// list only modules having access to the secret member key\n pub secret: bool,\n}\n\nimpl ListModulesOpt {\n /// Extract modules filters from cli options\n pub fn get_filters(self) -\u003e HashSet\u003cModulesFilter\u003e {\n let mut filters = HashSet::with_capacity(4);\n if self.disabled {\n filters.insert(ModulesFilter::Enabled(false));\n }\n if self.enabled {\n filters.insert(ModulesFilter::Enabled(true));\n }\n if self.network {\n filters.insert(ModulesFilter::Network());\n }\n if self.secret {\n filters.insert(ModulesFilter::RequireMemberPrivKey());\n }\n filters\n }\n}\n","traces":[{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":19},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","commands","reset.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Durs-core cli : reset subcommand.\n\nuse super::InvalidInput;\nuse crate::commands::DursExecutableCoreCommand;\nuse crate::errors::DursCoreError;\nuse crate::DursCore;\nuse durs_conf::DuRsConf;\nuse std::fs;\nuse std::str::FromStr;\n\n#[derive(Debug, Copy, Clone)]\n/// Reset type\npub enum ResetType {\n /// Reset datas\n Datas,\n /// Reset configuration\n Conf,\n /// Reset all\n All,\n}\n\nimpl FromStr for ResetType {\n type Err = InvalidInput;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match source {\n \"data\" =\u003e Ok(ResetType::Datas),\n \"conf\" =\u003e Ok(ResetType::Conf),\n \"all\" =\u003e Ok(ResetType::All),\n _ =\u003e Err(InvalidInput(\"Kind of data to be reseted: data, conf, all.\")),\n }\n }\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n/// Reset data or configuration\npub struct ResetOpt {\n /// Kind of data to be reseted: data, conf, all\n pub reset_type: ResetType,\n}\n\nimpl DursExecutableCoreCommand for ResetOpt {\n fn execute(self, durs_core: DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e {\n let profile_path = durs_core.soft_meta_datas.profile_path;\n\n match self.reset_type {\n ResetType::Datas =\u003e {\n let mut currency_datas_path = profile_path;\n currency_datas_path.push(durs_conf::constants::MODULES_DATAS_FOLDER);\n fs::remove_dir_all(currency_datas_path.as_path())\n .map_err(DursCoreError::FailRemoveDatasDir)\n }\n ResetType::Conf =\u003e {\n let mut conf_file_path = profile_path.clone();\n conf_file_path.push(durs_conf::constants::CONF_FILENAME);\n fs::remove_file(conf_file_path.as_path()).map_err(DursCoreError::FailRemoveConfFile)\n }\n ResetType::All =\u003e fs::remove_dir_all(profile_path.as_path())\n .map_err(DursCoreError::FailRemoveProfileDir),\n }\n }\n}\n","traces":[{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":20},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","errors.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Manage Dunitrust core errors.\n\nuse crate::logger::InitLoggerError;\nuse dup_currency_params::db::CurrencyParamsDbError;\nuse durs_module::{ModuleStaticName, PlugModuleError};\nuse failure::{Error, Fail};\n\n#[derive(Debug, Fail)]\n/// Dunitrust server error\npub enum DursCoreError {\n /// Error with configuration file\n #[fail(display = \"Error with configuration file: {}\", _0)]\n ConfFileError(durs_conf::DursConfFileError),\n /// Generic error that impl Fail\n #[fail(display = \"{}\", _0)]\n Error(Error),\n /// Fail to read currency params DB\n #[fail(display = \"Fail to read currency params DB: {}\", _0)]\n FailReadCurrencyParamsDb(CurrencyParamsDbError),\n /// Fail to remove configuration file\n #[fail(display = \"Fail to remove configuration file: {}\", _0)]\n FailRemoveConfFile(std::io::Error),\n /// Fail to remove profile directory\n #[fail(display = \"Fail to remove profile directory: {}\", _0)]\n FailRemoveProfileDir(std::io::Error),\n /// Fail to remove datas directory\n #[fail(display = \"Fail to remove datas directory: {}\", _0)]\n FailRemoveDatasDir(std::io::Error),\n /// Fail to update configuration file\n #[fail(display = \"Fail to update configuration file: {}\", _0)]\n FailUpdateConf(std::io::Error),\n /// Fail to write keypairs file\n #[fail(display = \"could not write keypairs file: {}\", _0)]\n FailWriteKeypairsFile(std::io::Error),\n /// Error on initialization of the logger\n #[fail(display = \"Error on initialization of the logger: {}\", _0)]\n InitLoggerError(InitLoggerError),\n /// Plug module error\n #[fail(display = \"Error on loading module '{}': {}\", module_name, error)]\n PlugModuleError {\n /// Module name\n module_name: ModuleStaticName,\n /// Error details\n error: PlugModuleError,\n },\n /// Sync without source and without option local\n #[fail(display = \"Please specify the url of a trusted node or use the --local option.\")]\n SyncWithoutSource,\n}\n\nimpl From\u003cInitLoggerError\u003e for DursCoreError {\n fn from(e: InitLoggerError) -\u003e Self {\n DursCoreError::InitLoggerError(e)\n }\n}\n","traces":[{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Crate containing Dunitrust core.\n\n//#![cfg_attr(feature = \"cargo-clippy\", allow(implicit_hasher))]\n#![deny(\n missing_docs,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate structopt;\n\nmod change_conf;\npub mod commands;\nmod constants;\npub mod errors;\nmod logger;\nmod router;\n\nuse crate::commands::*;\nuse crate::constants::DEFAULT_USER_PROFILE;\nuse crate::errors::DursCoreError;\nuse dup_currency_params::CurrencyName;\nuse durs_blockchain::{BlockchainModule, DBExQuery};\nuse durs_common_tools::fatal_error;\npub use durs_conf::{\n constants::KEYPAIRS_FILENAME, keys::*, ChangeGlobalConf, DuRsConf, DuniterKeyPairs,\n};\nuse durs_message::*;\nuse durs_module::*;\nuse durs_network::NetworkModule;\nuse std::collections::HashMap;\nuse std::path::PathBuf;\nuse std::sync::mpsc;\nuse std::thread;\nuse unwrap::unwrap;\n\n#[macro_export]\n/// Plug modules in durs core\nmacro_rules! durs_plug {\n ( [ $( $NetworkModule:ty ),* ], [ $( $Module:ty ),* ] ) =\u003e {\n {\n |core| {\n $(core.plug::\u003c$Module\u003e()?;)*\n $(core.plug_network::\u003c$NetworkModule\u003e()?;)*\n Ok(())\n }\n }\n };\n}\n\n/// Dunitrust Core Datas\npub struct DursCore\u003cDC: DursConfTrait\u003e {\n /// Currency name\n pub currency_name: Option\u003cCurrencyName\u003e,\n /// Dunitrust core options\n pub options: DursCoreOptions,\n /// Does the entered command require to launch server ?\n server_command: Option\u003cServerMode\u003e,\n /// Software meta datas\n pub soft_meta_datas: SoftwareMetaDatas\u003cDC\u003e,\n /// Keypairs\n pub keypairs: DuniterKeyPairs,\n /// Run duration. Zero = infinite duration.\n pub run_duration_in_secs: u64,\n /// Sender channel of router thread\n pub router_sender: Option\u003cmpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e\u003e,\n /// Count the number of plugged network modules\n pub network_modules_count: usize,\n /// Modules names\n pub modules_names: Vec\u003cModuleStaticName\u003e,\n /// Threads handlers that execute plugged modules\n pub threads: HashMap\u003cModuleStaticName, thread::JoinHandle\u003c()\u003e\u003e,\n}\n\n#[derive(Debug, Clone)]\n/// Server command\nenum ServerMode {\n /// Start\n Start(),\n /// Sync (SyncEndpoint)\n Sync(SyncOpt),\n /// List modules\n ListModules(ListModulesOpt),\n}\n\nimpl DursCore\u003cDuRsConf\u003e {\n /// Execute module command\n pub fn execute_module_command\u003cM: DursModule\u003cDuRsConf, DursMsg\u003e\u003e(\n durs_core_opts: DursCoreOptions,\n module_command: M::ModuleOpt,\n soft_name: \u0026'static str,\n soft_version: \u0026'static str,\n ) -\u003e Result\u003c(), DursCoreError\u003e {\n let mut durs_core = DursCore::\u003cDuRsConf\u003e::init(soft_name, soft_version, durs_core_opts, 0)?;\n // Load module conf and keys\n let module_conf_json = durs_core\n .soft_meta_datas\n .conf\n .clone()\n .modules()\n .get(\u0026M::name().to_string().as_str())\n .cloned();\n\n let ((module_conf, module_user_conf), required_keys) = get_module_conf_and_keys::\u003cM\u003e(\n durs_core.currency_name.as_ref(),\n \u0026durs_core.soft_meta_datas.conf.get_global_conf(),\n module_conf_json,\n durs_core.keypairs,\n )\n .map_err(|e| DursCoreError::PlugModuleError {\n module_name: M::name(),\n error: e.into(),\n })?;\n // Execute module subcommand\n let new_module_conf = M::exec_subcommand(\n \u0026durs_core.soft_meta_datas,\n required_keys,\n module_conf,\n module_user_conf,\n module_command,\n );\n\n durs_conf::write_new_module_conf(\n \u0026mut durs_core.soft_meta_datas.conf,\n durs_core.soft_meta_datas.profile_path.clone(),\n M::name().into(),\n unwrap!(serde_json::value::to_value(new_module_conf)),\n );\n\n Ok(())\n }\n\n /// Execute core command\n pub fn execute_core_command\u003cPlugFunc\u003e(\n core_command: DursCoreCommand,\n durs_core_opts: DursCoreOptions,\n external_followers: Vec\u003cmpsc::Sender\u003cDursMsg\u003e\u003e,\n mut plug_modules: PlugFunc,\n soft_name: \u0026'static str,\n soft_version: \u0026'static str,\n ) -\u003e Result\u003c(), DursCoreError\u003e\n where\n PlugFunc: FnMut(\u0026mut DursCore\u003cDuRsConf\u003e) -\u003e Result\u003c(), DursCoreError\u003e,\n {\n // Instantiate durs core\n let mut durs_core = DursCore::\u003cDuRsConf\u003e::init(soft_name, soft_version, durs_core_opts, 0)?;\n\n let profile_path = durs_core.soft_meta_datas.profile_path.clone();\n\n /*\n * CORE COMMAND PROCESSING\n */\n match core_command {\n DursCoreCommand::DisableOpt(opts) =\u003e opts.execute(durs_core),\n DursCoreCommand::EnableOpt(opts) =\u003e opts.execute(durs_core),\n DursCoreCommand::ListModulesOpt(opts) =\u003e {\n durs_core.server_command = Some(ServerMode::ListModules(opts));\n\n durs_core.router_sender = Some(router::start_router(\n 0,\n profile_path.clone(),\n durs_core.soft_meta_datas.conf.clone(),\n vec![],\n ));\n plug_modules(\u0026mut durs_core)\n }\n DursCoreCommand::StartOpt(_opts) =\u003e {\n durs_core.server_command = Some(ServerMode::Start());\n\n durs_core.router_sender = Some(router::start_router(\n durs_core.run_duration_in_secs,\n profile_path.clone(),\n durs_core.soft_meta_datas.conf.clone(),\n external_followers,\n ));\n plug_modules(\u0026mut durs_core)?;\n durs_core.start()\n }\n DursCoreCommand::SyncOpt(opts) =\u003e {\n if opts.local_path.is_some() {\n // Launch local sync\n BlockchainModule::local_sync(\n \u0026durs_core.soft_meta_datas.conf,\n durs_core.currency_name.as_ref(),\n profile_path.clone(),\n opts,\n )\n .map_err(DursCoreError::Error)?;\n Ok(())\n } else if opts.source.is_some() {\n durs_core.server_command = Some(ServerMode::Sync(opts));\n\n durs_core.router_sender = Some(router::start_router(\n durs_core.run_duration_in_secs,\n profile_path.clone(),\n durs_core.soft_meta_datas.conf.clone(),\n external_followers,\n ));\n plug_modules(\u0026mut durs_core)?;\n durs_core.start()\n } else {\n Err(DursCoreError::SyncWithoutSource)\n }\n }\n DursCoreCommand::DbExOpt(opts) =\u003e opts.execute(durs_core),\n DursCoreCommand::ResetOpt(opts) =\u003e opts.execute(durs_core),\n DursCoreCommand::KeysOpt(opts) =\u003e opts.execute(durs_core),\n }\n }\n /// Initialize Dunitrust core\n fn init(\n soft_name: \u0026'static str,\n soft_version: \u0026'static str,\n durs_core_opts: DursCoreOptions,\n run_duration_in_secs: u64,\n ) -\u003e Result\u003cDursCore\u003cDuRsConf\u003e, DursCoreError\u003e {\n // get profile path\n let profile_path = durs_conf::get_profile_path(\n \u0026durs_core_opts.profiles_path,\n \u0026durs_core_opts\n .profile_name\n .clone()\n .unwrap_or_else(|| DEFAULT_USER_PROFILE.to_owned()),\n );\n\n // Init logger\n logger::init(\n profile_path.clone(),\n soft_name,\n soft_version,\n \u0026durs_core_opts,\n )?;\n\n // Load global conf\n let (conf, keypairs) =\n durs_conf::load_conf(profile_path.clone(), \u0026durs_core_opts.keypairs_file)\n .map_err(DursCoreError::ConfFileError)?;\n info!(\"Success to load global conf.\");\n\n // Get currency name\n let currency_name = dup_currency_params::db::get_currency_name(durs_conf::get_datas_path(\n profile_path.clone(),\n ))\n .map_err(DursCoreError::FailReadCurrencyParamsDb)?;\n\n // Instanciate durs core\n Ok(DursCore {\n currency_name,\n keypairs,\n options: durs_core_opts,\n modules_names: Vec::new(),\n network_modules_count: 0,\n router_sender: None,\n run_duration_in_secs,\n server_command: None,\n soft_meta_datas: SoftwareMetaDatas {\n conf,\n profile_path,\n soft_name,\n soft_version,\n },\n threads: HashMap::new(),\n })\n }\n /// Start durs server\n pub fn start(mut self) -\u003e Result\u003c(), DursCoreError\u003e {\n if self.network_modules_count == 0 {\n fatal_error!(\n \"Dev error: no network module found: you must plug at least one network module !\"\n );\n }\n\n // Create blockchain module channel\n let (blockchain_sender, blockchain_receiver): (\n mpsc::Sender\u003cDursMsg\u003e,\n mpsc::Receiver\u003cDursMsg\u003e,\n ) = mpsc::channel();\n\n let router_sender = if let Some(ref router_sender) = self.router_sender {\n router_sender\n } else {\n fatal_error!(\"Dev error: try to start core without router_sender !\");\n };\n\n // Send expected modules count to router thread\n router_sender\n .send(RouterThreadMessage::ModulesCount(\n self.modules_names.len() + 1,\n ))\n .expect(\"Fatal error: fail to send expected modules count to router thread !\");\n\n // Send blockchain module registration to router thread\n router_sender\n .send(RouterThreadMessage::ModuleRegistration {\n static_name: BlockchainModule::name(),\n sender: blockchain_sender,\n roles: vec![ModuleRole::BlockchainDatas, ModuleRole::BlockValidation],\n events_subscription: vec![ModuleEvent::NewBlockFromNetwork, ModuleEvent::SyncEvent],\n reserved_apis_parts: vec![],\n endpoints: vec![],\n })\n .expect(\"Fatal error: fail to send blockchain registration to router thread !\");\n\n // Get profile path\n let profile_path = self.soft_meta_datas.profile_path;\n\n // Instantiate blockchain module and load is conf\n let mut blockchain_module = BlockchainModule::load_blockchain_conf(\n router_sender.clone(),\n profile_path,\n RequiredKeysContent::MemberKeyPair(None),\n );\n info!(\"Success to load Blockchain module.\");\n\n // Start blockchain module in thread\n let thread_builder = thread::Builder::new().name(BlockchainModule::name().0.into());\n let sync_opts = if let Some(ServerMode::Sync(opts)) = self.server_command {\n Some(opts)\n } else {\n None\n };\n let blockchain_thread_handler = thread_builder\n .spawn(move || blockchain_module.start_blockchain(\u0026blockchain_receiver, sync_opts))\n .expect(\"Fatal error: fail to spawn module main thread !\");\n\n // Wait until all modules threads are finished\n for module_static_name in \u0026self.modules_names {\n if let Some(module_thread_handler) = self.threads.remove(module_static_name) {\n if let Err(err) = module_thread_handler.join() {\n error!(\"'{}' module thread panic : {:?}\", module_static_name.0, err);\n }\n }\n }\n\n // Wait until blockchain main thread finished\n if let Err(err) = blockchain_thread_handler.join() {\n error!(\"'blockchain' thread panic : {:?}\", err);\n }\n\n Ok(())\n }\n #[inline]\n /// Plug a network module\n pub fn plug_network\u003cNM: NetworkModule\u003cDuRsConf, DursMsg\u003e\u003e(\n \u0026mut self,\n ) -\u003e Result\u003c(), DursCoreError\u003e {\n self.plug_network_::\u003cNM\u003e()\n .map_err(|error| DursCoreError::PlugModuleError {\n module_name: NM::name(),\n error,\n })\n }\n fn plug_network_\u003cNM: NetworkModule\u003cDuRsConf, DursMsg\u003e\u003e(\n \u0026mut self,\n ) -\u003e Result\u003c(), PlugModuleError\u003e {\n let enabled = enabled::\u003cDuRsConf, DursMsg, NM\u003e(\u0026self.soft_meta_datas.conf);\n if enabled {\n self.network_modules_count += 1;\n if let Some(ServerMode::Sync(ref network_sync)) = self.server_command {\n let sync_module_name =\n if let Some(ref sync_module_name) = network_sync.sync_module_name {\n sync_module_name.clone()\n } else {\n self.soft_meta_datas\n .conf\n .get_global_conf()\n .default_sync_module()\n .0\n };\n\n if NM::name().0 == sync_module_name {\n // Start module in a new thread\n let router_sender = self\n .router_sender\n .clone()\n .expect(\"Try to start a core without router_sender !\");\n let soft_meta_datas = self.soft_meta_datas.clone();\n let module_conf_json = self\n .soft_meta_datas\n .conf\n .clone()\n .modules()\n .get(\u0026NM::name().to_string().as_str())\n .cloned();\n let keypairs = self.keypairs;\n\n // Load module conf and keys\n let ((module_conf, _), required_keys) = get_module_conf_and_keys::\u003cNM\u003e(\n self.currency_name.as_ref(),\n \u0026soft_meta_datas.conf.get_global_conf(),\n module_conf_json,\n keypairs,\n )?;\n\n let sync_params = network_sync.clone();\n let thread_builder = thread::Builder::new().name(NM::name().0.into());\n self.threads.insert(\n NM::name(),\n thread_builder\n .spawn(move || {\n NM::sync(\n \u0026soft_meta_datas,\n required_keys,\n module_conf,\n router_sender,\n sync_params,\n )\n .unwrap_or_else(|_| {\n fatal_error!(\n \"Fatal error : fail to load module '{}' !\",\n NM::name().to_string()\n )\n });\n })\n .map_err(|e| PlugModuleError::FailSpawnModuleThread {\n module_name: NM::name(),\n error: e,\n })?,\n );\n self.modules_names.push(NM::name());\n info!(\"Success to load {} module.\", NM::name().to_string());\n Ok(())\n } else {\n debug!(\"Module '{}' not used for sync.\", NM::name());\n Ok(())\n }\n } else {\n self.plug_::\u003cNM\u003e(true)\n }\n } else {\n self.plug_::\u003cNM\u003e(true)\n }\n }\n #[inline]\n /// Plug a module\n pub fn plug\u003cM: DursModule\u003cDuRsConf, DursMsg\u003e\u003e(\u0026mut self) -\u003e Result\u003c(), DursCoreError\u003e {\n self.plug_::\u003cM\u003e(false)\n .map_err(|error| DursCoreError::PlugModuleError {\n module_name: M::name(),\n error,\n })\n }\n\n /// Plug a module\n pub fn plug_\u003cM: DursModule\u003cDuRsConf, DursMsg\u003e\u003e(\n \u0026mut self,\n is_network_module: bool,\n ) -\u003e Result\u003c(), PlugModuleError\u003e {\n let enabled = enabled::\u003cDuRsConf, DursMsg, M\u003e(\u0026self.soft_meta_datas.conf);\n if enabled {\n let (launch_module, sync_opts) = match self.server_command {\n Some(ServerMode::Start()) =\u003e (true, None),\n Some(ServerMode::Sync(ref opts)) =\u003e (M::launchable_as_sync(), Some(opts.clone())),\n Some(_) | None =\u003e (false, None),\n };\n\n if launch_module {\n // Start module in a new thread\n let router_sender_clone = self\n .router_sender\n .clone()\n .expect(\"Try to start a core without router_sender !\");\n let soft_meta_datas = self.soft_meta_datas.clone();\n let module_conf_json = self\n .soft_meta_datas\n .conf\n .clone()\n .modules()\n .get(\u0026M::name().to_string().as_str())\n .cloned();\n let keypairs = self.keypairs;\n // Load module conf and keys\n let ((module_conf, _), required_keys) = get_module_conf_and_keys::\u003cM\u003e(\n self.currency_name.as_ref(),\n \u0026soft_meta_datas.conf.get_global_conf(),\n module_conf_json,\n keypairs,\n )?;\n\n let thread_builder = thread::Builder::new().name(M::name().0.into());\n self.threads.insert(\n M::name(),\n thread_builder\n .spawn(move || {\n if let Some(sync_opts) = sync_opts {\n M::start_at_sync(\n \u0026soft_meta_datas,\n required_keys,\n module_conf,\n router_sender_clone,\n sync_opts.cautious_mode,\n sync_opts.unsafe_mode,\n )\n .unwrap_or_else(|e| fatal_error!(\"Module '{}': {}\", M::name(), e));\n } else {\n M::start(\n \u0026soft_meta_datas,\n required_keys,\n module_conf,\n router_sender_clone,\n )\n .unwrap_or_else(|e| fatal_error!(\"Module '{}': {}\", M::name(), e));\n }\n })\n .map_err(|e| PlugModuleError::FailSpawnModuleThread {\n module_name: M::name(),\n error: e,\n })?,\n );\n self.modules_names.push(M::name());\n info!(\"Success to load {} module.\", M::name().to_string());\n }\n }\n if let Some(ServerMode::ListModules(ref options)) = self.server_command {\n if module_valid_filters::\u003cDuRsConf, DursMsg, M, std::collections::hash_map::RandomState\u003e(\n \u0026self.soft_meta_datas.conf,\n \u0026options.get_filters(),\n is_network_module,\n ) {\n if enabled {\n println!(\"{}\", M::name().to_string());\n } else {\n println!(\"{} (disabled)\", M::name().to_string());\n }\n }\n }\n Ok(())\n }\n}\n\n/// Module configurations and required keys\npub type ModuleConfsAndKeys\u003cM\u003e = (\n (\n \u003cM as DursModule\u003cDuRsConf, DursMsg\u003e\u003e::ModuleConf,\n Option\u003c\u003cM as DursModule\u003cDuRsConf, DursMsg\u003e\u003e::ModuleUserConf\u003e,\n ),\n RequiredKeysContent,\n);\n\n/// Get module conf and keys\npub fn get_module_conf_and_keys\u003cM: DursModule\u003cDuRsConf, DursMsg\u003e\u003e(\n currency_name: Option\u003c\u0026CurrencyName\u003e,\n global_conf: \u0026\u003cDuRsConf as DursConfTrait\u003e::GlobalConf,\n module_conf_json: Option\u003cserde_json::Value\u003e,\n keypairs: DuniterKeyPairs,\n) -\u003e Result\u003cModuleConfsAndKeys\u003cM\u003e, ModuleConfError\u003e {\n Ok((\n get_module_conf::\u003cM\u003e(currency_name, global_conf, module_conf_json)?,\n DuniterKeyPairs::get_required_keys_content(M::ask_required_keys(), keypairs),\n ))\n}\n\n/// get module conf\npub fn get_module_conf\u003cM: DursModule\u003cDuRsConf, DursMsg\u003e\u003e(\n currency_name: Option\u003c\u0026CurrencyName\u003e,\n global_conf: \u0026\u003cDuRsConf as DursConfTrait\u003e::GlobalConf,\n module_conf_json: Option\u003cserde_json::Value\u003e,\n) -\u003e Result\u003c(M::ModuleConf, Option\u003cM::ModuleUserConf\u003e), ModuleConfError\u003e {\n if let Some(module_conf_json) = module_conf_json {\n let module_user_conf: Option\u003cM::ModuleUserConf\u003e =\n serde_json::from_str(module_conf_json.to_string().as_str())?;\n M::generate_module_conf(currency_name, global_conf, module_user_conf)\n } else {\n M::generate_module_conf(currency_name, global_conf, None)\n }\n}\n\n/// Launch databases explorer\npub fn dbex(profile_path: PathBuf, csv: bool, query: \u0026DBExQuery) {\n // Launch databases explorer\n BlockchainModule::dbex(profile_path, csv, query);\n}\n\n#[inline]\n/// Get sofware informations\npub fn get_software_infos(soft_name: \u0026'static str, soft_version: \u0026'static str) -\u003e String {\n if let Some(last_commit_hash) = get_last_commit_hash() {\n format!(\n \"{} v{}-dev (commit {})\",\n soft_name, soft_version, last_commit_hash\n )\n } else {\n format!(\"{} v{}\", soft_name, soft_version)\n }\n}\n\n#[inline]\n/// Get last commit hash\npub fn get_last_commit_hash() -\u003e Option\u003c\u0026'static str\u003e {\n option_env!(\"LAST_COMMIT_HASH\")\n}\n","traces":[{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":197,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":205,"address":null,"length":0,"stats":{"Line":0}},{"line":206,"address":null,"length":0,"stats":{"Line":0}},{"line":207,"address":null,"length":0,"stats":{"Line":0}},{"line":208,"address":null,"length":0,"stats":{"Line":0}},{"line":209,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":223,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":0}},{"line":230,"address":null,"length":0,"stats":{"Line":0}},{"line":234,"address":null,"length":0,"stats":{"Line":0}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":242,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":244,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":0}},{"line":250,"address":null,"length":0,"stats":{"Line":0}},{"line":251,"address":null,"length":0,"stats":{"Line":0}},{"line":252,"address":null,"length":0,"stats":{"Line":0}},{"line":253,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":259,"address":null,"length":0,"stats":{"Line":0}},{"line":260,"address":null,"length":0,"stats":{"Line":0}},{"line":261,"address":null,"length":0,"stats":{"Line":0}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":265,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":270,"address":null,"length":0,"stats":{"Line":0}},{"line":271,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":0}},{"line":273,"address":null,"length":0,"stats":{"Line":0}},{"line":274,"address":null,"length":0,"stats":{"Line":0}},{"line":275,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":277,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":279,"address":null,"length":0,"stats":{"Line":0}},{"line":280,"address":null,"length":0,"stats":{"Line":0}},{"line":281,"address":null,"length":0,"stats":{"Line":0}},{"line":282,"address":null,"length":0,"stats":{"Line":0}},{"line":283,"address":null,"length":0,"stats":{"Line":0}},{"line":285,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":null,"length":0,"stats":{"Line":0}},{"line":290,"address":null,"length":0,"stats":{"Line":0}},{"line":291,"address":null,"length":0,"stats":{"Line":0}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":0}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":303,"address":null,"length":0,"stats":{"Line":0}},{"line":304,"address":null,"length":0,"stats":{"Line":0}},{"line":305,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":316,"address":null,"length":0,"stats":{"Line":0}},{"line":328,"address":null,"length":0,"stats":{"Line":0}},{"line":331,"address":null,"length":0,"stats":{"Line":0}},{"line":332,"address":null,"length":0,"stats":{"Line":0}},{"line":333,"address":null,"length":0,"stats":{"Line":0}},{"line":334,"address":null,"length":0,"stats":{"Line":0}},{"line":336,"address":null,"length":0,"stats":{"Line":0}},{"line":339,"address":null,"length":0,"stats":{"Line":0}},{"line":340,"address":null,"length":0,"stats":{"Line":0}},{"line":341,"address":null,"length":0,"stats":{"Line":0}},{"line":342,"address":null,"length":0,"stats":{"Line":0}},{"line":343,"address":null,"length":0,"stats":{"Line":0}},{"line":345,"address":null,"length":0,"stats":{"Line":0}},{"line":346,"address":null,"length":0,"stats":{"Line":0}},{"line":347,"address":null,"length":0,"stats":{"Line":0}},{"line":350,"address":null,"length":0,"stats":{"Line":0}},{"line":351,"address":null,"length":0,"stats":{"Line":0}},{"line":352,"address":null,"length":0,"stats":{"Line":0}},{"line":353,"address":null,"length":0,"stats":{"Line":0}},{"line":359,"address":null,"length":0,"stats":{"Line":0}},{"line":360,"address":null,"length":0,"stats":{"Line":0}},{"line":363,"address":null,"length":0,"stats":{"Line":0}},{"line":367,"address":null,"length":0,"stats":{"Line":0}},{"line":370,"address":null,"length":0,"stats":{"Line":0}},{"line":371,"address":null,"length":0,"stats":{"Line":0}},{"line":372,"address":null,"length":0,"stats":{"Line":0}},{"line":373,"address":null,"length":0,"stats":{"Line":0}},{"line":376,"address":null,"length":0,"stats":{"Line":0}},{"line":379,"address":null,"length":0,"stats":{"Line":0}},{"line":380,"address":null,"length":0,"stats":{"Line":0}},{"line":381,"address":null,"length":0,"stats":{"Line":0}},{"line":382,"address":null,"length":0,"stats":{"Line":0}},{"line":383,"address":null,"length":0,"stats":{"Line":0}},{"line":384,"address":null,"length":0,"stats":{"Line":0}},{"line":385,"address":null,"length":0,"stats":{"Line":0}},{"line":386,"address":null,"length":0,"stats":{"Line":0}},{"line":387,"address":null,"length":0,"stats":{"Line":0}},{"line":388,"address":null,"length":0,"stats":{"Line":0}},{"line":389,"address":null,"length":0,"stats":{"Line":0}},{"line":390,"address":null,"length":0,"stats":{"Line":0}},{"line":391,"address":null,"length":0,"stats":{"Line":0}},{"line":394,"address":null,"length":0,"stats":{"Line":0}},{"line":396,"address":null,"length":0,"stats":{"Line":0}},{"line":397,"address":null,"length":0,"stats":{"Line":0}},{"line":398,"address":null,"length":0,"stats":{"Line":0}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":400,"address":null,"length":0,"stats":{"Line":0}},{"line":401,"address":null,"length":0,"stats":{"Line":0}},{"line":402,"address":null,"length":0,"stats":{"Line":0}},{"line":403,"address":null,"length":0,"stats":{"Line":0}},{"line":404,"address":null,"length":0,"stats":{"Line":0}},{"line":405,"address":null,"length":0,"stats":{"Line":0}},{"line":406,"address":null,"length":0,"stats":{"Line":0}},{"line":407,"address":null,"length":0,"stats":{"Line":0}},{"line":408,"address":null,"length":0,"stats":{"Line":0}},{"line":411,"address":null,"length":0,"stats":{"Line":0}},{"line":412,"address":null,"length":0,"stats":{"Line":0}},{"line":413,"address":null,"length":0,"stats":{"Line":0}},{"line":414,"address":null,"length":0,"stats":{"Line":0}},{"line":415,"address":null,"length":0,"stats":{"Line":0}},{"line":418,"address":null,"length":0,"stats":{"Line":0}},{"line":419,"address":null,"length":0,"stats":{"Line":0}},{"line":420,"address":null,"length":0,"stats":{"Line":0}},{"line":421,"address":null,"length":0,"stats":{"Line":0}},{"line":422,"address":null,"length":0,"stats":{"Line":0}},{"line":423,"address":null,"length":0,"stats":{"Line":0}},{"line":424,"address":null,"length":0,"stats":{"Line":0}},{"line":425,"address":null,"length":0,"stats":{"Line":0}},{"line":426,"address":null,"length":0,"stats":{"Line":0}},{"line":427,"address":null,"length":0,"stats":{"Line":0}},{"line":428,"address":null,"length":0,"stats":{"Line":0}},{"line":429,"address":null,"length":0,"stats":{"Line":0}},{"line":431,"address":null,"length":0,"stats":{"Line":0}},{"line":432,"address":null,"length":0,"stats":{"Line":0}},{"line":433,"address":null,"length":0,"stats":{"Line":0}},{"line":434,"address":null,"length":0,"stats":{"Line":0}},{"line":438,"address":null,"length":0,"stats":{"Line":0}},{"line":439,"address":null,"length":0,"stats":{"Line":0}},{"line":440,"address":null,"length":0,"stats":{"Line":0}},{"line":443,"address":null,"length":0,"stats":{"Line":0}},{"line":444,"address":null,"length":0,"stats":{"Line":0}},{"line":445,"address":null,"length":0,"stats":{"Line":0}},{"line":446,"address":null,"length":0,"stats":{"Line":0}},{"line":447,"address":null,"length":0,"stats":{"Line":0}},{"line":448,"address":null,"length":0,"stats":{"Line":0}},{"line":450,"address":null,"length":0,"stats":{"Line":0}},{"line":451,"address":null,"length":0,"stats":{"Line":0}},{"line":453,"address":null,"length":0,"stats":{"Line":0}},{"line":454,"address":null,"length":0,"stats":{"Line":0}},{"line":459,"address":null,"length":0,"stats":{"Line":0}},{"line":460,"address":null,"length":0,"stats":{"Line":0}},{"line":461,"address":null,"length":0,"stats":{"Line":0}},{"line":462,"address":null,"length":0,"stats":{"Line":0}},{"line":463,"address":null,"length":0,"stats":{"Line":0}},{"line":468,"address":null,"length":0,"stats":{"Line":0}},{"line":472,"address":null,"length":0,"stats":{"Line":0}},{"line":473,"address":null,"length":0,"stats":{"Line":0}},{"line":474,"address":null,"length":0,"stats":{"Line":0}},{"line":475,"address":null,"length":0,"stats":{"Line":0}},{"line":476,"address":null,"length":0,"stats":{"Line":0}},{"line":477,"address":null,"length":0,"stats":{"Line":0}},{"line":480,"address":null,"length":0,"stats":{"Line":0}},{"line":482,"address":null,"length":0,"stats":{"Line":0}},{"line":483,"address":null,"length":0,"stats":{"Line":0}},{"line":484,"address":null,"length":0,"stats":{"Line":0}},{"line":485,"address":null,"length":0,"stats":{"Line":0}},{"line":486,"address":null,"length":0,"stats":{"Line":0}},{"line":487,"address":null,"length":0,"stats":{"Line":0}},{"line":488,"address":null,"length":0,"stats":{"Line":0}},{"line":489,"address":null,"length":0,"stats":{"Line":0}},{"line":490,"address":null,"length":0,"stats":{"Line":0}},{"line":491,"address":null,"length":0,"stats":{"Line":0}},{"line":492,"address":null,"length":0,"stats":{"Line":0}},{"line":493,"address":null,"length":0,"stats":{"Line":0}},{"line":494,"address":null,"length":0,"stats":{"Line":0}},{"line":496,"address":null,"length":0,"stats":{"Line":0}},{"line":497,"address":null,"length":0,"stats":{"Line":0}},{"line":498,"address":null,"length":0,"stats":{"Line":0}},{"line":499,"address":null,"length":0,"stats":{"Line":0}},{"line":500,"address":null,"length":0,"stats":{"Line":0}},{"line":503,"address":null,"length":0,"stats":{"Line":0}},{"line":504,"address":null,"length":0,"stats":{"Line":0}},{"line":505,"address":null,"length":0,"stats":{"Line":0}},{"line":506,"address":null,"length":0,"stats":{"Line":0}},{"line":507,"address":null,"length":0,"stats":{"Line":0}},{"line":508,"address":null,"length":0,"stats":{"Line":0}},{"line":509,"address":null,"length":0,"stats":{"Line":0}},{"line":510,"address":null,"length":0,"stats":{"Line":0}},{"line":511,"address":null,"length":0,"stats":{"Line":0}},{"line":512,"address":null,"length":0,"stats":{"Line":0}},{"line":513,"address":null,"length":0,"stats":{"Line":0}},{"line":514,"address":null,"length":0,"stats":{"Line":0}},{"line":515,"address":null,"length":0,"stats":{"Line":0}},{"line":517,"address":null,"length":0,"stats":{"Line":0}},{"line":518,"address":null,"length":0,"stats":{"Line":0}},{"line":519,"address":null,"length":0,"stats":{"Line":0}},{"line":520,"address":null,"length":0,"stats":{"Line":0}},{"line":521,"address":null,"length":0,"stats":{"Line":0}},{"line":522,"address":null,"length":0,"stats":{"Line":0}},{"line":523,"address":null,"length":0,"stats":{"Line":0}},{"line":525,"address":null,"length":0,"stats":{"Line":0}},{"line":528,"address":null,"length":0,"stats":{"Line":0}},{"line":529,"address":null,"length":0,"stats":{"Line":0}},{"line":530,"address":null,"length":0,"stats":{"Line":0}},{"line":533,"address":null,"length":0,"stats":{"Line":0}},{"line":534,"address":null,"length":0,"stats":{"Line":0}},{"line":537,"address":null,"length":0,"stats":{"Line":0}},{"line":539,"address":null,"length":0,"stats":{"Line":0}},{"line":540,"address":null,"length":0,"stats":{"Line":0}},{"line":541,"address":null,"length":0,"stats":{"Line":0}},{"line":543,"address":null,"length":0,"stats":{"Line":0}},{"line":544,"address":null,"length":0,"stats":{"Line":0}},{"line":545,"address":null,"length":0,"stats":{"Line":0}},{"line":546,"address":null,"length":0,"stats":{"Line":0}},{"line":550,"address":null,"length":0,"stats":{"Line":0}},{"line":564,"address":4216336,"length":1,"stats":{"Line":0}},{"line":570,"address":4216356,"length":1,"stats":{"Line":0}},{"line":571,"address":4216372,"length":1,"stats":{"Line":0}},{"line":572,"address":4216938,"length":1,"stats":{"Line":0}},{"line":577,"address":4212432,"length":1,"stats":{"Line":0}},{"line":582,"address":4212458,"length":1,"stats":{"Line":0}},{"line":584,"address":4212581,"length":1,"stats":{"Line":0}},{"line":585,"address":4213089,"length":1,"stats":{"Line":0}},{"line":586,"address":4212893,"length":1,"stats":{"Line":0}},{"line":587,"address":4213240,"length":1,"stats":{"Line":0}},{"line":592,"address":4449936,"length":1,"stats":{"Line":0}},{"line":594,"address":4449954,"length":1,"stats":{"Line":0}},{"line":599,"address":null,"length":0,"stats":{"Line":0}},{"line":600,"address":null,"length":0,"stats":{"Line":0}},{"line":601,"address":null,"length":0,"stats":{"Line":0}},{"line":603,"address":null,"length":0,"stats":{"Line":0}},{"line":605,"address":null,"length":0,"stats":{"Line":0}},{"line":606,"address":null,"length":0,"stats":{"Line":0}},{"line":612,"address":null,"length":0,"stats":{"Line":0}},{"line":613,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":295},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","logger.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Dunitrust core logger\n\nuse crate::commands::DursCoreOptions;\nuse failure::Fail;\nuse log::{Level, SetLoggerError};\nuse simplelog::{CombinedLogger, Config, TermLogger, WriteLogger};\nuse std::fs::{File, OpenOptions};\nuse std::path::PathBuf;\n\n#[derive(Debug, Fail)]\npub enum InitLoggerError {\n #[fail(display = \"Fail to create log file: {}\", _0)]\n FailCreateLogFile(std::io::Error),\n #[fail(display = \"Fail to create term logger\")]\n FailCreateTermLogger,\n #[fail(display = \"Fail to open log file: {}\", _0)]\n FailOpenLogFile(std::io::Error),\n #[fail(display = \"Invalid log file path\")]\n LogFilePathNotStr,\n #[fail(display = \"Set logger error: {}\", _0)]\n SetLoggerError(SetLoggerError),\n}\n\nimpl From\u003cSetLoggerError\u003e for InitLoggerError {\n fn from(e: SetLoggerError) -\u003e Self {\n InitLoggerError::SetLoggerError(e)\n }\n}\n\n/// Initialize logger\n/// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !\npub fn init(\n profile_path: PathBuf,\n soft_name: \u0026'static str,\n soft_version: \u0026'static str,\n durs_core_opts: \u0026DursCoreOptions,\n) -\u003e Result\u003c(), InitLoggerError\u003e {\n let mut log_file_path = profile_path;\n\n // Get log_file_path\n log_file_path.push(format!(\"{}.log\", soft_name));\n\n // Get log_file_path_str\n let log_file_path_str = log_file_path\n .to_str()\n .ok_or(InitLoggerError::LogFilePathNotStr)?;\n\n // Create log file if not exist\n if !log_file_path.as_path().exists() {\n File::create(log_file_path_str).map_err(InitLoggerError::FailCreateLogFile)?;\n }\n\n // Open log file\n let file_logger_opts = OpenOptions::new()\n .write(true)\n .append(true)\n .open(log_file_path_str)\n .map_err(InitLoggerError::FailOpenLogFile)?;\n\n // Get log level filter\n let logs_level_filter = durs_core_opts\n .logs_level\n .unwrap_or(Level::Info)\n .to_level_filter();\n\n // Config logger\n let logger_config = Config {\n time: Some(Level::Error),\n level: Some(Level::Error),\n target: Some(Level::Debug),\n location: Some(Level::Debug),\n time_format: Some(\"%Y-%m-%d %H:%M:%S%:z\"),\n };\n\n if durs_core_opts.log_stdout {\n CombinedLogger::init(vec![\n TermLogger::new(logs_level_filter, logger_config)\n .ok_or(InitLoggerError::FailCreateTermLogger)?,\n WriteLogger::new(logs_level_filter, logger_config, file_logger_opts),\n ])?;\n } else {\n WriteLogger::init(logs_level_filter, logger_config, file_logger_opts)?;\n }\n\n info!(\n \"Launching {}\",\n crate::get_software_infos(soft_name, soft_version)\n );\n info!(\"Successfully init logger\");\n Ok(())\n}\n","traces":[{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":4460528,"length":1,"stats":{"Line":0}},{"line":53,"address":4460578,"length":1,"stats":{"Line":0}},{"line":56,"address":4460649,"length":1,"stats":{"Line":0}},{"line":59,"address":4460895,"length":1,"stats":{"Line":0}},{"line":61,"address":4460973,"length":1,"stats":{"Line":0}},{"line":64,"address":4461360,"length":1,"stats":{"Line":0}},{"line":65,"address":4461447,"length":1,"stats":{"Line":0}},{"line":69,"address":4461836,"length":1,"stats":{"Line":0}},{"line":72,"address":4461919,"length":1,"stats":{"Line":0}},{"line":73,"address":4462008,"length":1,"stats":{"Line":0}},{"line":76,"address":4462309,"length":1,"stats":{"Line":0}},{"line":78,"address":4464767,"length":1,"stats":{"Line":0}},{"line":82,"address":4462495,"length":1,"stats":{"Line":0}},{"line":83,"address":4462363,"length":1,"stats":{"Line":0}},{"line":84,"address":4462391,"length":1,"stats":{"Line":0}},{"line":85,"address":4462419,"length":1,"stats":{"Line":0}},{"line":86,"address":4462447,"length":1,"stats":{"Line":0}},{"line":87,"address":4462475,"length":1,"stats":{"Line":0}},{"line":90,"address":4462591,"length":1,"stats":{"Line":0}},{"line":91,"address":4462619,"length":1,"stats":{"Line":0}},{"line":92,"address":4462632,"length":1,"stats":{"Line":0}},{"line":93,"address":4462715,"length":1,"stats":{"Line":0}},{"line":94,"address":4463119,"length":1,"stats":{"Line":0}},{"line":97,"address":4463513,"length":1,"stats":{"Line":0}},{"line":100,"address":4463729,"length":1,"stats":{"Line":0}},{"line":102,"address":4463920,"length":1,"stats":{"Line":0}},{"line":104,"address":4464175,"length":1,"stats":{"Line":0}},{"line":105,"address":4464449,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":30},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","core","src","router.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Relay messages between durs modules.\n\nuse durs_common_tools::fatal_error;\nuse durs_conf::DuRsConf;\nuse durs_message::*;\nuse durs_module::*;\nuse durs_network_documents::network_endpoint::{ApiPart, EndpointEnum};\nuse std::collections::HashMap;\nuse std::path::PathBuf;\nuse std::sync::mpsc;\nuse std::sync::mpsc::RecvTimeoutError;\nuse std::thread;\nuse std::time::Duration;\nuse std::time::SystemTime;\n\nstatic MAX_REGISTRATION_DELAY: \u0026'static u64 = \u002620;\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\nenum DursMsgReceiver {\n Role(ModuleRole),\n Event(ModuleEvent),\n One(ModuleStaticName),\n}\n\n/// Start broadcasting thread\nfn start_broadcasting_thread(\n start_time: SystemTime,\n receiver: \u0026mpsc::Receiver\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n _external_followers: \u0026[mpsc::Sender\u003cDursMsg\u003e],\n) {\n // Define variables\n let mut modules_senders: HashMap\u003cModuleStaticName, mpsc::Sender\u003cDursMsg\u003e\u003e = HashMap::new();\n let mut pool_msgs: HashMap\u003cDursMsgReceiver, Vec\u003cDursMsg\u003e\u003e = HashMap::new();\n let mut events_subscriptions: HashMap\u003cModuleEvent, Vec\u003cModuleStaticName\u003e\u003e = HashMap::new();\n let mut roles: HashMap\u003cModuleRole, Vec\u003cModuleStaticName\u003e\u003e = HashMap::new();\n let mut registrations_count = 0;\n let mut expected_registrations_count = None;\n let mut local_node_endpoints: Vec\u003cEndpointEnum\u003e = Vec::new();\n let mut reserved_apis_parts: HashMap\u003cModuleStaticName, Vec\u003cApiPart\u003e\u003e = HashMap::new();\n\n loop {\n match receiver.recv_timeout(Duration::from_secs(1)) {\n Ok(mess) =\u003e {\n match mess {\n RouterThreadMessage::ModulesCount(modules_count) =\u003e {\n expected_registrations_count = Some(modules_count)\n }\n RouterThreadMessage::ModuleRegistration {\n static_name: module_static_name,\n sender: module_sender,\n roles: module_roles,\n events_subscription,\n reserved_apis_parts: module_reserved_apis_parts,\n endpoints: mut module_endpoints,\n } =\u003e {\n registrations_count += 1;\n // For all events\n for event in events_subscription {\n // Send pending message of this event\n for msg in pool_msgs\n .get(\u0026DursMsgReceiver::Event(event))\n .unwrap_or(\u0026Vec::with_capacity(0))\n {\n module_sender.send(msg.clone()).unwrap_or_else(|_| {\n fatal_error!(\n \"fail to relay DursMsg to {:?} !\",\n module_static_name\n )\n });\n }\n // Store event subscription\n events_subscriptions\n .entry(event)\n .or_insert_with(Vec::new)\n .push(module_static_name);\n }\n // For all roles\n for role in module_roles {\n // Send pending message for this role\n for msg in pool_msgs\n .get(\u0026DursMsgReceiver::Role(role))\n .unwrap_or(\u0026Vec::with_capacity(0))\n {\n module_sender.send(msg.clone()).unwrap_or_else(|_| {\n fatal_error!(\n \"fail to relay DursMsg to {:?} !\",\n module_static_name\n )\n });\n }\n // Store sender roles\n roles\n .entry(role)\n .or_insert_with(Vec::new)\n .push(module_static_name);\n }\n // For all reserved apis parts\n for other_module_reserved_apis_parts in reserved_apis_parts.values() {\n for other_api_part in other_module_reserved_apis_parts {\n for api_part in \u0026module_reserved_apis_parts {\n if api_part.union_exist(other_api_part) {\n fatal_error!(\n \"two modules try to reserve same api name '{}' with at least 1 version in common '({:?}; {:?})' !\",\n api_part.name.0,\n api_part.versions,\n other_api_part.versions,\n );\n }\n }\n }\n }\n // For all endpoints\n for ep in \u0026module_endpoints {\n let ep_api = ep.api();\n let ep_version = ep.version();\n\n if module_reserved_apis_parts\n .iter()\n .filter(|api_part| api_part.contains(\u0026ep_api, ep_version))\n .count()\n == 0\n {\n fatal_error!(\n \"Module {} try to declare endpoint with undeclared api part (name: '{}', version: '{}') !\",\n module_static_name.0,\n ep_api.0,\n ep_version.0,\n );\n }\n /*for other_module_ep in \u0026local_node_endpoints {\n if ep_api == other_module_ep.api() \u0026\u0026 ep_version == other_module_ep.version() {\n fatal_error!(\n \"two modules try to declare endpoint of same api '{}' and same version '{}' !\",\n ep_api.0,\n ep_version.0,\n );\n }\n }*/\n }\n // Store reserved APIs parts\n reserved_apis_parts.insert(module_static_name, module_reserved_apis_parts);\n // Add module endpoints to local node endpoints\n local_node_endpoints.append(\u0026mut module_endpoints);\n\n // If all modules registered\n if expected_registrations_count.is_some()\n \u0026\u0026 registrations_count == expected_registrations_count.unwrap()\n {\n // Get list of InterNodesNetwork modules\n let receivers = roles\n .get(\u0026ModuleRole::InterNodesNetwork)\n .expect(\"Fatal error : no module with role InterNodesNetwork !\")\n .to_vec();\n // Send endpoints to network module\n send_msg_to_several_receivers(\n DursMsg::ModulesEndpoints(local_node_endpoints.clone()),\n \u0026receivers,\n \u0026modules_senders,\n );\n }\n // Add this sender to modules_senders\n modules_senders.insert(module_static_name, module_sender);\n }\n RouterThreadMessage::ModuleMessage(msg) =\u003e match msg {\n DursMsg::Stop =\u003e break,\n DursMsg::Event {\n event_from,\n event_type,\n ..\n } =\u003e {\n // the node to be started less than MAX_REGISTRATION_DELAY seconds ago,\n // keep the message in memory to be able to send it back to modules not yet plugged\n store_msg_in_pool(start_time, \u0026msg, \u0026mut pool_msgs);\n // Get list of receivers\n let receivers = events_subscriptions\n .get(\u0026event_type)\n .unwrap_or(\u0026Vec::with_capacity(0))\n .iter()\n .filter(|module_static_name| **module_static_name != event_from)\n .cloned()\n .collect::\u003cVec\u003cModuleStaticName\u003e\u003e();\n // Send msg to receivers\n send_msg_to_several_receivers(msg, \u0026receivers, \u0026modules_senders)\n }\n DursMsg::Request { req_to: role, .. } =\u003e {\n // If the node to be started less than MAX_REGISTRATION_DELAY seconds ago,\n // keep the message in memory to be able to send it back to modules not yet plugged\n store_msg_in_pool(start_time, \u0026msg, \u0026mut pool_msgs);\n // Get list of receivers\n let receivers =\n roles.get(\u0026role).unwrap_or(\u0026Vec::with_capacity(0)).to_vec();\n // Send msg to receivers\n send_msg_to_several_receivers(msg, \u0026receivers, \u0026modules_senders)\n }\n _ =\u003e {} // Others DursMsg variants\n },\n }\n }\n Err(e) =\u003e match e {\n RecvTimeoutError::Timeout =\u003e continue,\n RecvTimeoutError::Disconnected =\u003e fatal_error!(\"router thread disconnnected !\"),\n },\n }\n if (expected_registrations_count.is_none()\n || registrations_count \u003c expected_registrations_count.unwrap())\n \u0026\u0026 SystemTime::now()\n .duration_since(start_time)\n .expect(\"Duration error !\")\n .as_secs()\n \u003e *MAX_REGISTRATION_DELAY\n {\n fatal_error!(\n \"{} modules have registered, but expected {} !\",\n registrations_count,\n expected_registrations_count.unwrap_or(0)\n );\n }\n }\n}\n\n/// Start conf thread\nfn start_conf_thread(\n profile_path: PathBuf,\n mut conf: DuRsConf,\n receiver: \u0026mpsc::Receiver\u003cDursMsg\u003e,\n) {\n let conf_path = durs_conf::get_conf_path(\u0026profile_path);\n loop {\n match receiver.recv() {\n Ok(msg) =\u003e {\n if let DursMsg::SaveNewModuleConf(module_static_name, new_json_conf) = msg {\n conf.set_module_conf(ModuleName(module_static_name.to_string()), new_json_conf);\n durs_conf::write_conf_file(\u0026conf_path, \u0026conf)\n .expect(\"Fail to write new module conf in conf file ! \");\n }\n }\n Err(_) =\u003e {\n info!(\"Conf thread stops.\");\n break;\n }\n }\n }\n}\n\n/// Send msg to several receivers\nfn send_msg_to_several_receivers(\n msg: DursMsg,\n receivers: \u0026[ModuleStaticName],\n modules_senders: \u0026HashMap\u003cModuleStaticName, mpsc::Sender\u003cDursMsg\u003e\u003e,\n) {\n if !receivers.is_empty() {\n // Send message by copy To all modules that subscribed to this event\n for module_static_name in \u0026receivers[1..] {\n if let Some(module_sender) = modules_senders.get(module_static_name) {\n module_sender.send(msg.clone()).unwrap_or_else(|_| {\n fatal_error!(\"fail to relay DursMsg to {:?} !\", module_static_name)\n });\n }\n }\n // Send message by move to the last module to be receive\n if let Some(module_sender) = modules_senders.get(\u0026receivers[0]) {\n module_sender\n .send(msg)\n .unwrap_or_else(|_| fatal_error!(\"Fail to relay DursMsg to {:?} !\", receivers[0]));\n }\n }\n}\n\n/// If the node to be started less than MAX_REGISTRATION_DELAY seconds ago,\n/// keep the message in memory to be able to send it back to modules not yet plugged\nfn store_msg_in_pool(\n start_time: SystemTime,\n msg: \u0026DursMsg,\n pool_msgs: \u0026mut HashMap\u003cDursMsgReceiver, Vec\u003cDursMsg\u003e\u003e,\n) {\n if SystemTime::now()\n .duration_since(start_time)\n .expect(\"Duration error !\")\n .as_secs()\n \u003c *MAX_REGISTRATION_DELAY\n {\n let msg_recv = match msg {\n DursMsg::Event { event_type, .. } =\u003e Some(DursMsgReceiver::Event(*event_type)),\n DursMsg::Request { req_to, .. } =\u003e Some(DursMsgReceiver::Role(*req_to)),\n DursMsg::Response { res_to, .. } =\u003e Some(DursMsgReceiver::One(*res_to)),\n _ =\u003e None,\n };\n if let Some(msg_recv) = msg_recv {\n pool_msgs\n .entry(msg_recv)\n .or_insert_with(Vec::new)\n .push(msg.clone());\n }\n } else if !pool_msgs.is_empty() {\n // Clear pool_msgs\n pool_msgs.clear();\n }\n}\n\n/// Start router thread\npub fn start_router(\n run_duration_in_secs: u64,\n profile_path: PathBuf,\n conf: DuRsConf,\n external_followers: Vec\u003cmpsc::Sender\u003cDursMsg\u003e\u003e,\n) -\u003e mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e {\n let start_time = SystemTime::now();\n\n // Create router channel\n let (router_sender, router_receiver): (\n mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n mpsc::Receiver\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) = mpsc::channel();\n\n // Create router thread\n thread::spawn(move || {\n // Create broadcasting thread channel\n let (broadcasting_sender, broadcasting_receiver): (\n mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n mpsc::Receiver\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) = mpsc::channel();\n\n // Create broadcasting thread\n thread::spawn(move || {\n start_broadcasting_thread(start_time, \u0026broadcasting_receiver, \u0026external_followers);\n });\n\n // Create conf thread channel\n let (conf_sender, conf_receiver): (mpsc::Sender\u003cDursMsg\u003e, mpsc::Receiver\u003cDursMsg\u003e) =\n mpsc::channel();\n\n // Create conf thread\n thread::spawn(move || {\n start_conf_thread(profile_path.clone(), conf, \u0026conf_receiver);\n });\n\n // Define variables\n let mut modules_senders: HashMap\u003cModuleStaticName, mpsc::Sender\u003cDursMsg\u003e\u003e = HashMap::new();\n let mut pool_msgs: HashMap\u003cModuleStaticName, Vec\u003cDursMsg\u003e\u003e = HashMap::new();\n\n // Wait to receiver modules senders\n loop {\n match router_receiver.recv_timeout(Duration::from_secs(1)) {\n Ok(mess) =\u003e {\n match mess {\n RouterThreadMessage::ModulesCount(expected_registrations_count) =\u003e {\n // Relay to broadcasting thread\n broadcasting_sender\n .send(RouterThreadMessage::ModulesCount(\n expected_registrations_count,\n ))\n .expect(\n \"Fail to relay ModulesCount message to broadcasting thread !\",\n );\n }\n RouterThreadMessage::ModuleRegistration {\n static_name: module_static_name,\n sender: module_sender,\n events_subscription,\n roles,\n reserved_apis_parts,\n endpoints,\n } =\u003e {\n // Send pending messages destined specifically to this module\n if let Some(msgs) = pool_msgs.remove(\u0026module_static_name) {\n for msg in msgs {\n module_sender.send(msg).unwrap_or_else(|_| {\n fatal_error!(\n \"Fail to relay DursMsg to {:?} !\",\n module_static_name\n )\n });\n }\n }\n // Add this sender to modules_senders\n modules_senders.insert(module_static_name, module_sender.clone());\n // Relay to broadcasting thread\n broadcasting_sender\n .send(RouterThreadMessage::ModuleRegistration {\n static_name: module_static_name,\n sender: module_sender,\n events_subscription,\n roles,\n reserved_apis_parts,\n endpoints,\n })\n .expect(\n \"Fail to relay module registration to broadcasting thread !\",\n );\n // Log the number of modules_senders received\n info!(\n \"Router thread receive '{}' module registration ({} modules registered).\",\n module_static_name.0,\n modules_senders.len()\n );\n }\n RouterThreadMessage::ModuleMessage(msg) =\u003e {\n trace!(\"Router thread receive ModuleMessage({:?})\", msg);\n match msg {\n DursMsg::Stop =\u003e {\n info!(\"TMP: Router: RECEIVE STOP MESSAGE !\");\n // Relay stop signal to broadcasting thread\n broadcasting_sender\n .send(RouterThreadMessage::ModuleMessage(msg))\n .expect(\"Fail to relay message to broadcasting thread !\");\n // Relay stop message to all modules\n for module_sender in modules_senders.values() {\n if module_sender.send(DursMsg::Stop).is_err() {\n warn!(\"Fail to relay stop to modules !\");\n }\n }\n break;\n }\n DursMsg::SaveNewModuleConf(_, _) =\u003e {\n // Forward it to the conf thread\n conf_sender\n .send(msg)\n .expect(\"Fail to reach conf thread !\");\n }\n DursMsg::Request{ .. } =\u003e {\n broadcasting_sender\n .send(RouterThreadMessage::ModuleMessage(msg))\n .expect(\n \"Fail to relay specific role message to broadcasting thread !\",\n );\n }\n DursMsg::Event{ .. } =\u003e broadcasting_sender\n .send(RouterThreadMessage::ModuleMessage(msg))\n .expect(\"Fail to relay specific event message to broadcasting thread !\"),\n DursMsg::Response {\n res_to: module_static_name,\n ..\n } =\u003e {\n if let Some(module_sender) =\n modules_senders.get(\u0026module_static_name)\n {\n module_sender.send(msg).unwrap_or_else(|_| {\n fatal_error!(\n \"Fail to relay DursMsg to {:?} !\",\n module_static_name\n )\n });\n } else if SystemTime::now()\n .duration_since(start_time)\n .expect(\"Duration error !\")\n .as_secs()\n \u003c *MAX_REGISTRATION_DELAY\n {\n pool_msgs\n .entry(module_static_name)\n .or_insert_with(Vec::new)\n .push(msg);\n } else {\n if !pool_msgs.is_empty() {\n pool_msgs = HashMap::with_capacity(0);\n }\n warn!(\n \"Message for unknow receiver : {:?}.\",\n module_static_name\n );\n }\n }\n DursMsg::ModulesEndpoints(_) =\u003e {\n warn!(\"A module try to send reserved router message: ModulesEndpoints.\");\n }\n }\n }\n }\n }\n Err(e) =\u003e {\n if let RecvTimeoutError::Disconnected = e {\n warn!(\"Router thread disconnnected... break router main loop.\");\n break;\n }\n }\n }\n if run_duration_in_secs \u003e 0\n \u0026\u0026 SystemTime::now()\n .duration_since(start_time)\n .expect(\"Duration error !\")\n .as_secs()\n \u003e run_duration_in_secs\n {\n broadcasting_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Stop))\n .expect(\"Fail to relay stop message to broadcasting thread !\");\n break;\n }\n }\n info!(\"Router thread stop.\")\n });\n\n router_sender\n}\n","traces":[{"line":41,"address":4261200,"length":1,"stats":{"Line":0}},{"line":47,"address":4261253,"length":1,"stats":{"Line":0}},{"line":48,"address":4261443,"length":1,"stats":{"Line":0}},{"line":49,"address":4261458,"length":1,"stats":{"Line":0}},{"line":50,"address":4261488,"length":1,"stats":{"Line":0}},{"line":51,"address":4261510,"length":1,"stats":{"Line":0}},{"line":52,"address":4261522,"length":1,"stats":{"Line":0}},{"line":53,"address":4261542,"length":1,"stats":{"Line":0}},{"line":54,"address":4261572,"length":1,"stats":{"Line":0}},{"line":56,"address":4275131,"length":1,"stats":{"Line":0}},{"line":57,"address":4261612,"length":1,"stats":{"Line":0}},{"line":58,"address":4261716,"length":1,"stats":{"Line":0}},{"line":59,"address":4262013,"length":1,"stats":{"Line":0}},{"line":60,"address":4261883,"length":1,"stats":{"Line":0}},{"line":61,"address":4261985,"length":1,"stats":{"Line":0}},{"line":64,"address":4262018,"length":1,"stats":{"Line":0}},{"line":65,"address":4262050,"length":1,"stats":{"Line":0}},{"line":66,"address":4262098,"length":1,"stats":{"Line":0}},{"line":67,"address":4262162,"length":1,"stats":{"Line":0}},{"line":68,"address":4262226,"length":1,"stats":{"Line":0}},{"line":69,"address":4262290,"length":1,"stats":{"Line":0}},{"line":71,"address":4262354,"length":1,"stats":{"Line":0}},{"line":73,"address":4262402,"length":1,"stats":{"Line":0}},{"line":75,"address":4262840,"length":1,"stats":{"Line":0}},{"line":76,"address":4262802,"length":1,"stats":{"Line":0}},{"line":77,"address":4262885,"length":1,"stats":{"Line":0}},{"line":79,"address":4263150,"length":1,"stats":{"Line":0}},{"line":80,"address":4372031,"length":1,"stats":{"Line":0}},{"line":82,"address":4372309,"length":1,"stats":{"Line":0}},{"line":87,"address":4263263,"length":1,"stats":{"Line":0}},{"line":90,"address":4263317,"length":1,"stats":{"Line":0}},{"line":93,"address":4263353,"length":1,"stats":{"Line":0}},{"line":95,"address":4263679,"length":1,"stats":{"Line":0}},{"line":96,"address":4263641,"length":1,"stats":{"Line":0}},{"line":97,"address":4263724,"length":1,"stats":{"Line":0}},{"line":99,"address":4263989,"length":1,"stats":{"Line":0}},{"line":100,"address":4373567,"length":1,"stats":{"Line":0}},{"line":102,"address":4373845,"length":1,"stats":{"Line":0}},{"line":107,"address":4264102,"length":1,"stats":{"Line":0}},{"line":110,"address":4264156,"length":1,"stats":{"Line":0}},{"line":113,"address":4264208,"length":1,"stats":{"Line":0}},{"line":114,"address":4264416,"length":1,"stats":{"Line":0}},{"line":115,"address":4264667,"length":1,"stats":{"Line":0}},{"line":116,"address":4264873,"length":1,"stats":{"Line":0}},{"line":117,"address":4264928,"length":1,"stats":{"Line":0}},{"line":119,"address":4265143,"length":1,"stats":{"Line":0}},{"line":120,"address":4265151,"length":1,"stats":{"Line":0}},{"line":121,"address":4265158,"length":1,"stats":{"Line":0}},{"line":128,"address":4264455,"length":1,"stats":{"Line":0}},{"line":129,"address":4267140,"length":1,"stats":{"Line":0}},{"line":130,"address":4267257,"length":1,"stats":{"Line":0}},{"line":132,"address":4267308,"length":1,"stats":{"Line":0}},{"line":134,"address":4267396,"length":1,"stats":{"Line":0}},{"line":138,"address":4267508,"length":1,"stats":{"Line":0}},{"line":156,"address":4267167,"length":1,"stats":{"Line":0}},{"line":158,"address":4269594,"length":1,"stats":{"Line":0}},{"line":161,"address":4269601,"length":1,"stats":{"Line":0}},{"line":162,"address":4269637,"length":1,"stats":{"Line":0}},{"line":165,"address":4269750,"length":1,"stats":{"Line":0}},{"line":171,"address":4269877,"length":1,"stats":{"Line":0}},{"line":172,"address":4269960,"length":1,"stats":{"Line":0}},{"line":177,"address":4270089,"length":1,"stats":{"Line":0}},{"line":179,"address":4270212,"length":1,"stats":{"Line":0}},{"line":180,"address":4270258,"length":1,"stats":{"Line":0}},{"line":182,"address":4270511,"length":1,"stats":{"Line":0}},{"line":183,"address":4270543,"length":1,"stats":{"Line":0}},{"line":188,"address":4270557,"length":1,"stats":{"Line":0}},{"line":190,"address":4270612,"length":1,"stats":{"Line":0}},{"line":192,"address":4270639,"length":1,"stats":{"Line":0}},{"line":194,"address":4270773,"length":1,"stats":{"Line":0}},{"line":196,"address":4270708,"length":1,"stats":{"Line":0}},{"line":198,"address":4270899,"length":1,"stats":{"Line":0}},{"line":200,"address":4271059,"length":1,"stats":{"Line":0}},{"line":203,"address":4271073,"length":1,"stats":{"Line":0}},{"line":206,"address":4271128,"length":1,"stats":{"Line":0}},{"line":208,"address":4271306,"length":1,"stats":{"Line":0}},{"line":214,"address":4271600,"length":1,"stats":{"Line":0}},{"line":215,"address":4271614,"length":1,"stats":{"Line":0}},{"line":216,"address":4271775,"length":1,"stats":{"Line":0}},{"line":219,"address":4272901,"length":1,"stats":{"Line":0}},{"line":220,"address":4272970,"length":1,"stats":{"Line":0}},{"line":221,"address":4272911,"length":1,"stats":{"Line":0}},{"line":222,"address":4273130,"length":1,"stats":{"Line":0}},{"line":225,"address":4273292,"length":1,"stats":{"Line":0}},{"line":227,"address":4273328,"length":1,"stats":{"Line":0}},{"line":230,"address":4273543,"length":1,"stats":{"Line":0}},{"line":237,"address":4277824,"length":1,"stats":{"Line":0}},{"line":242,"address":4277836,"length":1,"stats":{"Line":0}},{"line":243,"address":4277924,"length":1,"stats":{"Line":0}},{"line":244,"address":4277950,"length":1,"stats":{"Line":0}},{"line":245,"address":4277994,"length":1,"stats":{"Line":0}},{"line":246,"address":4278112,"length":1,"stats":{"Line":0}},{"line":247,"address":4278214,"length":1,"stats":{"Line":0}},{"line":248,"address":4278400,"length":1,"stats":{"Line":0}},{"line":253,"address":4278526,"length":1,"stats":{"Line":0}},{"line":261,"address":4279328,"length":1,"stats":{"Line":0}},{"line":266,"address":4279359,"length":1,"stats":{"Line":0}},{"line":268,"address":4279449,"length":1,"stats":{"Line":0}},{"line":269,"address":4279684,"length":1,"stats":{"Line":0}},{"line":270,"address":4279821,"length":1,"stats":{"Line":0}},{"line":271,"address":4375247,"length":1,"stats":{"Line":0}},{"line":276,"address":4279716,"length":1,"stats":{"Line":0}},{"line":277,"address":4280020,"length":1,"stats":{"Line":0}},{"line":279,"address":4280120,"length":1,"stats":{"Line":0}},{"line":286,"address":4280288,"length":1,"stats":{"Line":0}},{"line":291,"address":4280315,"length":1,"stats":{"Line":0}},{"line":292,"address":4280337,"length":1,"stats":{"Line":0}},{"line":295,"address":4280413,"length":1,"stats":{"Line":0}},{"line":297,"address":4280589,"length":1,"stats":{"Line":0}},{"line":298,"address":4280434,"length":1,"stats":{"Line":0}},{"line":299,"address":4280594,"length":1,"stats":{"Line":0}},{"line":300,"address":4280686,"length":1,"stats":{"Line":0}},{"line":301,"address":4280792,"length":1,"stats":{"Line":0}},{"line":303,"address":4280804,"length":1,"stats":{"Line":0}},{"line":304,"address":4280879,"length":1,"stats":{"Line":0}},{"line":307,"address":4280971,"length":1,"stats":{"Line":0}},{"line":309,"address":4281011,"length":1,"stats":{"Line":0}},{"line":311,"address":4281037,"length":1,"stats":{"Line":0}},{"line":316,"address":4281072,"length":1,"stats":{"Line":0}},{"line":322,"address":4281084,"length":1,"stats":{"Line":0}},{"line":325,"address":4281255,"length":1,"stats":{"Line":0}},{"line":328,"address":4281216,"length":1,"stats":{"Line":0}},{"line":331,"address":4281312,"length":1,"stats":{"Line":0}},{"line":333,"address":4382326,"length":1,"stats":{"Line":0}},{"line":336,"address":4382077,"length":1,"stats":{"Line":0}},{"line":339,"address":4378480,"length":1,"stats":{"Line":0}},{"line":340,"address":4378484,"length":1,"stats":{"Line":0}},{"line":344,"address":4382588,"length":1,"stats":{"Line":0}},{"line":345,"address":4382581,"length":1,"stats":{"Line":0}},{"line":348,"address":4378640,"length":1,"stats":{"Line":0}},{"line":349,"address":4378647,"length":1,"stats":{"Line":0}},{"line":353,"address":4382865,"length":1,"stats":{"Line":0}},{"line":354,"address":4382880,"length":1,"stats":{"Line":0}},{"line":357,"address":4390254,"length":1,"stats":{"Line":0}},{"line":358,"address":4382957,"length":1,"stats":{"Line":0}},{"line":359,"address":4383059,"length":1,"stats":{"Line":0}},{"line":360,"address":4383451,"length":1,"stats":{"Line":0}},{"line":361,"address":4383226,"length":1,"stats":{"Line":0}},{"line":363,"address":4383360,"length":1,"stats":{"Line":0}},{"line":364,"address":4383340,"length":1,"stats":{"Line":0}},{"line":365,"address":4383332,"length":1,"stats":{"Line":0}},{"line":372,"address":4383456,"length":1,"stats":{"Line":0}},{"line":373,"address":4383488,"length":1,"stats":{"Line":0}},{"line":374,"address":4383536,"length":1,"stats":{"Line":0}},{"line":375,"address":4383584,"length":1,"stats":{"Line":0}},{"line":376,"address":4383632,"length":1,"stats":{"Line":0}},{"line":377,"address":4383680,"length":1,"stats":{"Line":0}},{"line":380,"address":4383752,"length":1,"stats":{"Line":0}},{"line":381,"address":4383907,"length":1,"stats":{"Line":0}},{"line":382,"address":4378992,"length":1,"stats":{"Line":0}},{"line":383,"address":4379007,"length":1,"stats":{"Line":0}},{"line":385,"address":4379285,"length":1,"stats":{"Line":0}},{"line":391,"address":4384458,"length":1,"stats":{"Line":0}},{"line":393,"address":4384964,"length":1,"stats":{"Line":0}},{"line":394,"address":4384792,"length":1,"stats":{"Line":0}},{"line":395,"address":4384592,"length":1,"stats":{"Line":0}},{"line":396,"address":4384608,"length":1,"stats":{"Line":0}},{"line":397,"address":4384632,"length":1,"stats":{"Line":0}},{"line":398,"address":4384672,"length":1,"stats":{"Line":0}},{"line":399,"address":4384712,"length":1,"stats":{"Line":0}},{"line":400,"address":4384752,"length":1,"stats":{"Line":0}},{"line":406,"address":4385026,"length":1,"stats":{"Line":0}},{"line":409,"address":4385265,"length":1,"stats":{"Line":0}},{"line":412,"address":4385622,"length":1,"stats":{"Line":0}},{"line":413,"address":4385668,"length":1,"stats":{"Line":0}},{"line":414,"address":4387476,"length":1,"stats":{"Line":0}},{"line":415,"address":4386070,"length":1,"stats":{"Line":0}},{"line":416,"address":4386133,"length":1,"stats":{"Line":0}},{"line":418,"address":4386542,"length":1,"stats":{"Line":0}},{"line":419,"address":4386423,"length":1,"stats":{"Line":0}},{"line":422,"address":4386628,"length":1,"stats":{"Line":0}},{"line":423,"address":4386836,"length":1,"stats":{"Line":0}},{"line":424,"address":4387064,"length":1,"stats":{"Line":0}},{"line":427,"address":4391842,"length":1,"stats":{"Line":0}},{"line":431,"address":4387442,"length":1,"stats":{"Line":0}},{"line":432,"address":4387361,"length":1,"stats":{"Line":0}},{"line":436,"address":4387600,"length":1,"stats":{"Line":0}},{"line":437,"address":4387481,"length":1,"stats":{"Line":0}},{"line":442,"address":4387794,"length":1,"stats":{"Line":0}},{"line":443,"address":4387675,"length":1,"stats":{"Line":0}},{"line":446,"address":4387869,"length":1,"stats":{"Line":0}},{"line":449,"address":4387952,"length":1,"stats":{"Line":0}},{"line":450,"address":4387917,"length":1,"stats":{"Line":0}},{"line":452,"address":4380528,"length":1,"stats":{"Line":0}},{"line":453,"address":4380543,"length":1,"stats":{"Line":0}},{"line":455,"address":4380821,"length":1,"stats":{"Line":0}},{"line":458,"address":4388163,"length":1,"stats":{"Line":0}},{"line":459,"address":4388230,"length":1,"stats":{"Line":0}},{"line":462,"address":4388391,"length":1,"stats":{"Line":0}},{"line":464,"address":4388447,"length":1,"stats":{"Line":0}},{"line":465,"address":4388415,"length":1,"stats":{"Line":0}},{"line":467,"address":4388477,"length":1,"stats":{"Line":0}},{"line":469,"address":4388570,"length":1,"stats":{"Line":0}},{"line":470,"address":4388611,"length":1,"stats":{"Line":0}},{"line":472,"address":4388636,"length":1,"stats":{"Line":0}},{"line":479,"address":4389039,"length":1,"stats":{"Line":0}},{"line":485,"address":4389389,"length":1,"stats":{"Line":0}},{"line":486,"address":4389405,"length":1,"stats":{"Line":0}},{"line":487,"address":4389429,"length":1,"stats":{"Line":0}},{"line":492,"address":4389815,"length":1,"stats":{"Line":0}},{"line":493,"address":4389825,"length":1,"stats":{"Line":0}},{"line":494,"address":4389911,"length":1,"stats":{"Line":0}},{"line":499,"address":4390163,"length":1,"stats":{"Line":0}},{"line":500,"address":4390113,"length":1,"stats":{"Line":0}},{"line":502,"address":4390249,"length":1,"stats":{"Line":0}},{"line":505,"address":4382905,"length":1,"stats":{"Line":0}},{"line":508,"address":4281585,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":207},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","module","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Defined the few global types used by all modules,\n//! as well as the DursModule trait that all modules must implement.\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate serde_derive;\n\nuse dup_crypto::keys::{KeyPair, KeyPairEnum};\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_common_tools::traits::merge::Merge;\nuse durs_network_documents::network_endpoint::{ApiPart, EndpointEnum};\nuse failure::Fail;\nuse log::error;\nuse serde::de::DeserializeOwned;\nuse serde::ser::{Serialize, Serializer};\nuse std::collections::HashSet;\nuse std::fmt::Debug;\nuse std::path::PathBuf;\nuse std::sync::mpsc;\n//use structopt::clap::ArgMatches;\nuse structopt::StructOpt;\n\n#[derive(Copy, Clone, Deserialize, Debug, PartialEq, Eq, Hash, Serialize)]\n/// Store module name in static lifetime\npub struct ModuleStaticName(pub \u0026'static str);\n\nimpl std::fmt::Display for ModuleStaticName {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"{}\", self.0)\n }\n}\n\n#[derive(Clone, Deserialize, Debug, PartialEq, Eq, Hash, Serialize)]\n/// Store module name\npub struct ModuleName(pub String);\n\nimpl From\u003cModuleStaticName\u003e for ModuleName {\n fn from(source: ModuleStaticName) -\u003e Self {\n ModuleName(String::from(source.0))\n }\n}\n\nimpl\u003c'a\u003e From\u003c\u0026'a str\u003e for ModuleName {\n fn from(source: \u0026str) -\u003e Self {\n ModuleName(String::from(source))\n }\n}\n\nimpl ToString for ModuleName {\n fn to_string(\u0026self) -\u003e String {\n self.0.clone()\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Identifier of an inter-module request\npub struct ModuleReqId(pub u32);\n\nimpl Serialize for ModuleReqId {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n serializer.serialize_str(\u0026format!(\"{:x}\", self.0))\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Several modules can simultaneously send requests with the same identifier.\n/// To identify each request in a unique way, we must therefore also take into account the identifier of the module performing the request.\npub struct ModuleReqFullId(pub ModuleStaticName, pub ModuleReqId);\n\nimpl ToString for ModuleReqFullId {\n fn to_string(\u0026self) -\u003e String {\n format!(\"{}-{}\", self.0.to_string(), (self.1).0)\n }\n}\n\n/// Dunitrust global configuration trait\npub trait DursGlobalConfTrait:\n Clone + Debug + PartialEq + Serialize + DeserializeOwned + Send + ToOwned\n{\n /// Get node id\n fn my_node_id(\u0026self) -\u003e u32;\n /// Get default sync module\n fn default_sync_module(\u0026self) -\u003e ModuleName;\n}\n\n/// Dunitrust configuration trait\npub trait DursConfTrait:\n Clone + Debug + Default + PartialEq + Serialize + DeserializeOwned + Send + ToOwned\n{\n /// Dunitrust configuration without modules configuration\n type GlobalConf: DursGlobalConfTrait;\n\n /// Disable a module\n fn disable(\u0026mut self, module: ModuleName);\n /// Get disabled modules\n fn disabled_modules(\u0026self) -\u003e HashSet\u003cModuleName\u003e;\n /// Enable a module\n fn enable(\u0026mut self, module: ModuleName);\n /// Get enabled modules\n fn enabled_modules(\u0026self) -\u003e HashSet\u003cModuleName\u003e;\n /// Get global conf\n fn get_global_conf(\u0026self) -\u003e Self::GlobalConf;\n /// Get modules conf\n fn modules(\u0026self) -\u003e serde_json::Value;\n /// Get node id\n fn my_node_id(\u0026self) -\u003e u32 {\n self.get_global_conf().my_node_id()\n }\n /// Set currency\n fn set_currency(\u0026mut self, new_currency: CurrencyName);\n /// Change module conf\n fn set_module_conf(\u0026mut self, module_name: ModuleName, new_module_conf: serde_json::Value);\n /// Upgrade configuration to latest version\n /// Return new configuration and a boolean which indicates if the configuration has been updated\n fn upgrade(self) -\u003e (Self, bool);\n /// Get conf version profile\n fn version(\u0026self) -\u003e usize;\n}\n\n/// Sofware meta datas\n#[derive(Debug, Clone)]\npub struct SoftwareMetaDatas\u003cDC: DursConfTrait\u003e {\n /// User configuration\n pub conf: DC,\n /// Path where the user profile datas are stored\n pub profile_path: PathBuf,\n /// Software name\n pub soft_name: \u0026'static str,\n /// Software version\n pub soft_version: \u0026'static str,\n}\n\n/// The different modules of Duniter-rs can exchange messages with the type of their choice,\n/// provided that this type implements the ModuleMessage trait.\npub trait ModuleMessage: Clone + Debug {}\n\n/// List of the different roles that can be assigned to a module.\n/// This role list allows a module to send a message to all modules playing a specific role without knowing their name.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub enum ModuleRole {\n /// Manages the blockchain data (include forks datas)\n BlockchainDatas,\n /// Checks if a block complies with the entire blockchain protocol\n BlockValidation,\n /// Generates the content of the next block\n BlockGeneration,\n /// Change configuration file\n ChangeConf,\n /// Communicates with client software\n ClientsNetwork,\n /// Manage pending data for the currency (transactions and scripts)\n CurrencyPool,\n /// Manages the network between nodes implementing the DUP protocol\n InterNodesNetwork,\n /// Communicates with the node user\n UserInterface,\n /// Manage pending data for the wot\n WotPool,\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n///List of the different types of events that can be generated by a module.\n/// This list allows the different modules to subscribe only to the types of events that interest them\npub enum ModuleEvent {\n /// Currency parameters are defined\n /// This happens either at the start of the node if it's already synchronized on a currency, or at the 1st synchronization on a currency\n CurrencyParameters,\n /// A new block has been received from the network\n NewBlockFromNetwork,\n /// A new transaction has been received from a client software.\n NewTxFromNetwork,\n /// A new wot document has been received from a network.\n NewWotDocFromNetwork,\n /// A new valid block has been added to the local blockchain\n NewValidBlock,\n /// A new valid block issued by the local node has been added to the local blockchain\n NewValidBlockFromSelf,\n /// A new non-isolated fork is in the local database\n NewFork,\n /// Blockchain rooling back\n RevertBlocks,\n /// A new transaction has been integrated into the local mempool\n NewTxinPool,\n /// A new wot document has been integrated into the local mempool\n NewWotDocInPool,\n /// A new valid HEAD has been received from the network\n NewValidHeadFromNetwork,\n /// Change in connections with other nodes (disconnection of a connection or establishment of a new connection)\n ConnectionsChangeNodeNetwork,\n /// A new self peer card is generated\n NewSelfPeer,\n /// A new valid peer record has been received from the network\n NewValidPeerFromNodeNetwork,\n /// Synchronisation event\n SyncEvent,\n}\n\n#[derive(Debug, Clone)]\n/// Type sent by each module to the router during initialization\npub enum RouterThreadMessage\u003cM: ModuleMessage\u003e {\n /// Number of expected modules\n ModulesCount(usize),\n /// Registration of the module at the router\n ModuleRegistration {\n /// Module name\n static_name: ModuleStaticName,\n /// Module channel sender (to send messages to the module)\n sender: mpsc::Sender\u003cM\u003e,\n /// Module roles\n roles: Vec\u003cModuleRole\u003e,\n /// Events to which the module subscribes\n events_subscription: Vec\u003cModuleEvent\u003e,\n /// API parts that the module reserves\n reserved_apis_parts: Vec\u003cApiPart\u003e,\n /// Endpoints that the module wishes to declare in the peer card of the local node\n endpoints: Vec\u003cEndpointEnum\u003e,\n },\n /// Module message\n ModuleMessage(M),\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Indicates which keys the module needs to operate\npub enum RequiredKeys {\n /// The module needs the member keypair (private key included).\n MemberKeyPair(),\n /// The module only needs the member public key.\n MemberPublicKey(),\n /// The module needs the network keypair (private key included).\n NetworkKeyPair(),\n /// The module only needs the network public key.\n NetworkPublicKey(),\n /// The module does not need any key\n None(),\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n/// Contains the keys the module needs\npub enum RequiredKeysContent {\n /// Contains the member keypair (private key included).\n MemberKeyPair(Option\u003cKeyPairEnum\u003e),\n /// Contains the member public key.\n MemberPublicKey(Option\u003c\u003cKeyPairEnum as KeyPair\u003e::PublicKey\u003e),\n /// Contains the network keypair (private key included).\n NetworkKeyPair(KeyPairEnum),\n /// Contains the network public key.\n NetworkPublicKey(\u003cKeyPairEnum as KeyPair\u003e::PublicKey),\n /// Does not contain any keys\n None(),\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Defined the priority level of the module\npub enum ModulePriority {\n /// This module is necessary for Duniter-Rs to work properly, impossible to disable it.\n Essential(),\n /// This module is recommended but it's not essential, it's enabled by default but can be disabled by the user.\n Recommended(),\n /// This module is disabled by default, it must be explicitly enabled by the user.\n Optional(),\n}\n\n/// Determines if a module is activated or not\npub fn enabled\u003cDC: DursConfTrait, Mess: ModuleMessage, M: DursModule\u003cDC, Mess\u003e\u003e(conf: \u0026DC) -\u003e bool {\n let disabled_modules = conf.disabled_modules();\n let enabled_modules = conf.enabled_modules();\n match M::priority() {\n ModulePriority::Essential() =\u003e true,\n ModulePriority::Recommended() =\u003e !disabled_modules.contains(\u0026ModuleName::from(M::name())),\n ModulePriority::Optional() =\u003e enabled_modules.contains(\u0026ModuleName::from(M::name())),\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Modules filter\n/// If bool = false, the meaning of the filter is reversed.\npub enum ModulesFilter {\n /// Enabled modules\n Enabled(bool),\n /// Network modules\n Network(),\n /// Modules that require member private key\n RequireMemberPrivKey(),\n}\n\n/// Returns true only if the module checks all filters\npub fn module_valid_filters\u003c\n DC: DursConfTrait,\n Mess: ModuleMessage,\n M: DursModule\u003cDC, Mess\u003e,\n S: ::std::hash::BuildHasher,\n\u003e(\n conf: \u0026DC,\n filters: \u0026HashSet\u003cModulesFilter, S\u003e,\n network_module: bool,\n) -\u003e bool {\n if filters.contains(\u0026ModulesFilter::Network()) \u0026\u0026 !network_module {\n return false;\n }\n if filters.contains(\u0026ModulesFilter::RequireMemberPrivKey())\n \u0026\u0026 M::ask_required_keys() != RequiredKeys::MemberKeyPair()\n {\n return false;\n }\n if filters.contains(\u0026ModulesFilter::Enabled(true)) \u0026\u0026 !enabled::\u003cDC, Mess, M\u003e(conf) {\n return false;\n }\n if filters.contains(\u0026ModulesFilter::Enabled(false)) \u0026\u0026 enabled::\u003cDC, Mess, M\u003e(conf) {\n return false;\n }\n true\n}\n\n#[derive(Debug, Fail)]\n/// Error when generating the configuration of a module\npub enum ModuleConfError {\n /// Combination forbidden\n #[fail(display = \"Forbidden configuration: {}\", cause)]\n CombinationForbidden {\n /// Cause\n cause: String,\n },\n /// Invalid field\n #[fail(display = \"Field '{}' is invalid: {}\", field_name, cause)]\n InvalidField {\n /// Field name\n field_name: \u0026'static str,\n /// Cause\n cause: String,\n },\n /// Parse error\n #[fail(display = \"{}\", _0)]\n ParseError(serde_json::Error),\n}\n\nimpl From\u003cserde_json::Error\u003e for ModuleConfError {\n fn from(err: serde_json::Error) -\u003e Self {\n ModuleConfError::ParseError(err)\n }\n}\n\n#[derive(Debug, Fail)]\n/// Error when plug a module\npub enum PlugModuleError {\n /// Fail to spawn thread for a module\n #[fail(\n display = \"Fail to spawn main thread for module '{}': {}\",\n module_name, error\n )]\n FailSpawnModuleThread {\n /// Module name\n module_name: ModuleStaticName,\n /// Error\n error: std::io::Error,\n },\n /// Error when generating the configuration of a module\n #[fail(display = \"{}\", _0)]\n ModuleConfError(ModuleConfError),\n}\n\nimpl From\u003cModuleConfError\u003e for PlugModuleError {\n fn from(err: ModuleConfError) -\u003e Self {\n PlugModuleError::ModuleConfError(err)\n }\n}\n\n/// All Duniter-rs modules must implement this trait.\npub trait DursModule\u003cDC: DursConfTrait, M: ModuleMessage\u003e {\n ///Module user configuration (configuration provided by the user)\n type ModuleUserConf: Clone\n + Debug\n + Default\n + DeserializeOwned\n + Merge\n + Send\n + Serialize\n + Sync;\n /// Module real configuration (configuration calculated from the configuration provided by the user and the global configuration)\n type ModuleConf: 'static + Clone + Debug + Default + Send + Sync;\n /// Module subcommand options\n type ModuleOpt: StructOpt;\n\n /// Returns the module name\n fn name() -\u003e ModuleStaticName;\n /// Returns the module priority\n fn priority() -\u003e ModulePriority;\n /// Indicates which keys the module needs\n fn ask_required_keys() -\u003e RequiredKeys;\n /// Generate module configuration\n fn generate_module_conf(\n currency_name: Option\u003c\u0026CurrencyName\u003e,\n global_conf: \u0026DC::GlobalConf,\n module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n ) -\u003e Result\u003c(Self::ModuleConf, Option\u003cSelf::ModuleUserConf\u003e), ModuleConfError\u003e;\n /// Define if module have a cli subcommand\n fn have_subcommand() -\u003e bool {\n false\n }\n /// Execute injected subcommand\n fn exec_subcommand(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDC\u003e,\n _keys: RequiredKeysContent,\n _module_conf: Self::ModuleConf,\n _module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n _subcommand_args: Self::ModuleOpt,\n ) -\u003e Option\u003cSelf::ModuleUserConf\u003e {\n None\n }\n /// Module launchable as sync ?\n fn launchable_as_sync() -\u003e bool {\n false\n }\n /// Launch the module\n fn start(\n soft_meta_datas: \u0026SoftwareMetaDatas\u003cDC\u003e,\n keys: RequiredKeysContent,\n module_conf: Self::ModuleConf,\n main_sender: mpsc::Sender\u003cRouterThreadMessage\u003cM\u003e\u003e,\n ) -\u003e Result\u003c(), failure::Error\u003e;\n /// Launch the module in sync mode\n fn start_at_sync(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDC\u003e,\n _keys: RequiredKeysContent,\n _module_conf: Self::ModuleConf,\n _main_sender: mpsc::Sender\u003cRouterThreadMessage\u003cM\u003e\u003e,\n _cautious_mode: bool,\n _unsafe_mode: bool,\n ) -\u003e Result\u003c(), failure::Error\u003e {\n fatal_error!(\n \"Dev error: Module '{}' can be launched in sync but don't reimplement the sync method!\",\n Self::name(),\n )\n }\n}\n","traces":[{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":294,"address":4375984,"length":1,"stats":{"Line":0}},{"line":295,"address":4375996,"length":1,"stats":{"Line":0}},{"line":296,"address":4376029,"length":1,"stats":{"Line":0}},{"line":297,"address":4376053,"length":1,"stats":{"Line":0}},{"line":298,"address":4376091,"length":1,"stats":{"Line":0}},{"line":299,"address":4376180,"length":1,"stats":{"Line":0}},{"line":300,"address":4376305,"length":1,"stats":{"Line":0}},{"line":317,"address":4374784,"length":1,"stats":{"Line":0}},{"line":327,"address":4374806,"length":1,"stats":{"Line":0}},{"line":328,"address":4374867,"length":1,"stats":{"Line":0}},{"line":330,"address":4374886,"length":1,"stats":{"Line":0}},{"line":331,"address":4374916,"length":1,"stats":{"Line":0}},{"line":333,"address":4374981,"length":1,"stats":{"Line":0}},{"line":335,"address":4374988,"length":1,"stats":{"Line":0}},{"line":336,"address":4375067,"length":1,"stats":{"Line":0}},{"line":338,"address":4375077,"length":1,"stats":{"Line":0}},{"line":339,"address":4375154,"length":1,"stats":{"Line":0}},{"line":341,"address":4375164,"length":1,"stats":{"Line":0}},{"line":367,"address":null,"length":0,"stats":{"Line":0}},{"line":368,"address":null,"length":0,"stats":{"Line":0}},{"line":392,"address":null,"length":0,"stats":{"Line":0}},{"line":393,"address":null,"length":0,"stats":{"Line":0}},{"line":426,"address":null,"length":0,"stats":{"Line":0}},{"line":427,"address":null,"length":0,"stats":{"Line":0}},{"line":430,"address":null,"length":0,"stats":{"Line":0}},{"line":437,"address":null,"length":0,"stats":{"Line":0}},{"line":440,"address":null,"length":0,"stats":{"Line":0}},{"line":441,"address":null,"length":0,"stats":{"Line":0}},{"line":451,"address":null,"length":0,"stats":{"Line":0}},{"line":459,"address":null,"length":0,"stats":{"Line":0}},{"line":460,"address":null,"length":0,"stats":{"Line":0}},{"line":461,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":45},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","network","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Defined all aspects of the inter-node network that concern all modules and are therefore independent of one implementation or another of this network layer.\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate structopt;\n\nuse crate::cli::sync::SyncOpt;\nuse durs_module::*;\nuse durs_network_documents::network_endpoint::ApiFeatures;\nuse durs_network_documents::network_head::NetworkHead;\nuse durs_network_documents::*;\nuse failure::Fail;\nuse std::sync::mpsc;\n\npub mod cli;\npub mod events;\npub mod requests;\n\n/// ApiModule\npub trait ApiModule\u003cDC: DursConfTrait, M: ModuleMessage\u003e: DursModule\u003cDC, M\u003e {\n /// Parsing error\n type ParseErr;\n /// Parse raw api features\n fn parse_raw_api_features(str_features: \u0026str) -\u003e Result\u003cApiFeatures, Self::ParseErr\u003e;\n}\n\n/// NetworkModule\npub trait NetworkModule\u003cDC: DursConfTrait, M: ModuleMessage\u003e: ApiModule\u003cDC, M\u003e {\n /// Launch synchronisation\n fn sync(\n soft_meta_datas: \u0026SoftwareMetaDatas\u003cDC\u003e,\n keys: RequiredKeysContent,\n module_conf: \u003cSelf as DursModule\u003cDC, M\u003e\u003e::ModuleConf,\n main_sender: mpsc::Sender\u003cRouterThreadMessage\u003cM\u003e\u003e,\n sync_params: SyncOpt,\n ) -\u003e Result\u003c(), SyncError\u003e;\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Type returned when the network module fails to determine the current network consensus\npub enum NetworkConsensusError {\n /// The network module does not have enough data to determine consensus\n InsufficientData(usize),\n /// The network module does not determine consensus, there is most likely a fork\n Fork(),\n}\n\n#[derive(Debug, Clone, Fail, PartialEq, Eq, Hash)]\n/// Synchronization error\npub enum SyncError {\n /// Invalid source\n #[fail(display = \"invalid source: {}\", source)]\n InvalidSource {\n /// Source\n source: String,\n },\n /// Unreachable source\n #[fail(display = \"unreachable source: {}\", source)]\n UnreachableSource {\n /// Source\n source: String,\n },\n}\n","traces":[{"line":1,"address":4222011,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":1},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","core","network","requests.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Defined network requests.\n\nuse crate::*;\nuse dubp_documents::documents::block::BlockDocument;\nuse dubp_documents::documents::UserDocumentDUBP;\nuse dubp_documents::Blockstamp;\n\n#[derive(Debug, Copy, Clone)]\n/// Type containing a request addressed to the network module\npub enum OldNetworkRequest {\n /// Get a current block of a specific node\n GetCurrent(ModuleReqFullId),\n //GetBlock(u64),\n /// Get a blocks chunk from specified node\n GetBlocks(ModuleReqFullId, u32, u32),\n /// Get pending wot documents from specified node\n GetRequirementsPending(ModuleReqFullId, u32),\n /// Obtain the current network consensus\n GetConsensus(ModuleReqFullId),\n /// Getting the heads cache\n GetHeadsCache(ModuleReqFullId),\n /// Get a list of known endpoints\n GetEndpoints(ModuleReqFullId),\n}\n\nimpl OldNetworkRequest {\n /// Get request full identitifier\n pub fn get_req_full_id(\u0026self) -\u003e ModuleReqFullId {\n match *self {\n OldNetworkRequest::GetCurrent(ref req_id)\n | OldNetworkRequest::GetBlocks(ref req_id, _, _)\n | OldNetworkRequest::GetRequirementsPending(ref req_id, _)\n | OldNetworkRequest::GetConsensus(ref req_id)\n | OldNetworkRequest::GetHeadsCache(ref req_id)\n | OldNetworkRequest::GetEndpoints(ref req_id) =\u003e *req_id,\n }\n }\n /// Get request identitifier\n pub fn get_req_id(\u0026self) -\u003e ModuleReqId {\n self.get_req_full_id().1\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// Type returned when the network module does not get a satisfying answer to a request\npub enum OldNetworkRequestError {\n /// Receiving an invalid format response\n WrongFormat(),\n /// Unknow error\n UnknowError(),\n /// No response received\n NoResponse(),\n /// Unable to reach the target node\n ReceiverUnreachable(),\n}\n\n#[derive(Debug, Clone)]\n/// Type containing the response to a network request\npub enum NetworkResponse {\n /// CurrentBlock\n CurrentBlock(ModuleReqFullId, NodeFullId, Box\u003cBlockDocument\u003e),\n /// Block\n Block(ModuleReqFullId, NodeFullId, Box\u003cBlockDocument\u003e),\n /// Chunk\n Chunk(ModuleReqFullId, NodeFullId, Vec\u003cBlockDocument\u003e),\n /// PendingDocuments\n PendingDocuments(ModuleReqFullId, Vec\u003cUserDocumentDUBP\u003e),\n /// Consensus\n Consensus(ModuleReqFullId, Result\u003cBlockstamp, NetworkConsensusError\u003e),\n /// HeadsCache\n HeadsCache(ModuleReqFullId, Box\u003cNetworkHead\u003e),\n}\n\nimpl NetworkResponse {\n /// Get request full identifier\n pub fn get_req_full_id(\u0026self) -\u003e ModuleReqFullId {\n match *self {\n NetworkResponse::CurrentBlock(ref req_id, _, _)\n | NetworkResponse::Block(ref req_id, _, _)\n | NetworkResponse::Chunk(ref req_id, _, _)\n | NetworkResponse::PendingDocuments(ref req_id, _)\n | NetworkResponse::Consensus(ref req_id, _)\n | NetworkResponse::HeadsCache(ref req_id, _) =\u003e *req_id,\n }\n }\n /// Get request identifier\n pub fn get_req_id(\u0026self) -\u003e ModuleReqId {\n self.get_req_full_id().1\n }\n}\n","traces":[{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":20},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dbex.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::*;\nuse dubp_documents::documents::block::BlockDocumentTrait;\nuse dubp_documents::documents::transaction::*;\nuse dup_crypto::keys::*;\nuse durs_wot::data::rusty::RustyWebOfTrust;\nuse durs_wot::data::WebOfTrust;\nuse durs_wot::operations::distance::{DistanceCalculator, WotDistance, WotDistanceParameters};\nuse std::time::*;\nuse unwrap::unwrap;\n\npub static EMPTY_BLOCKCHAIN: \u0026'static str =\n \"No blockchain, please sync your node to get a blockchain.\";\n\n#[derive(Debug, Clone)]\n/// Query for wot databases explorer\npub enum DBExWotQuery {\n /// Ask distance of all members\n AllDistances(bool),\n /// Show members expire date\n ExpireMembers(bool),\n /// Show members list\n ListMembers(bool),\n /// Ask member datas\n MemberDatas(String),\n}\n\n#[derive(Debug, Clone)]\n/// Query for tx databases explorer\npub enum DBExTxQuery {\n /// Ask balance of an address (pubkey or uid)\n Balance(String),\n}\n\n#[derive(Debug, Clone)]\n/// Query for databases explorer\npub enum DBExQuery {\n /// Wot query\n WotQuery(DBExWotQuery),\n /// Tx query\n TxQuery(DBExTxQuery),\n}\n\npub fn dbex(profile_path: PathBuf, csv: bool, query: \u0026DBExQuery) {\n match *query {\n DBExQuery::WotQuery(ref wot_query) =\u003e dbex_wot(profile_path, csv, wot_query),\n DBExQuery::TxQuery(ref tx_query) =\u003e dbex_tx(profile_path, csv, tx_query),\n }\n}\n\npub fn dbex_tx(profile_path: PathBuf, _csv: bool, query: \u0026DBExTxQuery) {\n // Get db path\n let db_path = durs_conf::get_blockchain_db_path(profile_path);\n\n // Open databases\n let load_dbs_begin = SystemTime::now();\n //let blocks_databases = BlocksV10DBs::open(Some(\u0026db_path));\n let currency_databases = CurrencyV10DBs::open(Some(\u0026db_path));\n let wot_databases = WotsV10DBs::open(Some(\u0026db_path));\n let load_dbs_duration = SystemTime::now()\n .duration_since(load_dbs_begin)\n .expect(\"duration_since error !\");\n println!(\n \"Databases loaded in {}.{:03} seconds.\",\n load_dbs_duration.as_secs(),\n load_dbs_duration.subsec_millis()\n );\n let req_process_begin = SystemTime::now();\n match *query {\n DBExTxQuery::Balance(ref address_str) =\u003e {\n let pubkey = if let Ok(ed25519_pubkey) = ed25519::PublicKey::from_base58(address_str) {\n PubKey::Ed25519(ed25519_pubkey)\n } else if let Some(pubkey) =\n durs_blockchain_dal::readers::identity::get_pubkey_from_uid(\n \u0026wot_databases.identities_db,\n address_str,\n )\n .expect(\"get_uid : DALError\")\n {\n pubkey\n } else {\n println!(\"This address doesn't exist!\");\n return;\n };\n let address = UTXOConditionsGroup::Single(TransactionOutputCondition::Sig(pubkey));\n let address_balance = durs_blockchain_dal::readers::balance::get_address_balance(\n \u0026currency_databases.balances_db,\n \u0026address,\n )\n .expect(\"get_address_balance : DALError\")\n .expect(\"Address not found in balances DB.\");\n println!(\n \"Balance={},{} Ğ1\",\n (address_balance.0).0 / 100,\n (address_balance.0).0 % 100\n );\n }\n }\n\n let req_process_duration = SystemTime::now()\n .duration_since(req_process_begin)\n .expect(\"duration_since error\");\n println!(\n \"Request processed in {}.{:06} seconds.\",\n req_process_duration.as_secs(),\n req_process_duration.subsec_micros()\n );\n}\n\npub fn dbex_wot(profile_path: PathBuf, csv: bool, query: \u0026DBExWotQuery) {\n // Get db path\n let db_path = durs_conf::get_blockchain_db_path(profile_path.clone());\n\n // Open databases\n let load_dbs_begin = SystemTime::now();\n let wot_databases = WotsV10DBs::open(Some(\u0026db_path));\n let load_dbs_duration = SystemTime::now()\n .duration_since(load_dbs_begin)\n .expect(\"duration_since error\");\n println!(\n \"Databases loaded in {}.{:03} seconds.\",\n load_dbs_duration.as_secs(),\n load_dbs_duration.subsec_millis()\n );\n\n // Get currency parameters\n let currency_params_db_datas =\n dup_currency_params::db::get_currency_params(durs_conf::get_datas_path(profile_path))\n .expect(\"Fail to parse currency params !\");\n if currency_params_db_datas.is_none() {\n println!(\"{}\", EMPTY_BLOCKCHAIN);\n return;\n }\n let currency_params = unwrap!(currency_params_db_datas).1;\n\n // get wot_index\n let wot_index =\n readers::identity::get_wot_index(\u0026wot_databases.identities_db).expect(\"DALError\");\n\n // get wot_reverse_index\n let wot_reverse_index: HashMap\u003cNodeId, \u0026PubKey\u003e =\n wot_index.iter().map(|(p, id)| (*id, p)).collect();\n\n // get wot uid index\n let wot_uid_index: HashMap\u003cNodeId, String\u003e = wot_databases\n .identities_db\n .read(|db| {\n db.iter()\n .map(|(_, idty)| (idty.wot_id, String::from(idty.idty_doc.username())))\n .collect()\n })\n .expect(\"Fail to read IdentitiesDB !\");\n\n // Open wot db\n let wot_db = BinDB::File(\n open_file_db::\u003cRustyWebOfTrust\u003e(\u0026db_path, \"wot.db\").expect(\"Fail to open WotDB !\"),\n );\n\n // Print wot blockstamp\n //println!(\"Wot : Current blockstamp = {}.\", wot_blockstamp);\n\n // Get members count\n let members_count = wot_db\n .read(WebOfTrust::get_enabled)\n .expect(\"Fail to read WotDB\")\n .len();\n\n match *query {\n DBExWotQuery::AllDistances(ref reverse) =\u003e {\n println!(\"compute distances...\");\n let compute_distances_begin = SystemTime::now();\n let mut distances_datas: Vec\u003c(NodeId, WotDistance)\u003e = wot_db\n .read(|db| {\n db.get_enabled()\n .iter()\n .map(|wot_id| {\n (\n *wot_id,\n DISTANCE_CALCULATOR\n .compute_distance(\n db,\n WotDistanceParameters {\n node: *wot_id,\n sentry_requirement: 5,\n step_max: currency_params.step_max as u32,\n x_percent: currency_params.x_percent,\n },\n )\n .expect(\"Fail to get distance !\"),\n )\n })\n .collect()\n })\n .expect(\"Fail to read WotDB\");\n let compute_distances_duration = SystemTime::now()\n .duration_since(compute_distances_begin)\n .expect(\"duration_since error\");\n if *reverse {\n distances_datas.sort_unstable_by(|(_, d1), (_, d2)| d1.success.cmp(\u0026d2.success));\n } else {\n distances_datas.sort_unstable_by(|(_, d1), (_, d2)| d2.success.cmp(\u0026d1.success));\n }\n for (wot_id, distance_datas) in distances_datas {\n let distance_percent: f64 =\n f64::from(distance_datas.success) / f64::from(distance_datas.sentries) * 100.0;\n if csv {\n println!(\"{}, {}\", wot_uid_index[\u0026wot_id], distance_percent,);\n } else {\n println!(\n \"{} -\u003e distance: {:.2}% ({}/{})\",\n wot_uid_index[\u0026wot_id],\n distance_percent,\n distance_datas.success,\n distance_datas.sentries\n );\n }\n }\n println!(\n \"compute_distances_duration = {},{:03}.\",\n compute_distances_duration.as_secs(),\n compute_distances_duration.subsec_millis()\n );\n }\n DBExWotQuery::ExpireMembers(ref reverse) =\u003e {\n // Open blockchain database\n let blockchain_db = open_file_db::\u003cLocalBlockchainV10Datas\u003e(\u0026db_path, \"blockchain.db\")\n .expect(\"Fail to open blockchain db\");\n // Get blocks_times\n let (current_bc_time, blocks_times): (u64, HashMap\u003cBlockNumber, u64\u003e) = blockchain_db\n .read(|db| {\n (\n db[\u0026BlockNumber(db.len() as u32 - 1)].block.common_time(),\n db.iter()\n .map(|(block_id, dal_block)| (*block_id, dal_block.block.common_time()))\n .collect(),\n )\n })\n .expect(\"Fail to read blockchain db\");\n // Get expire_dates\n let min_created_ms_time = current_bc_time - currency_params.ms_validity;\n let mut expire_dates: Vec\u003c(NodeId, u64)\u003e = wot_databases\n .ms_db\n .read(|db| {\n let mut expire_dates = Vec::new();\n for (block_id, nodes_ids) in db {\n let created_ms_time = blocks_times[\u0026block_id];\n if created_ms_time \u003e min_created_ms_time {\n for node_id in nodes_ids {\n expire_dates.push((\n *node_id,\n created_ms_time + currency_params.ms_validity,\n ));\n }\n }\n }\n expire_dates\n })\n .expect(\"Fail to read ms db\");\n if *reverse {\n expire_dates.sort_unstable_by(|(_, d1), (_, d2)| d1.cmp(\u0026d2));\n } else {\n expire_dates.sort_unstable_by(|(_, d1), (_, d2)| d2.cmp(\u0026d1));\n }\n for (node_id, expire_date) in expire_dates {\n println!(\"{}, {}\", wot_uid_index[\u0026node_id], expire_date);\n }\n }\n DBExWotQuery::MemberDatas(ref uid) =\u003e {\n println!(\" Members count = {}.\", members_count);\n if let Some(pubkey) = durs_blockchain_dal::readers::identity::get_pubkey_from_uid(\n \u0026wot_databases.identities_db,\n uid,\n )\n .expect(\"get_pubkey_from_uid() : DALError !\")\n {\n let wot_id = wot_index[\u0026pubkey];\n println!(\n \"{} : wot_id={}, pubkey={}.\",\n uid,\n wot_id.0,\n pubkey.to_string()\n );\n let distance_datas = wot_db\n .read(|db| {\n DISTANCE_CALCULATOR.compute_distance(\n db,\n WotDistanceParameters {\n node: wot_id,\n sentry_requirement: 5,\n step_max: currency_params.step_max as u32,\n x_percent: currency_params.x_percent,\n },\n )\n })\n .expect(\"Fail to read WotDB\")\n .expect(\"Fail to get distance !\");\n let distance_percent: f64 =\n f64::from(distance_datas.success) / f64::from(distance_datas.sentries) * 100.0;\n println!(\n \"Distance {:.2}% ({}/{})\",\n distance_percent, distance_datas.success, distance_datas.sentries\n );\n let sources = wot_db\n .read(|db| db.get_links_source(wot_id))\n .expect(\"Fail to read WotDB\")\n .expect(\"Fail to get links source !\");\n println!(\"Certifiers : {}\", sources.len());\n for (i, source) in sources.iter().enumerate() {\n let source_uid = durs_blockchain_dal::readers::identity::get_uid(\n \u0026wot_databases.identities_db,\n *(wot_reverse_index[\u0026source]),\n )\n .expect(\"get_uid() : DALError\")\n .expect(\"Not found source_uid !\");\n println!(\"{}: {}\", i + 1, source_uid);\n }\n } else {\n println!(\"Uid \\\"{}\\\" not found !\", uid);\n }\n }\n _ =\u003e {}\n }\n}\n","traces":[{"line":58,"address":4563744,"length":1,"stats":{"Line":0}},{"line":59,"address":4563880,"length":1,"stats":{"Line":0}},{"line":60,"address":4563762,"length":1,"stats":{"Line":0}},{"line":61,"address":4563882,"length":1,"stats":{"Line":0}},{"line":65,"address":4563968,"length":1,"stats":{"Line":0}},{"line":67,"address":4563995,"length":1,"stats":{"Line":0}},{"line":70,"address":4564069,"length":1,"stats":{"Line":0}},{"line":72,"address":4564136,"length":1,"stats":{"Line":0}},{"line":73,"address":4564197,"length":1,"stats":{"Line":0}},{"line":74,"address":4564232,"length":1,"stats":{"Line":0}},{"line":75,"address":4564306,"length":1,"stats":{"Line":0}},{"line":77,"address":4564543,"length":1,"stats":{"Line":0}},{"line":79,"address":4564468,"length":1,"stats":{"Line":0}},{"line":80,"address":4564507,"length":1,"stats":{"Line":0}},{"line":82,"address":4564856,"length":1,"stats":{"Line":0}},{"line":84,"address":4564915,"length":1,"stats":{"Line":0}},{"line":85,"address":4564931,"length":1,"stats":{"Line":0}},{"line":86,"address":4565081,"length":1,"stats":{"Line":0}},{"line":87,"address":4565351,"length":1,"stats":{"Line":0}},{"line":88,"address":4565269,"length":1,"stats":{"Line":0}},{"line":89,"address":4565222,"length":1,"stats":{"Line":0}},{"line":90,"address":4565230,"length":1,"stats":{"Line":0}},{"line":94,"address":4565428,"length":1,"stats":{"Line":0}},{"line":96,"address":4565465,"length":1,"stats":{"Line":0}},{"line":99,"address":4565576,"length":1,"stats":{"Line":0}},{"line":100,"address":4565744,"length":1,"stats":{"Line":0}},{"line":101,"address":4565736,"length":1,"stats":{"Line":0}},{"line":106,"address":4566109,"length":1,"stats":{"Line":0}},{"line":108,"address":4565899,"length":1,"stats":{"Line":0}},{"line":109,"address":4565977,"length":1,"stats":{"Line":0}},{"line":114,"address":4566408,"length":1,"stats":{"Line":0}},{"line":115,"address":4566455,"length":1,"stats":{"Line":0}},{"line":117,"address":4566647,"length":1,"stats":{"Line":0}},{"line":119,"address":4566584,"length":1,"stats":{"Line":0}},{"line":120,"address":4566617,"length":1,"stats":{"Line":0}},{"line":124,"address":4567104,"length":1,"stats":{"Line":0}},{"line":126,"address":4567137,"length":1,"stats":{"Line":0}},{"line":129,"address":4567253,"length":1,"stats":{"Line":0}},{"line":130,"address":4567320,"length":1,"stats":{"Line":0}},{"line":131,"address":4567373,"length":1,"stats":{"Line":0}},{"line":132,"address":4567432,"length":1,"stats":{"Line":0}},{"line":134,"address":4567672,"length":1,"stats":{"Line":0}},{"line":136,"address":4567597,"length":1,"stats":{"Line":0}},{"line":137,"address":4567636,"length":1,"stats":{"Line":0}},{"line":142,"address":4567985,"length":1,"stats":{"Line":0}},{"line":143,"address":4568109,"length":1,"stats":{"Line":0}},{"line":144,"address":4568125,"length":1,"stats":{"Line":0}},{"line":145,"address":4568174,"length":1,"stats":{"Line":0}},{"line":148,"address":4568410,"length":1,"stats":{"Line":0}},{"line":152,"address":4568607,"length":1,"stats":{"Line":0}},{"line":156,"address":4356736,"length":1,"stats":{"Line":0}},{"line":159,"address":4568756,"length":1,"stats":{"Line":0}},{"line":161,"address":4356976,"length":1,"stats":{"Line":0}},{"line":162,"address":4356988,"length":1,"stats":{"Line":0}},{"line":163,"address":4356816,"length":1,"stats":{"Line":0}},{"line":169,"address":4568918,"length":1,"stats":{"Line":0}},{"line":170,"address":4568829,"length":1,"stats":{"Line":0}},{"line":177,"address":4569010,"length":1,"stats":{"Line":0}},{"line":180,"address":4569097,"length":1,"stats":{"Line":0}},{"line":182,"address":4571658,"length":1,"stats":{"Line":0}},{"line":183,"address":4569143,"length":1,"stats":{"Line":0}},{"line":184,"address":4569250,"length":1,"stats":{"Line":0}},{"line":185,"address":4569308,"length":1,"stats":{"Line":0}},{"line":186,"address":4569383,"length":1,"stats":{"Line":0}},{"line":187,"address":4357296,"length":1,"stats":{"Line":0}},{"line":188,"address":4357316,"length":1,"stats":{"Line":0}},{"line":190,"address":4357056,"length":1,"stats":{"Line":0}},{"line":192,"address":4357073,"length":1,"stats":{"Line":0}},{"line":193,"address":4357081,"length":1,"stats":{"Line":0}},{"line":195,"address":4357088,"length":1,"stats":{"Line":0}},{"line":196,"address":4357141,"length":1,"stats":{"Line":0}},{"line":197,"address":4357099,"length":1,"stats":{"Line":0}},{"line":199,"address":4357107,"length":1,"stats":{"Line":0}},{"line":200,"address":4357126,"length":1,"stats":{"Line":0}},{"line":208,"address":4569449,"length":1,"stats":{"Line":0}},{"line":209,"address":4569457,"length":1,"stats":{"Line":0}},{"line":210,"address":4569516,"length":1,"stats":{"Line":0}},{"line":212,"address":4569655,"length":1,"stats":{"Line":0}},{"line":213,"address":4357552,"length":1,"stats":{"Line":0}},{"line":215,"address":4357648,"length":1,"stats":{"Line":0}},{"line":217,"address":4569780,"length":1,"stats":{"Line":0}},{"line":219,"address":4570158,"length":1,"stats":{"Line":0}},{"line":220,"address":4570265,"length":1,"stats":{"Line":0}},{"line":221,"address":4570295,"length":1,"stats":{"Line":0}},{"line":223,"address":4570666,"length":1,"stats":{"Line":0}},{"line":225,"address":4570635,"length":1,"stats":{"Line":0}},{"line":227,"address":4570650,"length":1,"stats":{"Line":0}},{"line":232,"address":4571337,"length":1,"stats":{"Line":0}},{"line":234,"address":4571262,"length":1,"stats":{"Line":0}},{"line":235,"address":4571301,"length":1,"stats":{"Line":0}},{"line":238,"address":4571663,"length":1,"stats":{"Line":0}},{"line":240,"address":4571683,"length":1,"stats":{"Line":0}},{"line":243,"address":4571773,"length":1,"stats":{"Line":0}},{"line":244,"address":4357856,"length":1,"stats":{"Line":0}},{"line":246,"address":4357871,"length":1,"stats":{"Line":0}},{"line":247,"address":4357982,"length":1,"stats":{"Line":0}},{"line":248,"address":4357744,"length":1,"stats":{"Line":0}},{"line":254,"address":4571885,"length":1,"stats":{"Line":0}},{"line":255,"address":4571956,"length":1,"stats":{"Line":0}},{"line":257,"address":4358112,"length":1,"stats":{"Line":0}},{"line":258,"address":4358127,"length":1,"stats":{"Line":0}},{"line":259,"address":4358172,"length":1,"stats":{"Line":0}},{"line":260,"address":4358452,"length":1,"stats":{"Line":0}},{"line":261,"address":4358541,"length":1,"stats":{"Line":0}},{"line":262,"address":4358567,"length":1,"stats":{"Line":0}},{"line":263,"address":4358822,"length":1,"stats":{"Line":0}},{"line":264,"address":4358761,"length":1,"stats":{"Line":0}},{"line":265,"address":4358772,"length":1,"stats":{"Line":0}},{"line":270,"address":4358475,"length":1,"stats":{"Line":0}},{"line":272,"address":4572070,"length":1,"stats":{"Line":0}},{"line":273,"address":4572078,"length":1,"stats":{"Line":0}},{"line":274,"address":4358928,"length":1,"stats":{"Line":0}},{"line":276,"address":4359008,"length":1,"stats":{"Line":0}},{"line":278,"address":4572203,"length":1,"stats":{"Line":0}},{"line":279,"address":4572538,"length":1,"stats":{"Line":0}},{"line":282,"address":4572923,"length":1,"stats":{"Line":0}},{"line":283,"address":4572951,"length":1,"stats":{"Line":0}},{"line":284,"address":4573156,"length":1,"stats":{"Line":0}},{"line":285,"address":4573109,"length":1,"stats":{"Line":0}},{"line":286,"address":4573117,"length":1,"stats":{"Line":0}},{"line":290,"address":4573327,"length":1,"stats":{"Line":0}},{"line":291,"address":4573396,"length":1,"stats":{"Line":0}},{"line":295,"address":4573361,"length":1,"stats":{"Line":0}},{"line":297,"address":4573895,"length":1,"stats":{"Line":0}},{"line":298,"address":4359088,"length":1,"stats":{"Line":0}},{"line":299,"address":4359110,"length":1,"stats":{"Line":0}},{"line":300,"address":4359117,"length":1,"stats":{"Line":0}},{"line":301,"address":4359156,"length":1,"stats":{"Line":0}},{"line":302,"address":4359122,"length":1,"stats":{"Line":0}},{"line":304,"address":4359130,"length":1,"stats":{"Line":0}},{"line":305,"address":4359145,"length":1,"stats":{"Line":0}},{"line":312,"address":4574004,"length":1,"stats":{"Line":0}},{"line":313,"address":4574109,"length":1,"stats":{"Line":0}},{"line":315,"address":4574093,"length":1,"stats":{"Line":0}},{"line":317,"address":4574568,"length":1,"stats":{"Line":0}},{"line":318,"address":4359216,"length":1,"stats":{"Line":0}},{"line":321,"address":4574669,"length":1,"stats":{"Line":0}},{"line":322,"address":4574896,"length":1,"stats":{"Line":0}},{"line":323,"address":4575390,"length":1,"stats":{"Line":0}},{"line":324,"address":4575287,"length":1,"stats":{"Line":0}},{"line":325,"address":4575295,"length":1,"stats":{"Line":0}},{"line":329,"address":4575496,"length":1,"stats":{"Line":0}},{"line":331,"address":4574786,"length":1,"stats":{"Line":0}},{"line":332,"address":4575857,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":144},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dubp","apply","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that applies the content of a block to the indexes of the local blockchain.\n\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait, BlockDocumentV10};\nuse dubp_documents::documents::transaction::{TxAmount, TxBase};\nuse dubp_documents::{BlockNumber, Document};\nuse dup_crypto::keys::*;\nuse durs_blockchain_dal::entities::block::DALBlock;\nuse durs_blockchain_dal::entities::sources::SourceAmount;\nuse durs_blockchain_dal::writers::requests::*;\nuse durs_blockchain_dal::BinDB;\nuse durs_common_tools::fatal_error;\nuse durs_wot::data::NewLinkResult;\nuse durs_wot::{NodeId, WebOfTrust};\nuse std::collections::{HashMap, HashSet};\n\n#[derive(Debug, Clone)]\n/// Stores all queries to apply in database to \"apply\" the block\npub struct ValidBlockApplyReqs(\n pub BlocksDBsWriteQuery,\n pub Vec\u003cWotsDBsWriteQuery\u003e,\n pub Vec\u003cCurrencyDBsWriteQuery\u003e,\n);\n\n#[derive(Debug, Copy, Clone)]\n/// ApplyValidBlockError\npub enum ApplyValidBlockError {\n DBsCorrupted,\n ExcludeUnknowNodeId,\n RevokeUnknowNodeId,\n}\n\n#[inline]\npub fn apply_valid_block\u003cW: WebOfTrust\u003e(\n block: BlockDocument,\n wot_index: \u0026mut HashMap\u003cPubKey, NodeId\u003e,\n wot_db: \u0026BinDB\u003cW\u003e,\n expire_certs: \u0026HashMap\u003c(NodeId, NodeId), BlockNumber\u003e,\n) -\u003e Result\u003cValidBlockApplyReqs, ApplyValidBlockError\u003e {\n match block {\n BlockDocument::V10(block_v10) =\u003e {\n apply_valid_block_v10(block_v10, wot_index, wot_db, expire_certs)\n }\n }\n}\n\npub fn apply_valid_block_v10\u003cW: WebOfTrust\u003e(\n mut block: BlockDocumentV10,\n wot_index: \u0026mut HashMap\u003cPubKey, NodeId\u003e,\n wot_db: \u0026BinDB\u003cW\u003e,\n expire_certs: \u0026HashMap\u003c(NodeId, NodeId), BlockNumber\u003e,\n) -\u003e Result\u003cValidBlockApplyReqs, ApplyValidBlockError\u003e {\n debug!(\n \"BlockchainModule : apply_valid_block({})\",\n block.blockstamp(),\n );\n let mut wot_dbs_requests = Vec::new();\n let mut currency_dbs_requests = Vec::new();\n let current_blockstamp = block.blockstamp();\n let mut identities = HashMap::with_capacity(block.identities.len());\n for identity in \u0026block.identities {\n identities.insert(identity.issuers()[0], identity);\n }\n for joiner in \u0026block.joiners {\n let pubkey = joiner.issuers()[0];\n if let Some(idty_doc) = identities.get(\u0026pubkey) {\n // Newcomer\n let wot_id = NodeId(\n wot_db\n .read(WebOfTrust::size)\n .expect(\"Fatal error : fail to read WotDB !\"),\n );\n wot_db\n .write(|db| {\n db.add_node();\n })\n .expect(\"Fail to write in WotDB\");\n wot_index.insert(pubkey, wot_id);\n wot_dbs_requests.push(WotsDBsWriteQuery::CreateIdentity(\n wot_id,\n current_blockstamp,\n block.median_time,\n Box::new((*idty_doc).clone()),\n joiner.blockstamp().id,\n ));\n } else {\n // Renewer\n let wot_id = wot_index[\u0026joiner.issuers()[0]];\n wot_db\n .write(|db| {\n db.set_enabled(wot_id, true);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RenewalIdentity(\n joiner.issuers()[0],\n wot_id,\n block.median_time,\n joiner.blockstamp().id,\n ));\n }\n }\n for active in \u0026block.actives {\n let pubkey = active.issuers()[0];\n if !identities.contains_key(\u0026pubkey) {\n let wot_id = wot_index[\u0026pubkey];\n wot_db\n .write(|db| {\n db.set_enabled(wot_id, true);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RenewalIdentity(\n pubkey,\n wot_id,\n block.median_time,\n active.blockstamp().id,\n ));\n }\n }\n for exclusion in \u0026block.excluded {\n let wot_id = if let Some(wot_id) = wot_index.get(\u0026exclusion) {\n wot_id\n } else {\n return Err(ApplyValidBlockError::ExcludeUnknowNodeId);\n };\n wot_db\n .write(|db| {\n db.set_enabled(*wot_id, false);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::ExcludeIdentity(\n *exclusion,\n block.blockstamp(),\n ));\n }\n for revocation in \u0026block.revoked {\n let compact_revoc = revocation.to_compact_document();\n let wot_id = if let Some(wot_id) = wot_index.get(\u0026compact_revoc.issuer) {\n wot_id\n } else {\n return Err(ApplyValidBlockError::RevokeUnknowNodeId);\n };\n wot_db\n .write(|db| {\n db.set_enabled(*wot_id, false);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevokeIdentity(\n compact_revoc.issuer,\n block.blockstamp(),\n true,\n ));\n }\n for certification in \u0026block.certifications {\n trace!(\"stack_up_valid_block: apply cert...\");\n let compact_cert = certification.to_compact_document();\n let wot_node_from = wot_index\n .get(\u0026compact_cert.issuer)\n .ok_or(ApplyValidBlockError::DBsCorrupted)?;\n let wot_node_to = wot_index\n .get(\u0026compact_cert.target)\n .ok_or(ApplyValidBlockError::DBsCorrupted)?;\n wot_db\n .write(|db| {\n let result = db.add_link(*wot_node_from, *wot_node_to);\n match result {\n NewLinkResult::Ok(_) =\u003e {}\n _ =\u003e fatal_error!(\n \"Fail to add_link {}-\u003e{} : {:?}\",\n wot_node_from.0,\n wot_node_to.0,\n result\n ),\n }\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::CreateCert(\n compact_cert.issuer,\n *wot_node_from,\n *wot_node_to,\n compact_cert.block_number,\n block.median_time,\n ));\n trace!(\"stack_up_valid_block: apply cert...success.\");\n }\n if !expire_certs.is_empty() {\n let mut blocks_already_expire = HashSet::new();\n for ((source, target), created_block_id) in expire_certs {\n if !blocks_already_expire.contains(created_block_id) {\n wot_dbs_requests.push(WotsDBsWriteQuery::ExpireCerts(*created_block_id));\n blocks_already_expire.insert(*created_block_id);\n }\n wot_db\n .write(|db| {\n let _ = db.rem_link(*source, *target);\n })\n .expect(\"Fail to write in WotDB\");\n }\n }\n if let Some(du_amount) = block.dividend {\n if du_amount \u003e 0 {\n let members_wot_ids = wot_db\n .read(WebOfTrust::get_enabled)\n .expect(\"Fail to read WotDB\");\n let mut members_pubkeys = Vec::new();\n for (pubkey, wot_id) in wot_index {\n if members_wot_ids.contains(wot_id) {\n members_pubkeys.push(*pubkey);\n }\n }\n currency_dbs_requests.push(CurrencyDBsWriteQuery::CreateUD(\n SourceAmount(TxAmount(du_amount as isize), TxBase(block.unit_base)),\n block.number,\n members_pubkeys,\n ));\n }\n }\n\n for tx in \u0026block.transactions {\n currency_dbs_requests.push(CurrencyDBsWriteQuery::WriteTx(Box::new(tx.unwrap_doc())));\n }\n\n /*// Calculate the state of the wot\n if !wot_events.is_empty() \u0026\u0026 verif_level != SyncVerificationLevel::FastSync() {\n // Calculate sentries_count\n let sentries_count = wot.get_sentries(3).len();\n // Calculate average_density\n let average_density = calculate_average_density::\u003cW\u003e(\u0026wot);\n let sentry_requirement =\n get_sentry_requirement(block.members_count, G1_PARAMS.step_max);\n // Calculate distances and connectivities\n let (average_distance, distances, average_connectivity, connectivities) =\n compute_distances::\u003cW\u003e(\n \u0026wot,\n sentry_requirement,\n G1_PARAMS.step_max,\n G1_PARAMS.x_percent,\n );\n // Calculate centralities and average_centrality\n let centralities =\n calculate_distance_stress_centralities::\u003cW\u003e(\u0026wot, G1_PARAMS.step_max);\n let average_centrality =\n (centralities.iter().sum::\u003cu64\u003e() as f64 / centralities.len() as f64) as usize;\n // Register the state of the wot\n let max_connectivity = currency_params.max_connectivity();\n durs_blockchain_dal::register_wot_state(\n db,\n \u0026WotState {\n block_number: block.number.0,\n block_hash: block.hash.expect(\"Fail to get block hash\").to_string(),\n sentries_count,\n average_density,\n average_distance,\n distances,\n average_connectivity,\n connectivities: connectivities\n .iter()\n .map(|c| {\n if *c \u003e max_connectivity {\n max_connectivity\n } else {\n *c\n }\n })\n .collect(),\n average_centrality,\n centralities,\n },\n );\n }*/\n // Create DALBlock\n block.reduce();\n let dal_block = DALBlock {\n block: BlockDocument::V10(block),\n expire_certs: Some(expire_certs.clone()),\n };\n // Return DBs requests\n Ok(ValidBlockApplyReqs(\n BlocksDBsWriteQuery::WriteBlock(dal_block),\n wot_dbs_requests,\n currency_dbs_requests,\n ))\n}\n","traces":[{"line":48,"address":5365152,"length":1,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":5365360,"length":1,"stats":{"Line":0}},{"line":67,"address":5365400,"length":1,"stats":{"Line":0}},{"line":69,"address":5365716,"length":1,"stats":{"Line":0}},{"line":71,"address":5365914,"length":1,"stats":{"Line":0}},{"line":72,"address":5365921,"length":1,"stats":{"Line":0}},{"line":73,"address":5365944,"length":1,"stats":{"Line":0}},{"line":74,"address":5365983,"length":1,"stats":{"Line":0}},{"line":75,"address":5366042,"length":1,"stats":{"Line":0}},{"line":76,"address":5366278,"length":1,"stats":{"Line":0}},{"line":78,"address":5366313,"length":1,"stats":{"Line":0}},{"line":79,"address":5366647,"length":1,"stats":{"Line":0}},{"line":80,"address":5366809,"length":1,"stats":{"Line":0}},{"line":82,"address":5366954,"length":1,"stats":{"Line":0}},{"line":83,"address":5366888,"length":1,"stats":{"Line":0}},{"line":87,"address":5366962,"length":1,"stats":{"Line":0}},{"line":92,"address":5367016,"length":1,"stats":{"Line":0}},{"line":93,"address":5367387,"length":1,"stats":{"Line":0}},{"line":94,"address":5367109,"length":1,"stats":{"Line":0}},{"line":95,"address":5367117,"length":1,"stats":{"Line":0}},{"line":96,"address":5367171,"length":1,"stats":{"Line":0}},{"line":97,"address":5367175,"length":1,"stats":{"Line":0}},{"line":98,"address":5367345,"length":1,"stats":{"Line":0}},{"line":102,"address":5367516,"length":1,"stats":{"Line":0}},{"line":103,"address":5367643,"length":1,"stats":{"Line":0}},{"line":108,"address":5367894,"length":1,"stats":{"Line":0}},{"line":109,"address":5367721,"length":1,"stats":{"Line":0}},{"line":110,"address":5367820,"length":1,"stats":{"Line":0}},{"line":111,"address":5367836,"length":1,"stats":{"Line":0}},{"line":112,"address":5367840,"length":1,"stats":{"Line":0}},{"line":116,"address":5366682,"length":1,"stats":{"Line":0}},{"line":117,"address":5368199,"length":1,"stats":{"Line":0}},{"line":118,"address":5368361,"length":1,"stats":{"Line":0}},{"line":119,"address":5368393,"length":1,"stats":{"Line":0}},{"line":120,"address":5368443,"length":1,"stats":{"Line":0}},{"line":125,"address":5368660,"length":1,"stats":{"Line":0}},{"line":126,"address":5368521,"length":1,"stats":{"Line":0}},{"line":127,"address":5368567,"length":1,"stats":{"Line":0}},{"line":128,"address":5368583,"length":1,"stats":{"Line":0}},{"line":129,"address":5368587,"length":1,"stats":{"Line":0}},{"line":133,"address":5368234,"length":1,"stats":{"Line":0}},{"line":134,"address":5368965,"length":1,"stats":{"Line":0}},{"line":135,"address":5369103,"length":1,"stats":{"Line":0}},{"line":137,"address":5369169,"length":1,"stats":{"Line":0}},{"line":139,"address":5369119,"length":1,"stats":{"Line":0}},{"line":144,"address":5369411,"length":1,"stats":{"Line":0}},{"line":145,"address":5369347,"length":1,"stats":{"Line":0}},{"line":146,"address":5369404,"length":1,"stats":{"Line":0}},{"line":149,"address":5369004,"length":1,"stats":{"Line":0}},{"line":150,"address":5369721,"length":1,"stats":{"Line":0}},{"line":151,"address":5369791,"length":1,"stats":{"Line":0}},{"line":152,"address":5369882,"length":1,"stats":{"Line":0}},{"line":154,"address":5369945,"length":1,"stats":{"Line":0}},{"line":156,"address":5369898,"length":1,"stats":{"Line":0}},{"line":161,"address":5370079,"length":1,"stats":{"Line":0}},{"line":162,"address":5370010,"length":1,"stats":{"Line":0}},{"line":163,"address":5370072,"length":1,"stats":{"Line":0}},{"line":167,"address":5369752,"length":1,"stats":{"Line":0}},{"line":168,"address":5370397,"length":1,"stats":{"Line":0}},{"line":169,"address":5370712,"length":1,"stats":{"Line":0}},{"line":170,"address":5370735,"length":1,"stats":{"Line":0}},{"line":171,"address":5370743,"length":1,"stats":{"Line":0}},{"line":172,"address":5370766,"length":1,"stats":{"Line":0}},{"line":173,"address":5370983,"length":1,"stats":{"Line":0}},{"line":174,"address":5370991,"length":1,"stats":{"Line":0}},{"line":175,"address":5371014,"length":1,"stats":{"Line":0}},{"line":176,"address":5371229,"length":1,"stats":{"Line":0}},{"line":190,"address":5371418,"length":1,"stats":{"Line":0}},{"line":191,"address":5371331,"length":1,"stats":{"Line":0}},{"line":192,"address":5371377,"length":1,"stats":{"Line":0}},{"line":193,"address":5371388,"length":1,"stats":{"Line":0}},{"line":194,"address":5371399,"length":1,"stats":{"Line":0}},{"line":195,"address":5371414,"length":1,"stats":{"Line":0}},{"line":197,"address":5371526,"length":1,"stats":{"Line":0}},{"line":199,"address":5370438,"length":1,"stats":{"Line":0}},{"line":200,"address":5371847,"length":1,"stats":{"Line":0}},{"line":201,"address":5371854,"length":1,"stats":{"Line":0}},{"line":202,"address":5372181,"length":1,"stats":{"Line":0}},{"line":203,"address":5372244,"length":1,"stats":{"Line":0}},{"line":204,"address":5372292,"length":1,"stats":{"Line":0}},{"line":206,"address":5372330,"length":1,"stats":{"Line":0}},{"line":213,"address":5372447,"length":1,"stats":{"Line":0}},{"line":214,"address":5372484,"length":1,"stats":{"Line":0}},{"line":215,"address":5372499,"length":1,"stats":{"Line":0}},{"line":218,"address":5372557,"length":1,"stats":{"Line":0}},{"line":219,"address":5372576,"length":1,"stats":{"Line":0}},{"line":220,"address":5372881,"length":1,"stats":{"Line":0}},{"line":221,"address":5373189,"length":1,"stats":{"Line":0}},{"line":224,"address":5373033,"length":1,"stats":{"Line":0}},{"line":225,"address":5372919,"length":1,"stats":{"Line":0}},{"line":226,"address":5372987,"length":1,"stats":{"Line":0}},{"line":227,"address":5372993,"length":1,"stats":{"Line":0}},{"line":232,"address":5373299,"length":1,"stats":{"Line":0}},{"line":233,"address":5373517,"length":1,"stats":{"Line":0}},{"line":285,"address":5373544,"length":1,"stats":{"Line":0}},{"line":286,"address":5373888,"length":1,"stats":{"Line":0}},{"line":287,"address":5373688,"length":1,"stats":{"Line":0}},{"line":288,"address":5373774,"length":1,"stats":{"Line":0}},{"line":291,"address":5374182,"length":1,"stats":{"Line":0}},{"line":292,"address":5374008,"length":1,"stats":{"Line":0}},{"line":293,"address":5374081,"length":1,"stats":{"Line":0}},{"line":294,"address":5374121,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":105},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dubp","check","hashs.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Verify block inner hash and block hash\n\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait, VerifyBlockHashError};\n\n/// Verify block hashs\npub fn verify_block_hashs(block_doc: \u0026BlockDocument) -\u003e Result\u003c(), VerifyBlockHashError\u003e {\n trace!(\"complete_block #{}...\", block_doc.number());\n\n match block_doc.verify_inner_hash() {\n Ok(()) =\u003e block_doc.verify_hash().map_err(|e| {\n warn!(\"BlockchainModule : Refuse Bloc : invalid hash !\");\n e\n }),\n Err(e) =\u003e {\n warn!(\"BlockchainModule : Refuse Bloc : invalid inner hash !\");\n warn!(\"BlockDocument=\\\"{:?}\\\"\", block_doc);\n warn!(\n \"BlockInnerFormat=\\\"{}\\\"\",\n block_doc.generate_compact_inner_text()\n );\n Err(e)\n }\n }\n}\n","traces":[{"line":21,"address":4486960,"length":1,"stats":{"Line":0}},{"line":22,"address":4486985,"length":1,"stats":{"Line":0}},{"line":24,"address":4487389,"length":1,"stats":{"Line":0}},{"line":25,"address":4487410,"length":1,"stats":{"Line":0}},{"line":26,"address":4830593,"length":1,"stats":{"Line":0}},{"line":27,"address":4830816,"length":1,"stats":{"Line":0}},{"line":29,"address":4487514,"length":1,"stats":{"Line":0}},{"line":30,"address":4487551,"length":1,"stats":{"Line":0}},{"line":31,"address":4487794,"length":1,"stats":{"Line":0}},{"line":32,"address":4488138,"length":1,"stats":{"Line":0}},{"line":34,"address":4488301,"length":1,"stats":{"Line":0}},{"line":36,"address":4488524,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":12},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dubp","check","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module checking if a block complies with all the rules of the (DUBP DUniter Blockchain Protocol).\n\npub mod hashs;\n\nuse crate::dubp::BlockError;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\nuse dubp_documents::*;\nuse dup_crypto::keys::PubKey;\nuse durs_blockchain_dal::*;\nuse durs_wot::*;\nuse std::collections::HashMap;\n\n#[derive(Debug, Copy, Clone)]\npub enum InvalidBlockError {\n NoPreviousBlock,\n VersionDecrease,\n}\n\npub fn verify_block_validity\u003cW: WebOfTrust\u003e(\n block: \u0026BlockDocument,\n blockchain_db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n _certs_db: \u0026BinDB\u003cCertsExpirV10Datas\u003e,\n _wot_index: \u0026HashMap\u003cPubKey, NodeId\u003e,\n _wot_db: \u0026BinDB\u003cW\u003e,\n) -\u003e Result\u003c(), BlockError\u003e {\n // Rules that do not concern genesis block\n if block.number().0 \u003e 0 {\n // Get previous block\n let previous_block_opt = readers::block::get_block_in_local_blockchain(\n blockchain_db,\n BlockNumber(block.number().0 - 1),\n )?;\n\n // Previous block must exist\n if previous_block_opt.is_none() {\n return Err(BlockError::InvalidBlock(InvalidBlockError::NoPreviousBlock));\n }\n let previous_block = previous_block_opt.expect(\"safe unwrap\");\n\n // Block version must not decrease\n if previous_block.version() \u003e block.version() {\n return Err(BlockError::InvalidBlock(InvalidBlockError::VersionDecrease));\n }\n }\n\n Ok(())\n}\n","traces":[{"line":34,"address":5123120,"length":1,"stats":{"Line":0}},{"line":42,"address":5123167,"length":1,"stats":{"Line":0}},{"line":44,"address":5123290,"length":1,"stats":{"Line":0}},{"line":45,"address":5123232,"length":1,"stats":{"Line":0}},{"line":46,"address":5123237,"length":1,"stats":{"Line":0}},{"line":50,"address":5123641,"length":1,"stats":{"Line":0}},{"line":51,"address":5123660,"length":1,"stats":{"Line":0}},{"line":53,"address":5123755,"length":1,"stats":{"Line":0}},{"line":56,"address":5123838,"length":1,"stats":{"Line":0}},{"line":57,"address":5123916,"length":1,"stats":{"Line":0}},{"line":61,"address":5124066,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dubp","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that checks and applies the content of a block according to the DUBP (DUBP DUniter Blockchain Protocol).\n\npub mod apply;\npub mod check;\n\nuse crate::*;\nuse apply::*;\nuse check::*;\nuse dubp_documents::documents::block::{BlockDocumentTrait, VerifyBlockHashError};\nuse dubp_documents::Blockstamp;\nuse dubp_documents::Document;\nuse durs_blockchain_dal::entities::block::DALBlock;\nuse durs_blockchain_dal::*;\nuse unwrap::unwrap;\n\n#[derive(Debug, Clone)]\npub enum CheckAndApplyBlockReturn {\n ValidMainBlock(ValidBlockApplyReqs),\n ForkBlock,\n OrphanBlock,\n}\n\n#[derive(Debug, Copy, Clone)]\npub enum BlockError {\n AlreadyHaveBlock,\n BlockOrOutForkWindow,\n VerifyBlockHashError(VerifyBlockHashError),\n DALError(DALError),\n InvalidBlock(InvalidBlockError),\n ApplyValidBlockError(ApplyValidBlockError),\n}\n\nimpl From\u003cVerifyBlockHashError\u003e for BlockError {\n fn from(err: VerifyBlockHashError) -\u003e Self {\n BlockError::VerifyBlockHashError(err)\n }\n}\n\nimpl From\u003cDALError\u003e for BlockError {\n fn from(err: DALError) -\u003e Self {\n BlockError::DALError(err)\n }\n}\n\nimpl From\u003cApplyValidBlockError\u003e for BlockError {\n fn from(err: ApplyValidBlockError) -\u003e Self {\n BlockError::ApplyValidBlockError(err)\n }\n}\n\npub fn check_and_apply_block(\n bc: \u0026mut BlockchainModule,\n block_doc: BlockDocument,\n) -\u003e Result\u003cCheckAndApplyBlockReturn, BlockError\u003e {\n // Get BlockDocument \u0026\u0026 check if already have block\n let already_have_block = readers::block::already_have_block(\n \u0026bc.blocks_databases.blockchain_db,\n \u0026bc.forks_dbs,\n block_doc.blockstamp(),\n block_doc.previous_hash(),\n )?;\n\n // Verify block hashs\n dubp::check::hashs::verify_block_hashs(\u0026block_doc)?;\n\n // Check block chainability\n if (block_doc.number().0 == 0 \u0026\u0026 bc.current_blockstamp == Blockstamp::default())\n || (block_doc.number().0 == bc.current_blockstamp.id.0 + 1\n \u0026\u0026 unwrap!(block_doc.previous_hash()).to_string()\n == bc.current_blockstamp.hash.0.to_string())\n {\n debug!(\n \"stackable_block : block {} chainable !\",\n block_doc.blockstamp()\n );\n // Detect expire_certs\n let blocks_expiring = Vec::with_capacity(0);\n let expire_certs = durs_blockchain_dal::readers::certs::find_expire_certs(\n \u0026bc.wot_databases.certs_db,\n blocks_expiring,\n )?;\n\n // Verify block validity (check all protocol rule, very long !)\n verify_block_validity(\n \u0026block_doc,\n \u0026bc.blocks_databases.blockchain_db,\n \u0026bc.wot_databases.certs_db,\n \u0026bc.wot_index,\n \u0026bc.wot_databases.wot_db,\n )?;\n\n // If we're in block genesis, get the currency parameters\n if block_doc.number() == BlockNumber(0) {\n // Open currency_params_db\n let datas_path = durs_conf::get_datas_path(bc.profile_path.clone());\n // Get and write currency params\n bc.currency_params = Some(\n durs_blockchain_dal::readers::currency_params::get_and_write_currency_params(\n \u0026datas_path,\n \u0026block_doc,\n ),\n );\n }\n\n Ok(CheckAndApplyBlockReturn::ValidMainBlock(apply_valid_block(\n block_doc,\n \u0026mut bc.wot_index,\n \u0026bc.wot_databases.wot_db,\n \u0026expire_certs,\n )?))\n } else if already_have_block {\n Err(BlockError::AlreadyHaveBlock)\n } else if block_doc.number().0 \u003e= bc.current_blockstamp.id.0\n || (bc.current_blockstamp.id.0 - block_doc.number().0)\n \u003c unwrap!(bc.currency_params).fork_window_size as u32\n {\n debug!(\n \"stackable_block : block {} not chainable, store this for future !\",\n block_doc.blockstamp()\n );\n\n let dal_block = DALBlock {\n block: block_doc.clone(),\n expire_certs: None,\n };\n\n if durs_blockchain_dal::writers::block::insert_new_fork_block(\u0026bc.forks_dbs, dal_block)\n .expect(\"durs_blockchain_dal::writers::block::insert_new_fork_block() : DALError\")\n {\n Ok(CheckAndApplyBlockReturn::ForkBlock)\n } else {\n Ok(CheckAndApplyBlockReturn::OrphanBlock)\n }\n } else {\n debug!(\n \"stackable_block : block {} not chainable and already stored or out of forkWindowSize !\",\n block_doc.blockstamp()\n );\n Err(BlockError::BlockOrOutForkWindow)\n }\n}\n","traces":[{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":4212448,"length":1,"stats":{"Line":0}},{"line":71,"address":4212472,"length":1,"stats":{"Line":0}},{"line":72,"address":4212496,"length":1,"stats":{"Line":0}},{"line":73,"address":4212511,"length":1,"stats":{"Line":0}},{"line":74,"address":4212518,"length":1,"stats":{"Line":0}},{"line":75,"address":4212615,"length":1,"stats":{"Line":0}},{"line":79,"address":4212956,"length":1,"stats":{"Line":0}},{"line":82,"address":4213273,"length":1,"stats":{"Line":0}},{"line":83,"address":4213305,"length":1,"stats":{"Line":0}},{"line":84,"address":4213492,"length":1,"stats":{"Line":0}},{"line":85,"address":4213694,"length":1,"stats":{"Line":0}},{"line":87,"address":4213844,"length":1,"stats":{"Line":0}},{"line":89,"address":4214059,"length":1,"stats":{"Line":0}},{"line":92,"address":4214281,"length":1,"stats":{"Line":0}},{"line":93,"address":4214334,"length":1,"stats":{"Line":0}},{"line":94,"address":4214288,"length":1,"stats":{"Line":0}},{"line":95,"address":4214302,"length":1,"stats":{"Line":0}},{"line":99,"address":4214705,"length":1,"stats":{"Line":0}},{"line":101,"address":4217797,"length":1,"stats":{"Line":0}},{"line":102,"address":4217812,"length":1,"stats":{"Line":0}},{"line":103,"address":4217832,"length":1,"stats":{"Line":0}},{"line":108,"address":4215014,"length":1,"stats":{"Line":0}},{"line":110,"address":4215087,"length":1,"stats":{"Line":0}},{"line":112,"address":4215175,"length":1,"stats":{"Line":0}},{"line":113,"address":4215140,"length":1,"stats":{"Line":0}},{"line":120,"address":4215382,"length":1,"stats":{"Line":0}},{"line":121,"address":4215276,"length":1,"stats":{"Line":0}},{"line":122,"address":4215325,"length":1,"stats":{"Line":0}},{"line":123,"address":4215343,"length":1,"stats":{"Line":0}},{"line":126,"address":4214671,"length":1,"stats":{"Line":0}},{"line":127,"address":4215931,"length":1,"stats":{"Line":0}},{"line":128,"address":4216002,"length":1,"stats":{"Line":0}},{"line":129,"address":4216026,"length":1,"stats":{"Line":0}},{"line":130,"address":4216148,"length":1,"stats":{"Line":0}},{"line":132,"address":4216352,"length":1,"stats":{"Line":0}},{"line":134,"address":4216567,"length":1,"stats":{"Line":0}},{"line":137,"address":4216812,"length":1,"stats":{"Line":0}},{"line":138,"address":4216793,"length":1,"stats":{"Line":0}},{"line":139,"address":4216800,"length":1,"stats":{"Line":0}},{"line":142,"address":4216925,"length":1,"stats":{"Line":0}},{"line":145,"address":4217114,"length":1,"stats":{"Line":0}},{"line":147,"address":4217178,"length":1,"stats":{"Line":0}},{"line":150,"address":4217245,"length":1,"stats":{"Line":0}},{"line":152,"address":4217436,"length":1,"stats":{"Line":0}},{"line":154,"address":4217634,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":51},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dunp","queries.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that sends requests to the inter-node network layer.\n\nuse crate::*;\nuse durs_message::*;\nuse durs_module::ModuleReqId;\nuse durs_network::requests::OldNetworkRequest;\n\n/// Send network request\npub fn request_network(\n bc: \u0026BlockchainModule,\n req_id: ModuleReqId,\n request: \u0026OldNetworkRequest,\n) -\u003e ModuleReqId {\n if bc\n .router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Request {\n req_from: BlockchainModule::name(),\n req_to: ModuleRole::InterNodesNetwork,\n req_id,\n req_content: DursReqContent::OldNetworkRequest(*request),\n }))\n .is_err()\n {\n debug!(\"Fail to send OldNetworkRequest to router\");\n }\n request.get_req_id()\n}\n\n/// Request chunk from network (chunk = group of blocks)\npub fn request_chunk(\n bc: \u0026BlockchainModule,\n req_id: ModuleReqId,\n from: u32,\n) -\u003e (ModuleReqId, OldNetworkRequest) {\n let req = OldNetworkRequest::GetBlocks(\n ModuleReqFullId(BlockchainModule::name(), req_id),\n *CHUNK_SIZE,\n from,\n );\n (request_network(bc, req_id, \u0026req), req)\n}\n/// Requests blocks from current to `to`\npub fn request_blocks_to(\n bc: \u0026BlockchainModule,\n to: BlockNumber,\n) -\u003e HashMap\u003cModuleReqId, OldNetworkRequest\u003e {\n let from = if bc.current_blockstamp == Blockstamp::default() {\n 0\n } else {\n bc.current_blockstamp.id.0 + 1\n };\n info!(\n \"BlockchainModule : request_blocks_to({}-{})\",\n bc.current_blockstamp.id.0 + 1,\n to\n );\n if bc.current_blockstamp.id \u003c to {\n let real_to = if (to.0 - bc.current_blockstamp.id.0) \u003e *MAX_BLOCKS_REQUEST {\n bc.current_blockstamp.id.0 + *MAX_BLOCKS_REQUEST\n } else {\n to.0\n };\n request_blocks_from_to(bc, from, real_to)\n } else {\n HashMap::with_capacity(0)\n }\n}\n\n/// Requets previous blocks from specific orphan block\n#[inline]\npub fn request_orphan_previous(\n _bc: \u0026BlockchainModule,\n _orphan_block_number: BlockNumber,\n) -\u003e HashMap\u003cModuleReqId, OldNetworkRequest\u003e {\n /*if orphan_block_number.0\n \u003e bc.current_blockstamp.id.0 - *durs_blockchain_dal::constants::FORK_WINDOW_SIZE as u32\n \u0026\u0026 orphan_block_number.0 \u003c= bc.current_blockstamp.id.0 + *CHUNK_SIZE\n {\n request_blocks_from_to(\n bc,\n orphan_block_number.0 - *CHUNK_SIZE + 1,\n orphan_block_number.0,\n )\n } else {*/\n HashMap::with_capacity(0)\n}\n\nfn request_blocks_from_to(\n bc: \u0026BlockchainModule,\n mut from: u32,\n to: u32,\n) -\u003e HashMap\u003cModuleReqId, OldNetworkRequest\u003e {\n let mut requests_ids = HashMap::new();\n while from \u003c= to {\n let mut req_id = ModuleReqId(0);\n while bc.pending_network_requests.contains_key(\u0026req_id)\n || requests_ids.contains_key(\u0026req_id)\n {\n req_id = ModuleReqId(req_id.0 + 1);\n }\n let (req_id, req) = request_chunk(bc, req_id, from);\n requests_ids.insert(req_id, req);\n from += *CHUNK_SIZE;\n }\n requests_ids\n}\n","traces":[{"line":24,"address":4765712,"length":1,"stats":{"Line":0}},{"line":29,"address":4765733,"length":1,"stats":{"Line":0}},{"line":39,"address":4766112,"length":1,"stats":{"Line":0}},{"line":41,"address":4766348,"length":1,"stats":{"Line":0}},{"line":45,"address":4766400,"length":1,"stats":{"Line":0}},{"line":50,"address":4766495,"length":1,"stats":{"Line":0}},{"line":51,"address":4766433,"length":1,"stats":{"Line":0}},{"line":52,"address":4766482,"length":1,"stats":{"Line":0}},{"line":53,"address":4766491,"length":1,"stats":{"Line":0}},{"line":55,"address":4766547,"length":1,"stats":{"Line":0}},{"line":58,"address":4766672,"length":1,"stats":{"Line":0}},{"line":62,"address":4766691,"length":1,"stats":{"Line":0}},{"line":63,"address":4766761,"length":1,"stats":{"Line":0}},{"line":65,"address":4766771,"length":1,"stats":{"Line":0}},{"line":67,"address":4766816,"length":1,"stats":{"Line":0}},{"line":69,"address":4766979,"length":1,"stats":{"Line":0}},{"line":72,"address":4767274,"length":1,"stats":{"Line":0}},{"line":73,"address":4767315,"length":1,"stats":{"Line":0}},{"line":74,"address":4767361,"length":1,"stats":{"Line":0}},{"line":76,"address":4767410,"length":1,"stats":{"Line":0}},{"line":78,"address":4767421,"length":1,"stats":{"Line":0}},{"line":86,"address":4767552,"length":1,"stats":{"Line":0}},{"line":103,"address":4767600,"length":1,"stats":{"Line":0}},{"line":108,"address":4767623,"length":1,"stats":{"Line":0}},{"line":109,"address":4767663,"length":1,"stats":{"Line":0}},{"line":110,"address":4767712,"length":1,"stats":{"Line":0}},{"line":111,"address":4767720,"length":1,"stats":{"Line":0}},{"line":112,"address":4767795,"length":1,"stats":{"Line":0}},{"line":114,"address":4767852,"length":1,"stats":{"Line":0}},{"line":116,"address":4767750,"length":1,"stats":{"Line":0}},{"line":117,"address":4767947,"length":1,"stats":{"Line":0}},{"line":118,"address":4768027,"length":1,"stats":{"Line":0}},{"line":120,"address":4767678,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":33},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","dunp","receiver.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the reception of messages from the inter-node network layer\n//! (received by the intermediaries of events transmitted by the network module).\n\nuse crate::*;\nuse dubp_documents::documents::UserDocumentDUBP;\nuse unwrap::unwrap;\n\npub fn receive_user_documents(_bc: \u0026mut BlockchainModule, network_documents: \u0026[UserDocumentDUBP]) {\n for network_document in network_documents {\n match network_document {\n UserDocumentDUBP::Certification(_) =\u003e {}\n UserDocumentDUBP::Identity(_) =\u003e {}\n UserDocumentDUBP::Membership(_) =\u003e {}\n UserDocumentDUBP::Revocation(_) =\u003e {}\n UserDocumentDUBP::Transaction(_) =\u003e {}\n }\n }\n}\n\npub fn receive_blocks(bc: \u0026mut BlockchainModule, blocks: Vec\u003cBlockDocument\u003e) {\n debug!(\"BlockchainModule : receive_blocks({})\", blocks.len());\n let mut save_blocks_dbs = false;\n let mut save_wots_dbs = false;\n let mut save_currency_dbs = false;\n let mut first_orphan = true;\n for block in blocks.into_iter() {\n let blockstamp = block.blockstamp();\n match check_and_apply_block(bc, block) {\n Ok(check_block_return) =\u003e match check_block_return {\n CheckAndApplyBlockReturn::ValidMainBlock(ValidBlockApplyReqs(\n bc_db_query,\n wot_dbs_queries,\n tx_dbs_queries,\n )) =\u003e {\n let new_current_block = bc_db_query.get_block_doc_copy();\n bc.current_blockstamp = new_current_block.blockstamp();\n // Apply db requests\n bc_db_query\n .apply(\n \u0026bc.blocks_databases.blockchain_db,\n \u0026bc.forks_dbs,\n unwrap!(bc.currency_params).fork_window_size,\n None,\n )\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\");\n for query in \u0026wot_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026unwrap!(bc.currency_params), \u0026bc.wot_databases)\n .expect(\"Fatal error : Fail to apply WotsDBsWriteRequest !\");\n }\n for query in \u0026tx_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026bc.currency_databases)\n .expect(\"Fatal error : Fail to apply CurrencyDBsWriteRequest !\");\n }\n save_blocks_dbs = true;\n if !wot_dbs_queries.is_empty() {\n save_wots_dbs = true;\n }\n if !tx_dbs_queries.is_empty() {\n save_currency_dbs = true;\n }\n events::sent::send_event(\n bc,\n \u0026BlockchainEvent::StackUpValidBlock(Box::new(new_current_block)),\n );\n }\n CheckAndApplyBlockReturn::ForkBlock =\u003e {\n info!(\"new fork block(#{})\", blockstamp);\n if let Ok(Some(new_bc_branch)) = fork_algo::fork_resolution_algo(\n \u0026bc.forks_dbs,\n unwrap!(bc.currency_params).fork_window_size,\n bc.current_blockstamp,\n \u0026bc.invalid_forks,\n ) {\n rollback::apply_rollback(bc, new_bc_branch);\n }\n }\n CheckAndApplyBlockReturn::OrphanBlock =\u003e {\n if first_orphan {\n first_orphan = false;\n debug!(\"new orphan block(#{})\", blockstamp);\n crate::requests::sent::request_orphan_previous(bc, blockstamp);\n }\n }\n },\n Err(e) =\u003e match e {\n BlockError::VerifyBlockHashError(_) | BlockError::InvalidBlock(_) =\u003e {\n warn!(\"InvalidBlock(#{})\", blockstamp.id.0);\n crate::events::sent::send_event(bc, \u0026BlockchainEvent::RefusedBlock(blockstamp));\n }\n BlockError::ApplyValidBlockError(e2) =\u003e {\n error!(\"ApplyValidBlockError(#{}): {:?}\", blockstamp, e2);\n crate::events::sent::send_event(bc, \u0026BlockchainEvent::RefusedBlock(blockstamp));\n }\n BlockError::DALError(e2) =\u003e {\n error!(\"BlockError::DALError(#{}): {:?}\", blockstamp, e2);\n crate::events::sent::send_event(bc, \u0026BlockchainEvent::RefusedBlock(blockstamp));\n }\n BlockError::AlreadyHaveBlock =\u003e {\n debug!(\"AlreadyHaveBlock(#{})\", blockstamp.id);\n }\n BlockError::BlockOrOutForkWindow =\u003e {\n debug!(\"BlockOrOutForkWindow(#{})\", blockstamp);\n }\n },\n }\n }\n // Save databases\n if save_blocks_dbs {\n bc.blocks_databases.save_dbs();\n bc.forks_dbs.save_dbs();\n }\n if save_wots_dbs {\n bc.wot_databases.save_dbs();\n }\n if save_currency_dbs {\n bc.currency_databases.save_dbs(true, true);\n }\n}\n","traces":[{"line":23,"address":5380560,"length":1,"stats":{"Line":0}},{"line":24,"address":5380579,"length":1,"stats":{"Line":0}},{"line":26,"address":5380715,"length":1,"stats":{"Line":0}},{"line":35,"address":5380752,"length":1,"stats":{"Line":0}},{"line":36,"address":5380773,"length":1,"stats":{"Line":0}},{"line":37,"address":5381395,"length":1,"stats":{"Line":0}},{"line":38,"address":5381403,"length":1,"stats":{"Line":0}},{"line":39,"address":5381411,"length":1,"stats":{"Line":0}},{"line":40,"address":5381419,"length":1,"stats":{"Line":0}},{"line":41,"address":5381427,"length":1,"stats":{"Line":0}},{"line":42,"address":5388763,"length":1,"stats":{"Line":0}},{"line":43,"address":5381915,"length":1,"stats":{"Line":0}},{"line":44,"address":5382046,"length":1,"stats":{"Line":0}},{"line":45,"address":5382211,"length":1,"stats":{"Line":0}},{"line":46,"address":5382328,"length":1,"stats":{"Line":0}},{"line":47,"address":5382385,"length":1,"stats":{"Line":0}},{"line":48,"address":5382433,"length":1,"stats":{"Line":0}},{"line":50,"address":5382481,"length":1,"stats":{"Line":0}},{"line":51,"address":5382524,"length":1,"stats":{"Line":0}},{"line":53,"address":5382653,"length":1,"stats":{"Line":0}},{"line":61,"address":5383022,"length":1,"stats":{"Line":0}},{"line":62,"address":5383227,"length":1,"stats":{"Line":0}},{"line":66,"address":5383415,"length":1,"stats":{"Line":0}},{"line":67,"address":5383729,"length":1,"stats":{"Line":0}},{"line":71,"address":5383780,"length":1,"stats":{"Line":0}},{"line":72,"address":5383796,"length":1,"stats":{"Line":0}},{"line":73,"address":5383859,"length":1,"stats":{"Line":0}},{"line":75,"address":5383875,"length":1,"stats":{"Line":0}},{"line":76,"address":5383904,"length":1,"stats":{"Line":0}},{"line":79,"address":5383912,"length":1,"stats":{"Line":0}},{"line":80,"address":5383920,"length":1,"stats":{"Line":0}},{"line":84,"address":5384154,"length":1,"stats":{"Line":0}},{"line":85,"address":5384848,"length":1,"stats":{"Line":0}},{"line":86,"address":5384552,"length":1,"stats":{"Line":0}},{"line":87,"address":5384570,"length":1,"stats":{"Line":0}},{"line":88,"address":5384759,"length":1,"stats":{"Line":0}},{"line":89,"address":5384810,"length":1,"stats":{"Line":0}},{"line":91,"address":5384928,"length":1,"stats":{"Line":0}},{"line":95,"address":5385044,"length":1,"stats":{"Line":0}},{"line":96,"address":5385058,"length":1,"stats":{"Line":0}},{"line":97,"address":5385066,"length":1,"stats":{"Line":0}},{"line":98,"address":5385464,"length":1,"stats":{"Line":0}},{"line":102,"address":5385583,"length":1,"stats":{"Line":0}},{"line":103,"address":5385663,"length":1,"stats":{"Line":0}},{"line":104,"address":5385726,"length":1,"stats":{"Line":0}},{"line":105,"address":5386124,"length":1,"stats":{"Line":0}},{"line":107,"address":5386288,"length":1,"stats":{"Line":0}},{"line":108,"address":5386302,"length":1,"stats":{"Line":0}},{"line":109,"address":5386832,"length":1,"stats":{"Line":0}},{"line":111,"address":5386996,"length":1,"stats":{"Line":0}},{"line":112,"address":5387010,"length":1,"stats":{"Line":0}},{"line":113,"address":5387544,"length":1,"stats":{"Line":0}},{"line":116,"address":5387708,"length":1,"stats":{"Line":0}},{"line":119,"address":5388081,"length":1,"stats":{"Line":0}},{"line":125,"address":5388466,"length":1,"stats":{"Line":0}},{"line":126,"address":5388476,"length":1,"stats":{"Line":0}},{"line":127,"address":5388502,"length":1,"stats":{"Line":0}},{"line":129,"address":5388532,"length":1,"stats":{"Line":0}},{"line":130,"address":5388542,"length":1,"stats":{"Line":0}},{"line":132,"address":5388572,"length":1,"stats":{"Line":0}},{"line":133,"address":5388582,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":61},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","events","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing events received from other durs modules\n\nuse crate::*;\nuse durs_message::events::DursEvent;\nuse durs_module::*;\nuse std::ops::Deref;\n\npub fn receive_event(\n bc: \u0026mut BlockchainModule,\n _event_type: ModuleEvent,\n event_content: DursEvent,\n) {\n match event_content {\n DursEvent::NetworkEvent(network_event) =\u003e match network_event {\n NetworkEvent::ReceiveDocuments(network_docs) =\u003e {\n dunp::receiver::receive_user_documents(bc, \u0026network_docs);\n }\n NetworkEvent::ReceiveBlocks(blocks) =\u003e {\n dunp::receiver::receive_blocks(bc, blocks);\n }\n NetworkEvent::ReceiveHeads(_) =\u003e {}\n _ =\u003e {}\n },\n DursEvent::MemPoolEvent(mempool_event) =\u003e {\n if let MemPoolEvent::FindNextBlock(next_block_box) = mempool_event {\n dunp::receiver::receive_blocks(bc, vec![next_block_box.deref().clone()]);\n }\n }\n _ =\u003e {} // Others modules events\n }\n}\n","traces":[{"line":23,"address":4233696,"length":1,"stats":{"Line":0}},{"line":28,"address":4234435,"length":1,"stats":{"Line":0}},{"line":29,"address":4233721,"length":1,"stats":{"Line":0}},{"line":30,"address":4234002,"length":1,"stats":{"Line":0}},{"line":31,"address":4234155,"length":1,"stats":{"Line":0}},{"line":33,"address":4234316,"length":1,"stats":{"Line":0}},{"line":34,"address":4234356,"length":1,"stats":{"Line":0}},{"line":39,"address":4234440,"length":1,"stats":{"Line":0}},{"line":40,"address":4234504,"length":1,"stats":{"Line":0}},{"line":41,"address":4234551,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":10},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","events","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the events emitted by the blockchain module.\n\nuse crate::*;\nuse durs_common_tools::fatal_error;\nuse durs_message::events::BlockchainEvent;\nuse durs_module::ModuleEvent;\n\n/// Send blockchain event\npub fn send_event(bc: \u0026BlockchainModule, event: \u0026BlockchainEvent) {\n let module_event = match event {\n BlockchainEvent::StackUpValidBlock(_) =\u003e ModuleEvent::NewValidBlock,\n BlockchainEvent::RevertBlocks(_) =\u003e ModuleEvent::RevertBlocks,\n _ =\u003e return,\n };\n bc.router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Event {\n event_from: ModuleStaticName(MODULE_NAME),\n event_type: module_event,\n event_content: DursEvent::BlockchainEvent(Box::new(event.clone())),\n }))\n .unwrap_or_else(|_| fatal_error!(\"Fail to send BlockchainEvent to router\"));\n}\n","traces":[{"line":24,"address":4211840,"length":1,"stats":{"Line":0}},{"line":25,"address":4211910,"length":1,"stats":{"Line":0}},{"line":26,"address":4211857,"length":1,"stats":{"Line":0}},{"line":27,"address":4211912,"length":1,"stats":{"Line":0}},{"line":28,"address":4211919,"length":1,"stats":{"Line":0}},{"line":30,"address":4211929,"length":1,"stats":{"Line":0}},{"line":36,"address":4756960,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":7},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","fork","fork_algo.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dubp_documents::documents::block::BlockDocumentTrait;\nuse dubp_documents::Blockstamp;\nuse durs_blockchain_dal::entities::fork_tree::ForkTree;\nuse durs_blockchain_dal::{DALError, ForksDBs};\nuse std::collections::HashSet;\n\n/// Number of advance blocks required\npub static ADVANCE_BLOCKS: \u0026'static u32 = \u00263;\n/// Advance blockchain time required (in seconds)\npub static ADVANCE_TIME: \u0026'static u64 = \u0026900;\n\npub fn fork_resolution_algo(\n forks_dbs: \u0026ForksDBs,\n fork_window_size: usize,\n current_blockstamp: Blockstamp,\n invalid_blocks: \u0026HashSet\u003cBlockstamp\u003e,\n) -\u003e Result\u003cOption\u003cVec\u003cBlockstamp\u003e\u003e, DALError\u003e {\n let current_bc_time = forks_dbs.fork_blocks_db.read(|db| {\n db.get(\u0026current_blockstamp)\n .expect(\"safe unwrap\")\n .block\n .common_time()\n })?;\n\n let mut sheets = forks_dbs.fork_tree_db.read(ForkTree::get_sheets)?;\n\n sheets.sort_unstable_by(|s1, s2| s2.1.id.cmp(\u0026s1.1.id));\n\n for sheet in sheets {\n if sheet.1 != current_blockstamp {\n let branch = forks_dbs\n .fork_tree_db\n .read(|fork_tree| fork_tree.get_fork_branch(sheet.0))?;\n\n if branch.is_empty() {\n continue;\n }\n\n let branch_head_blockstamp = branch.last().expect(\"safe unwrap\");\n let branch_head_median_time = forks_dbs.fork_blocks_db.read(|db| {\n db.get(\u0026branch_head_blockstamp)\n .expect(\"safe unwrap\")\n .block\n .common_time()\n })?;\n if branch_head_blockstamp.id.0 \u003e= current_blockstamp.id.0 + *ADVANCE_BLOCKS\n \u0026\u0026 branch_head_median_time \u003e= current_bc_time + *ADVANCE_TIME\n \u0026\u0026 branch[0].id.0 + fork_window_size as u32 \u003e current_blockstamp.id.0\n {\n let mut valid_branch = true;\n for blockstamp in \u0026branch {\n if invalid_blocks.contains(blockstamp) {\n valid_branch = false;\n break;\n }\n }\n\n if valid_branch {\n return Ok(Some(branch));\n }\n }\n }\n }\n\n Ok(None)\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n use crate::*;\n use dubp_documents::documents::block::BlockDocument;\n use dubp_documents::BlockHash;\n use durs_blockchain_dal::entities::block::DALBlock;\n\n #[test]\n fn test_fork_resolution_algo() -\u003e Result\u003c(), DALError\u003e {\n // Get FORK_WINDOW_SIZE value\n let fork_window_size = *dup_currency_params::constants::DEFAULT_FORK_WINDOW_SIZE;\n\n // Open empty databases in memory mode\n let bc_dbs = BlocksV10DBs::open(None);\n let forks_dbs = ForksDBs::open(None);\n\n // Begin with no invalid blocks\n let invalid_blocks: HashSet\u003cBlockstamp\u003e = HashSet::new();\n\n // Generate `FORK_WINDOW_SIZE + 2` mock blocks\n let main_branch: Vec\u003cBlockDocument\u003e =\n dubp_documents_tests_tools::mocks::gen_empty_timed_blocks_v10(\n fork_window_size + 2,\n 0u64,\n );\n\n // Insert mock blocks in forks_dbs\n for block in \u0026main_branch {\n durs_blockchain_dal::writers::block::insert_new_head_block(\n \u0026bc_dbs.blockchain_db,\n \u0026forks_dbs,\n DALBlock {\n block: block.clone(),\n expire_certs: None,\n },\n )?;\n }\n assert_eq!(\n fork_window_size,\n forks_dbs.fork_tree_db.read(|fork_tree| fork_tree.size())?\n );\n assert_eq!(\n fork_window_size,\n forks_dbs.fork_blocks_db.read(|db| db.len())?\n );\n\n // Get current blockstamp\n let mut current_blockstamp = forks_dbs\n .fork_tree_db\n .read(|fork_tree| fork_tree.get_sheets())?\n .get(0)\n .expect(\"must be one sheet\")\n .1;\n\n // Generate 3 fork block\n let fork_point = \u0026main_branch[main_branch.len() - 2];\n let fork_blocks: Vec\u003cBlockDocument\u003e = (0..3)\n .map(|i| {\n BlockDocument::V10(\n dubp_documents_tests_tools::mocks::gen_empty_timed_block_v10(\n Blockstamp {\n id: BlockNumber(fork_point.number().0 + i + 1),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),\n },\n ADVANCE_TIME - 1,\n if i == 0 {\n fork_point.hash().expect(\"safe unwrap\").0\n } else {\n dup_crypto_tests_tools::mocks::hash('A')\n },\n ),\n )\n })\n .collect();\n\n // Add forks blocks into fork tree\n insert_fork_blocks(\u0026forks_dbs, \u0026fork_blocks)?;\n assert_eq!(\n 2,\n forks_dbs\n .fork_tree_db\n .read(|tree| tree.get_sheets().len())?\n );\n\n // Must not fork\n assert_eq!(\n None,\n fork_resolution_algo(\n \u0026forks_dbs,\n fork_window_size,\n current_blockstamp,\n \u0026invalid_blocks\n )?\n );\n\n // Add the determining fork block\n let determining_blockstamp = Blockstamp {\n id: BlockNumber(fork_point.number().0 + 4),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),\n };\n assert_eq!(\n true,\n durs_blockchain_dal::writers::block::insert_new_fork_block(\n \u0026forks_dbs,\n DALBlock {\n block: BlockDocument::V10(\n dubp_documents_tests_tools::mocks::gen_empty_timed_block_v10(\n determining_blockstamp,\n *ADVANCE_TIME,\n dup_crypto_tests_tools::mocks::hash('A'),\n )\n ),\n expire_certs: None,\n },\n )?,\n );\n\n // Must fork\n assert_eq!(\n Some(vec![\n fork_blocks[0].blockstamp(),\n fork_blocks[1].blockstamp(),\n fork_blocks[2].blockstamp(),\n determining_blockstamp,\n ]),\n fork_resolution_algo(\n \u0026forks_dbs,\n fork_window_size,\n current_blockstamp,\n \u0026invalid_blocks\n )?\n );\n current_blockstamp = determining_blockstamp;\n\n // The old main branch catches up and overlaps with the fork\n let new_main_blocks: Vec\u003cBlockDocument\u003e = (0..7)\n .map(|i| {\n BlockDocument::V10(\n dubp_documents_tests_tools::mocks::gen_empty_timed_block_v10(\n Blockstamp {\n id: BlockNumber(fork_point.number().0 + i + 1),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),\n },\n ADVANCE_TIME * 2,\n if i == 0 {\n fork_point.hash().expect(\"safe unwrap\").0\n } else {\n dup_crypto_tests_tools::mocks::hash('B')\n },\n ),\n )\n })\n .collect();\n insert_fork_blocks(\u0026forks_dbs, \u0026new_main_blocks)?;\n\n // Must refork\n assert_eq!(\n Some(new_main_blocks.iter().map(|b| b.blockstamp()).collect()),\n fork_resolution_algo(\n \u0026forks_dbs,\n fork_window_size,\n current_blockstamp,\n \u0026invalid_blocks\n )?\n );\n //current_blockstamp = new_main_blocks.last().expect(\"safe unwrap\").blockstamp();\n\n Ok(())\n }\n\n fn insert_fork_blocks(forks_dbs: \u0026ForksDBs, blocks: \u0026[BlockDocument]) -\u003e Result\u003c(), DALError\u003e {\n for block in blocks {\n assert_eq!(\n true,\n durs_blockchain_dal::writers::block::insert_new_fork_block(\n forks_dbs,\n DALBlock {\n block: block.clone(),\n expire_certs: None,\n },\n )?,\n );\n }\n\n Ok(())\n }\n}\n","traces":[{"line":27,"address":4380032,"length":1,"stats":{"Line":1}},{"line":33,"address":4380066,"length":1,"stats":{"Line":2}},{"line":34,"address":5270478,"length":1,"stats":{"Line":1}},{"line":40,"address":4380535,"length":1,"stats":{"Line":1}},{"line":42,"address":4381007,"length":1,"stats":{"Line":2}},{"line":44,"address":4381054,"length":1,"stats":{"Line":1}},{"line":45,"address":4381469,"length":1,"stats":{"Line":1}},{"line":46,"address":4381533,"length":1,"stats":{"Line":1}},{"line":48,"address":4381549,"length":1,"stats":{"Line":2}},{"line":50,"address":4382050,"length":1,"stats":{"Line":1}},{"line":51,"address":4382084,"length":1,"stats":{"Line":0}},{"line":54,"address":4382089,"length":1,"stats":{"Line":1}},{"line":55,"address":4382200,"length":1,"stats":{"Line":2}},{"line":56,"address":5270702,"length":1,"stats":{"Line":1}},{"line":61,"address":4382591,"length":1,"stats":{"Line":1}},{"line":62,"address":4382717,"length":1,"stats":{"Line":1}},{"line":63,"address":4382657,"length":1,"stats":{"Line":1}},{"line":65,"address":4382895,"length":1,"stats":{"Line":1}},{"line":66,"address":4382903,"length":1,"stats":{"Line":1}},{"line":67,"address":4383093,"length":1,"stats":{"Line":1}},{"line":68,"address":4383147,"length":1,"stats":{"Line":0}},{"line":69,"address":4383155,"length":1,"stats":{"Line":0}},{"line":73,"address":4383122,"length":1,"stats":{"Line":1}},{"line":74,"address":4383162,"length":1,"stats":{"Line":1}},{"line":80,"address":4383334,"length":1,"stats":{"Line":1}},{"line":93,"address":4753872,"length":1,"stats":{"Line":2}},{"line":95,"address":5258964,"length":1,"stats":{"Line":1}},{"line":98,"address":5259042,"length":1,"stats":{"Line":1}},{"line":99,"address":5259093,"length":1,"stats":{"Line":1}},{"line":102,"address":5259140,"length":1,"stats":{"Line":1}},{"line":106,"address":5259211,"length":1,"stats":{"Line":1}},{"line":107,"address":5259162,"length":1,"stats":{"Line":1}},{"line":112,"address":5259265,"length":1,"stats":{"Line":1}},{"line":113,"address":5259670,"length":1,"stats":{"Line":1}},{"line":116,"address":5259565,"length":1,"stats":{"Line":1}},{"line":117,"address":5259493,"length":1,"stats":{"Line":1}},{"line":118,"address":5259553,"length":1,"stats":{"Line":1}},{"line":122,"address":5260080,"length":1,"stats":{"Line":0}},{"line":124,"address":4753920,"length":1,"stats":{"Line":2}},{"line":126,"address":5260946,"length":1,"stats":{"Line":0}},{"line":128,"address":4753952,"length":1,"stats":{"Line":2}},{"line":132,"address":5261720,"length":1,"stats":{"Line":1}},{"line":134,"address":4753984,"length":1,"stats":{"Line":2}},{"line":137,"address":5261820,"length":1,"stats":{"Line":0}},{"line":140,"address":5262345,"length":1,"stats":{"Line":1}},{"line":141,"address":5262432,"length":1,"stats":{"Line":1}},{"line":142,"address":4754032,"length":1,"stats":{"Line":2}},{"line":143,"address":4754513,"length":1,"stats":{"Line":1}},{"line":144,"address":4754477,"length":1,"stats":{"Line":1}},{"line":145,"address":4754225,"length":1,"stats":{"Line":1}},{"line":146,"address":4754051,"length":1,"stats":{"Line":1}},{"line":147,"address":4754142,"length":1,"stats":{"Line":1}},{"line":149,"address":4754303,"length":1,"stats":{"Line":1}},{"line":150,"address":4754324,"length":1,"stats":{"Line":1}},{"line":151,"address":4754331,"length":1,"stats":{"Line":1}},{"line":153,"address":4754458,"length":1,"stats":{"Line":1}},{"line":161,"address":5262530,"length":1,"stats":{"Line":1}},{"line":162,"address":5262942,"length":1,"stats":{"Line":0}},{"line":164,"address":5262850,"length":1,"stats":{"Line":1}},{"line":166,"address":4754592,"length":1,"stats":{"Line":2}},{"line":170,"address":5263881,"length":1,"stats":{"Line":0}},{"line":172,"address":5263785,"length":1,"stats":{"Line":1}},{"line":174,"address":5263699,"length":1,"stats":{"Line":1}},{"line":175,"address":5263707,"length":1,"stats":{"Line":1}},{"line":181,"address":5264724,"length":1,"stats":{"Line":1}},{"line":182,"address":5264618,"length":1,"stats":{"Line":1}},{"line":183,"address":5264668,"length":1,"stats":{"Line":1}},{"line":185,"address":5265404,"length":1,"stats":{"Line":1}},{"line":187,"address":5265088,"length":1,"stats":{"Line":1}},{"line":189,"address":5264978,"length":1,"stats":{"Line":1}},{"line":190,"address":5264901,"length":1,"stats":{"Line":1}},{"line":191,"address":5264858,"length":1,"stats":{"Line":1}},{"line":192,"address":5264770,"length":1,"stats":{"Line":1}},{"line":193,"address":5264816,"length":1,"stats":{"Line":1}},{"line":194,"address":5264826,"length":1,"stats":{"Line":1}},{"line":197,"address":5264958,"length":1,"stats":{"Line":1}},{"line":203,"address":5266370,"length":1,"stats":{"Line":0}},{"line":204,"address":5265646,"length":1,"stats":{"Line":1}},{"line":205,"address":5265671,"length":1,"stats":{"Line":1}},{"line":206,"address":5265933,"length":1,"stats":{"Line":1}},{"line":207,"address":5265988,"length":1,"stats":{"Line":1}},{"line":208,"address":5266030,"length":1,"stats":{"Line":1}},{"line":210,"address":5266363,"length":1,"stats":{"Line":1}},{"line":212,"address":5266277,"length":1,"stats":{"Line":1}},{"line":213,"address":5266285,"length":1,"stats":{"Line":1}},{"line":217,"address":5267223,"length":1,"stats":{"Line":1}},{"line":220,"address":5267315,"length":1,"stats":{"Line":1}},{"line":221,"address":4754704,"length":1,"stats":{"Line":2}},{"line":222,"address":4755185,"length":1,"stats":{"Line":1}},{"line":223,"address":4755149,"length":1,"stats":{"Line":1}},{"line":224,"address":4754897,"length":1,"stats":{"Line":1}},{"line":225,"address":4754723,"length":1,"stats":{"Line":1}},{"line":226,"address":4754814,"length":1,"stats":{"Line":1}},{"line":228,"address":4754975,"length":1,"stats":{"Line":1}},{"line":229,"address":4754996,"length":1,"stats":{"Line":1}},{"line":230,"address":4755003,"length":1,"stats":{"Line":1}},{"line":232,"address":4755130,"length":1,"stats":{"Line":1}},{"line":238,"address":5267413,"length":1,"stats":{"Line":1}},{"line":241,"address":5267938,"length":1,"stats":{"Line":0}},{"line":242,"address":4755264,"length":1,"stats":{"Line":2}},{"line":243,"address":5267931,"length":1,"stats":{"Line":1}},{"line":245,"address":5267845,"length":1,"stats":{"Line":1}},{"line":246,"address":5267853,"length":1,"stats":{"Line":1}},{"line":252,"address":5268726,"length":1,"stats":{"Line":1}},{"line":255,"address":5258000,"length":1,"stats":{"Line":1}},{"line":256,"address":5258028,"length":1,"stats":{"Line":1}},{"line":257,"address":5258559,"length":1,"stats":{"Line":1}},{"line":259,"address":5258349,"length":1,"stats":{"Line":1}},{"line":260,"address":5258203,"length":1,"stats":{"Line":1}},{"line":261,"address":5258264,"length":1,"stats":{"Line":1}},{"line":262,"address":5258208,"length":1,"stats":{"Line":1}},{"line":263,"address":5258252,"length":1,"stats":{"Line":1}},{"line":269,"address":5258239,"length":1,"stats":{"Line":1}}],"covered":103,"coverable":113},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","fork","revert_block.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that applies a block backwards.\n\nuse dubp_documents::documents::block::v10::TxDocOrTxHash;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait, BlockDocumentV10};\nuse dubp_documents::documents::transaction::{TxAmount, TxBase};\nuse dubp_documents::{BlockNumber, Document};\nuse dup_crypto::keys::*;\nuse durs_blockchain_dal::entities::block::DALBlock;\nuse durs_blockchain_dal::entities::sources::SourceAmount;\nuse durs_blockchain_dal::writers::requests::*;\nuse durs_blockchain_dal::writers::transaction::DALTxV10;\nuse durs_blockchain_dal::{BinDB, DALError, TxV10Datas};\nuse durs_common_tools::fatal_error;\nuse durs_wot::data::{NewLinkResult, RemLinkResult};\nuse durs_wot::{NodeId, WebOfTrust};\nuse std::collections::HashMap;\nuse unwrap::unwrap;\n\n#[derive(Debug)]\n/// Stores all queries to apply in database to \"apply\" the block\npub struct ValidBlockRevertReqs(\n pub BlocksDBsWriteQuery,\n pub Vec\u003cWotsDBsWriteQuery\u003e,\n pub Vec\u003cCurrencyDBsWriteQuery\u003e,\n);\n\n#[derive(Debug, Copy, Clone)]\n/// RevertValidBlockError\npub enum RevertValidBlockError {\n ExcludeUnknowNodeId(),\n RevokeUnknowNodeId(),\n DALError(DALError),\n}\n\nimpl From\u003cDALError\u003e for RevertValidBlockError {\n fn from(e: DALError) -\u003e Self {\n RevertValidBlockError::DALError(e)\n }\n}\n\npub fn revert_block\u003cW: WebOfTrust\u003e(\n dal_block: DALBlock,\n wot_index: \u0026mut HashMap\u003cPubKey, NodeId\u003e,\n wot_db: \u0026BinDB\u003cW\u003e,\n txs_db: \u0026BinDB\u003cTxV10Datas\u003e,\n) -\u003e Result\u003cValidBlockRevertReqs, RevertValidBlockError\u003e {\n match dal_block.block {\n BlockDocument::V10(block_v10) =\u003e revert_block_v10(\n block_v10,\n unwrap!(dal_block.expire_certs),\n wot_index,\n wot_db,\n txs_db,\n ),\n }\n}\n\npub fn revert_block_v10\u003cW: WebOfTrust\u003e(\n mut block: BlockDocumentV10,\n expire_certs: HashMap\u003c(NodeId, NodeId), BlockNumber\u003e,\n wot_index: \u0026mut HashMap\u003cPubKey, NodeId\u003e,\n wot_db: \u0026BinDB\u003cW\u003e,\n txs_db: \u0026BinDB\u003cTxV10Datas\u003e,\n) -\u003e Result\u003cValidBlockRevertReqs, RevertValidBlockError\u003e {\n // Get transactions\n let dal_txs: Vec\u003cDALTxV10\u003e = block\n .transactions\n .iter()\n .map(|tx_enum| match *tx_enum {\n TxDocOrTxHash::TxHash(ref tx_hash) =\u003e {\n if let Ok(Some(tx)) = txs_db.read(|db| db.get(tx_hash).cloned()) {\n tx\n } else {\n fatal_error!(\"revert_block(): tx {} not found !\", tx_hash);\n }\n }\n TxDocOrTxHash::TxDoc(ref _dal_tx) =\u003e fatal_error!(\"Try to revert not reduce block !\"),\n })\n .collect();\n\n // Revert reduce block\n block.generate_inner_hash();\n debug!(\n \"BlockchainModule : revert_valid_block({})\",\n block.blockstamp()\n );\n\n // REVERT_CURRENCY_EVENTS\n let mut currency_dbs_requests = Vec::new();\n // Revert transactions\n for dal_tx in dal_txs.iter().rev() {\n currency_dbs_requests.push(CurrencyDBsWriteQuery::RevertTx(Box::new(dal_tx.clone())));\n }\n // Revert UD\n if let Some(du_amount) = block.dividend {\n if du_amount \u003e 0 {\n let members_wot_ids = wot_db\n .read(WebOfTrust::get_enabled)\n .expect(\"Fail to read WotDB\");\n let mut members_pubkeys = Vec::new();\n for (pubkey, wot_id) in wot_index.iter() {\n if members_wot_ids.contains(wot_id) {\n members_pubkeys.push(*pubkey);\n }\n }\n currency_dbs_requests.push(CurrencyDBsWriteQuery::RevertUD(\n SourceAmount(TxAmount(du_amount as isize), TxBase(block.unit_base)),\n block.number,\n members_pubkeys,\n ));\n }\n }\n // REVERT WOT EVENTS\n let mut wot_dbs_requests = Vec::new();\n // Revert expire_certs\n if !expire_certs.is_empty() {\n for ((source, target), created_block_id) in \u0026expire_certs {\n wot_db\n .write(|db| {\n let result = db.add_link(*source, *target);\n match result {\n NewLinkResult::Ok(_) =\u003e {}\n _ =\u003e fatal_error!(\n \"Fail to add_link {}-\u003e{} : {:?}\",\n source.0,\n target.0,\n result\n ),\n }\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertExpireCert(\n *source,\n *target,\n *created_block_id,\n ));\n }\n }\n // Revert certifications\n for certification in block.certifications.clone() {\n trace!(\"stack_up_valid_block: apply cert...\");\n let compact_cert = certification.to_compact_document();\n let wot_node_from = wot_index[\u0026compact_cert.issuer];\n let wot_node_to = wot_index[\u0026compact_cert.target];\n wot_db\n .write(|db| {\n let result = db.rem_link(wot_node_from, wot_node_to);\n match result {\n RemLinkResult::Removed(_) =\u003e {}\n _ =\u003e fatal_error!(\n \"Fail to rem_link {}-\u003e{} : {:?}\",\n wot_node_from.0,\n wot_node_to.0,\n result\n ),\n }\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertCert(\n compact_cert,\n wot_node_from,\n wot_node_to,\n ));\n trace!(\"stack_up_valid_block: apply cert...success.\");\n }\n // Revert revocations\n for revocation in block.revoked.clone() {\n let compact_revoc = revocation.to_compact_document();\n let wot_id = if let Some(wot_id) = wot_index.get(\u0026compact_revoc.issuer) {\n wot_id\n } else {\n return Err(RevertValidBlockError::RevokeUnknowNodeId());\n };\n wot_db\n .write(|db| {\n db.set_enabled(*wot_id, false);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertRevokeIdentity(\n compact_revoc.issuer,\n block.blockstamp(),\n true,\n ));\n }\n // Revert exclusions\n for exclusion in block.excluded.clone() {\n let wot_id = if let Some(wot_id) = wot_index.get(\u0026exclusion) {\n wot_id\n } else {\n return Err(RevertValidBlockError::ExcludeUnknowNodeId());\n };\n wot_db\n .write(|db| {\n db.set_enabled(*wot_id, false);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertExcludeIdentity(\n exclusion,\n block.blockstamp(),\n ));\n }\n // List block identities\n let mut identities = HashMap::with_capacity(block.identities.len());\n for identity in block.identities.clone() {\n identities.insert(identity.issuers()[0], identity);\n }\n // Revert renewals\n for active in block.actives.clone() {\n let pubkey = active.issuers()[0];\n if !identities.contains_key(\u0026pubkey) {\n let wot_id = wot_index[\u0026pubkey];\n wot_db\n .write(|db| {\n db.set_enabled(wot_id, true);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertRenewalIdentity(\n pubkey,\n wot_id,\n block.median_time,\n active.blockstamp().id,\n ));\n }\n }\n // Revert joiners\n for joiner in block.joiners.iter().rev() {\n let pubkey = joiner.clone().issuers()[0];\n if let Some(_idty_doc) = identities.get(\u0026pubkey) {\n // Newcomer\n wot_db\n .write(|db| {\n db.rem_node();\n })\n .expect(\"Fail to write in WotDB\");\n wot_index.remove(\u0026pubkey);\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertCreateIdentity(pubkey));\n } else {\n // Renewer\n let wot_id = wot_index[\u0026joiner.issuers()[0]];\n wot_db\n .write(|db| {\n db.set_enabled(wot_id, true);\n })\n .expect(\"Fail to write in WotDB\");\n wot_dbs_requests.push(WotsDBsWriteQuery::RevertRenewalIdentity(\n joiner.issuers()[0],\n wot_id,\n block.common_time(),\n joiner.blockstamp().id,\n ));\n }\n }\n // Return DBs requests\n Ok(ValidBlockRevertReqs(\n BlocksDBsWriteQuery::RevertBlock(DALBlock {\n block: BlockDocument::V10(block),\n expire_certs: Some(expire_certs),\n }),\n wot_dbs_requests,\n currency_dbs_requests,\n ))\n}\n","traces":[{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":4279648,"length":1,"stats":{"Line":0}},{"line":64,"address":4279737,"length":1,"stats":{"Line":0}},{"line":65,"address":4279780,"length":1,"stats":{"Line":0}},{"line":66,"address":4279932,"length":1,"stats":{"Line":0}},{"line":67,"address":4279937,"length":1,"stats":{"Line":0}},{"line":68,"address":4279942,"length":1,"stats":{"Line":0}},{"line":73,"address":4280080,"length":1,"stats":{"Line":0}},{"line":81,"address":4280120,"length":1,"stats":{"Line":0}},{"line":84,"address":4280371,"length":1,"stats":{"Line":0}},{"line":85,"address":4291469,"length":1,"stats":{"Line":0}},{"line":86,"address":4291360,"length":1,"stats":{"Line":0}},{"line":87,"address":4291723,"length":1,"stats":{"Line":0}},{"line":89,"address":4291793,"length":1,"stats":{"Line":0}},{"line":92,"address":4293289,"length":1,"stats":{"Line":0}},{"line":97,"address":4280441,"length":1,"stats":{"Line":0}},{"line":98,"address":4280460,"length":1,"stats":{"Line":0}},{"line":100,"address":4280709,"length":1,"stats":{"Line":0}},{"line":104,"address":4280907,"length":1,"stats":{"Line":0}},{"line":106,"address":4280914,"length":1,"stats":{"Line":0}},{"line":107,"address":4281246,"length":1,"stats":{"Line":0}},{"line":110,"address":4281277,"length":1,"stats":{"Line":0}},{"line":111,"address":4281444,"length":1,"stats":{"Line":0}},{"line":112,"address":4281459,"length":1,"stats":{"Line":0}},{"line":115,"address":4281517,"length":1,"stats":{"Line":0}},{"line":116,"address":4281536,"length":1,"stats":{"Line":0}},{"line":117,"address":4281864,"length":1,"stats":{"Line":0}},{"line":118,"address":4282172,"length":1,"stats":{"Line":0}},{"line":121,"address":4282016,"length":1,"stats":{"Line":0}},{"line":122,"address":4281902,"length":1,"stats":{"Line":0}},{"line":123,"address":4281970,"length":1,"stats":{"Line":0}},{"line":124,"address":4281976,"length":1,"stats":{"Line":0}},{"line":129,"address":4282282,"length":1,"stats":{"Line":0}},{"line":131,"address":4282289,"length":1,"stats":{"Line":0}},{"line":132,"address":4282337,"length":1,"stats":{"Line":0}},{"line":133,"address":4282644,"length":1,"stats":{"Line":0}},{"line":147,"address":4282780,"length":1,"stats":{"Line":0}},{"line":148,"address":4282748,"length":1,"stats":{"Line":0}},{"line":149,"address":4282759,"length":1,"stats":{"Line":0}},{"line":150,"address":4282770,"length":1,"stats":{"Line":0}},{"line":155,"address":4282847,"length":1,"stats":{"Line":0}},{"line":156,"address":4283307,"length":1,"stats":{"Line":0}},{"line":157,"address":4283605,"length":1,"stats":{"Line":0}},{"line":158,"address":4283612,"length":1,"stats":{"Line":0}},{"line":159,"address":4283662,"length":1,"stats":{"Line":0}},{"line":160,"address":4283712,"length":1,"stats":{"Line":0}},{"line":174,"address":4283887,"length":1,"stats":{"Line":0}},{"line":175,"address":4283814,"length":1,"stats":{"Line":0}},{"line":176,"address":4283871,"length":1,"stats":{"Line":0}},{"line":177,"address":4283879,"length":1,"stats":{"Line":0}},{"line":179,"address":4284020,"length":1,"stats":{"Line":0}},{"line":182,"address":4284346,"length":1,"stats":{"Line":0}},{"line":183,"address":4290591,"length":1,"stats":{"Line":0}},{"line":184,"address":4284805,"length":1,"stats":{"Line":0}},{"line":185,"address":4284914,"length":1,"stats":{"Line":0}},{"line":187,"address":4284980,"length":1,"stats":{"Line":0}},{"line":189,"address":4284930,"length":1,"stats":{"Line":0}},{"line":194,"address":4285291,"length":1,"stats":{"Line":0}},{"line":195,"address":4285222,"length":1,"stats":{"Line":0}},{"line":196,"address":4285284,"length":1,"stats":{"Line":0}},{"line":201,"address":4285458,"length":1,"stats":{"Line":0}},{"line":202,"address":4285874,"length":1,"stats":{"Line":0}},{"line":203,"address":4285983,"length":1,"stats":{"Line":0}},{"line":205,"address":4286046,"length":1,"stats":{"Line":0}},{"line":207,"address":4285999,"length":1,"stats":{"Line":0}},{"line":212,"address":4286195,"length":1,"stats":{"Line":0}},{"line":213,"address":4286126,"length":1,"stats":{"Line":0}},{"line":214,"address":4286188,"length":1,"stats":{"Line":0}},{"line":218,"address":4286331,"length":1,"stats":{"Line":0}},{"line":219,"address":4286390,"length":1,"stats":{"Line":0}},{"line":220,"address":4286868,"length":1,"stats":{"Line":0}},{"line":223,"address":4287128,"length":1,"stats":{"Line":0}},{"line":224,"address":4287595,"length":1,"stats":{"Line":0}},{"line":225,"address":4287709,"length":1,"stats":{"Line":0}},{"line":226,"address":4287741,"length":1,"stats":{"Line":0}},{"line":227,"address":4287791,"length":1,"stats":{"Line":0}},{"line":232,"address":4288008,"length":1,"stats":{"Line":0}},{"line":233,"address":4287869,"length":1,"stats":{"Line":0}},{"line":234,"address":4287915,"length":1,"stats":{"Line":0}},{"line":235,"address":4287931,"length":1,"stats":{"Line":0}},{"line":236,"address":4287935,"length":1,"stats":{"Line":0}},{"line":241,"address":4288162,"length":1,"stats":{"Line":0}},{"line":242,"address":4288497,"length":1,"stats":{"Line":0}},{"line":243,"address":4289367,"length":1,"stats":{"Line":0}},{"line":245,"address":4289446,"length":1,"stats":{"Line":0}},{"line":250,"address":4289500,"length":1,"stats":{"Line":0}},{"line":251,"address":4289539,"length":1,"stats":{"Line":0}},{"line":254,"address":4289667,"length":1,"stats":{"Line":0}},{"line":255,"address":4289770,"length":1,"stats":{"Line":0}},{"line":260,"address":4290006,"length":1,"stats":{"Line":0}},{"line":261,"address":4289848,"length":1,"stats":{"Line":0}},{"line":262,"address":4289935,"length":1,"stats":{"Line":0}},{"line":263,"address":4289943,"length":1,"stats":{"Line":0}},{"line":264,"address":4289972,"length":1,"stats":{"Line":0}},{"line":269,"address":4289042,"length":1,"stats":{"Line":0}},{"line":270,"address":4288777,"length":1,"stats":{"Line":0}},{"line":271,"address":4288523,"length":1,"stats":{"Line":0}},{"line":272,"address":4288642,"length":1,"stats":{"Line":0}},{"line":274,"address":4288941,"length":1,"stats":{"Line":0}},{"line":275,"address":4288981,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":101},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","fork","rollback.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::fork::revert_block::ValidBlockRevertReqs;\nuse crate::*;\nuse dubp_documents::Blockstamp;\nuse durs_common_tools::fatal_error;\nuse unwrap::unwrap;\n\npub fn apply_rollback(bc: \u0026mut BlockchainModule, new_bc_branch: Vec\u003cBlockstamp\u003e) {\n if new_bc_branch.is_empty() {\n return;\n }\n\n let old_current_blockstamp = bc.current_blockstamp;\n let last_common_block_number = new_bc_branch[0].id.0;\n\n // Rollback (revert old branch)\n while bc.current_blockstamp.id.0 \u003e last_common_block_number {\n if let Some(dal_block) = bc\n .forks_dbs\n .fork_blocks_db\n .read(|db| db.get(\u0026bc.current_blockstamp).cloned())\n .unwrap_or_else(|_| {\n fatal_error!(\"revert block {} fail !\", bc.current_blockstamp);\n })\n {\n let blockstamp = dal_block.block.blockstamp();\n let ValidBlockRevertReqs(bc_db_query, wot_dbs_queries, tx_dbs_queries) =\n super::revert_block::revert_block(\n dal_block,\n \u0026mut bc.wot_index,\n \u0026bc.wot_databases.wot_db,\n \u0026bc.currency_databases.tx_db,\n )\n .unwrap_or_else(|_| {\n fatal_error!(\"revert block {} fail !\", bc.current_blockstamp);\n });\n // Apply db requests\n bc_db_query\n .apply(\n \u0026bc.blocks_databases.blockchain_db,\n \u0026bc.forks_dbs,\n unwrap!(bc.currency_params).fork_window_size,\n None,\n )\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\");\n for query in \u0026wot_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026unwrap!(bc.currency_params), \u0026bc.wot_databases)\n .expect(\"Fatal error : Fail to apply WotsDBsWriteRequest !\");\n }\n for query in \u0026tx_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026bc.currency_databases)\n .expect(\"Fatal error : Fail to apply CurrencyDBsWriteRequest !\");\n }\n } else {\n fatal_error!(\"apply_rollback(): Not found current block in forks blocks DB !\");\n }\n }\n\n // Apply new branch\n let mut new_branch_is_valid = true;\n for blockstamp in \u0026new_bc_branch {\n if let Ok(Some(dal_block)) = bc\n .forks_dbs\n .fork_blocks_db\n .read(|db| db.get(\u0026blockstamp).cloned())\n {\n if let Ok(CheckAndApplyBlockReturn::ValidMainBlock(ValidBlockApplyReqs(\n bc_db_query,\n wot_dbs_queries,\n tx_dbs_queries,\n ))) = check_and_apply_block(bc, dal_block.block)\n {\n bc.current_blockstamp = *blockstamp;\n // Apply db requests\n bc_db_query\n .apply(\n \u0026bc.blocks_databases.blockchain_db,\n \u0026bc.forks_dbs,\n unwrap!(bc.currency_params).fork_window_size,\n None,\n )\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\");\n for query in \u0026wot_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026unwrap!(bc.currency_params), \u0026bc.wot_databases)\n .expect(\"Fatal error : Fail to apply WotsDBsWriteRequest !\");\n }\n for query in \u0026tx_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026bc.currency_databases)\n .expect(\"Fatal error : Fail to apply CurrencyDBsWriteRequest !\");\n }\n } else {\n new_branch_is_valid = false;\n bc.invalid_forks.insert(*blockstamp);\n break;\n }\n } else {\n fatal_error!(\n \"apply_rollback(): Fail to get block {} on new branch in forks blocks DB !\",\n blockstamp\n );\n }\n }\n\n if new_branch_is_valid {\n // update main branch in fork tree\n if let Err(err) = durs_blockchain_dal::writers::fork_tree::change_main_branch(\n \u0026bc.forks_dbs,\n old_current_blockstamp,\n bc.current_blockstamp,\n ) {\n fatal_error!(\"DALError: ForksDB: {:?}\", err);\n }\n\n // save dbs\n bc.blocks_databases.save_dbs();\n bc.forks_dbs.save_dbs();\n bc.wot_databases.save_dbs();\n bc.currency_databases.save_dbs(true, true);\n // Send events stackUpValidBlock\n let new_branch_blocks: Vec\u003cBlockDocument\u003e = new_bc_branch\n .into_iter()\n .map(|blockstamp| {\n bc.forks_dbs\n .fork_blocks_db\n .read(|db| db.get(\u0026blockstamp).cloned())\n .expect(\"safe unwrap\")\n .expect(\"safe unwrap\")\n .block\n })\n .collect();\n for block in new_branch_blocks {\n events::sent::send_event(bc, \u0026BlockchainEvent::StackUpValidBlock(Box::new(block)))\n }\n } else {\n // reload dbs\n let dbs_path = durs_conf::get_blockchain_db_path(bc.profile_path.clone());\n bc.blocks_databases = BlocksV10DBs::open(Some(\u0026dbs_path));\n bc.forks_dbs = ForksDBs::open(Some(\u0026dbs_path));\n bc.wot_databases = WotsV10DBs::open(Some(\u0026dbs_path));\n bc.currency_databases = CurrencyV10DBs::open(Some(\u0026dbs_path));\n }\n}\n","traces":[{"line":22,"address":5395056,"length":1,"stats":{"Line":0}},{"line":23,"address":5395077,"length":1,"stats":{"Line":0}},{"line":27,"address":5395279,"length":1,"stats":{"Line":0}},{"line":28,"address":5395330,"length":1,"stats":{"Line":0}},{"line":31,"address":5395378,"length":1,"stats":{"Line":0}},{"line":32,"address":5395447,"length":1,"stats":{"Line":0}},{"line":35,"address":4300144,"length":1,"stats":{"Line":0}},{"line":36,"address":4300240,"length":1,"stats":{"Line":0}},{"line":37,"address":4300255,"length":1,"stats":{"Line":0}},{"line":40,"address":5395685,"length":1,"stats":{"Line":0}},{"line":41,"address":5395920,"length":1,"stats":{"Line":0}},{"line":42,"address":5395828,"length":1,"stats":{"Line":0}},{"line":43,"address":5395720,"length":1,"stats":{"Line":0}},{"line":44,"address":5395769,"length":1,"stats":{"Line":0}},{"line":45,"address":5395787,"length":1,"stats":{"Line":0}},{"line":46,"address":5395797,"length":1,"stats":{"Line":0}},{"line":48,"address":4298656,"length":1,"stats":{"Line":0}},{"line":49,"address":4298687,"length":1,"stats":{"Line":0}},{"line":52,"address":5396049,"length":1,"stats":{"Line":0}},{"line":60,"address":5396467,"length":1,"stats":{"Line":0}},{"line":61,"address":5396670,"length":1,"stats":{"Line":0}},{"line":65,"address":5396858,"length":1,"stats":{"Line":0}},{"line":66,"address":5397172,"length":1,"stats":{"Line":0}},{"line":70,"address":5396296,"length":1,"stats":{"Line":0}},{"line":71,"address":5397340,"length":1,"stats":{"Line":0}},{"line":76,"address":5395401,"length":1,"stats":{"Line":0}},{"line":77,"address":5395409,"length":1,"stats":{"Line":0}},{"line":78,"address":5398652,"length":1,"stats":{"Line":0}},{"line":81,"address":4301792,"length":1,"stats":{"Line":0}},{"line":83,"address":5398992,"length":1,"stats":{"Line":0}},{"line":84,"address":5399036,"length":1,"stats":{"Line":0}},{"line":85,"address":5399101,"length":1,"stats":{"Line":0}},{"line":86,"address":5399149,"length":1,"stats":{"Line":0}},{"line":87,"address":5398822,"length":1,"stats":{"Line":0}},{"line":89,"address":5399197,"length":1,"stats":{"Line":0}},{"line":91,"address":5399293,"length":1,"stats":{"Line":0}},{"line":99,"address":5399749,"length":1,"stats":{"Line":0}},{"line":100,"address":5399954,"length":1,"stats":{"Line":0}},{"line":104,"address":5400143,"length":1,"stats":{"Line":0}},{"line":105,"address":5400457,"length":1,"stats":{"Line":0}},{"line":110,"address":5400568,"length":1,"stats":{"Line":0}},{"line":111,"address":5400576,"length":1,"stats":{"Line":0}},{"line":112,"address":5405839,"length":1,"stats":{"Line":0}},{"line":114,"address":5399648,"length":1,"stats":{"Line":0}},{"line":115,"address":5400816,"length":1,"stats":{"Line":0}},{"line":122,"address":5398710,"length":1,"stats":{"Line":0}},{"line":124,"address":5402410,"length":1,"stats":{"Line":0}},{"line":125,"address":5402299,"length":1,"stats":{"Line":0}},{"line":126,"address":5402313,"length":1,"stats":{"Line":0}},{"line":127,"address":5402359,"length":1,"stats":{"Line":0}},{"line":129,"address":5402542,"length":1,"stats":{"Line":0}},{"line":133,"address":5404045,"length":1,"stats":{"Line":0}},{"line":134,"address":5404071,"length":1,"stats":{"Line":0}},{"line":135,"address":5404099,"length":1,"stats":{"Line":0}},{"line":136,"address":5404127,"length":1,"stats":{"Line":0}},{"line":138,"address":5404162,"length":1,"stats":{"Line":0}},{"line":140,"address":4301952,"length":1,"stats":{"Line":0}},{"line":141,"address":4301967,"length":1,"stats":{"Line":0}},{"line":143,"address":4301872,"length":1,"stats":{"Line":0}},{"line":149,"address":5404298,"length":1,"stats":{"Line":0}},{"line":150,"address":5404739,"length":1,"stats":{"Line":0}},{"line":154,"address":5404848,"length":1,"stats":{"Line":0}},{"line":155,"address":5404913,"length":1,"stats":{"Line":0}},{"line":156,"address":5404991,"length":1,"stats":{"Line":0}},{"line":157,"address":5405018,"length":1,"stats":{"Line":0}},{"line":158,"address":5405045,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":66},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","fork","stackable_blocks.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that finds and applies the orphaned blocks that have become stackable on the local blockchain.\n\nuse crate::*;\nuse dubp_documents::documents::block::BlockDocumentTrait;\nuse unwrap::unwrap;\n\npub fn apply_stackable_blocks(bc: \u0026mut BlockchainModule) {\n 'blockchain: loop {\n let stackable_blocks = durs_blockchain_dal::readers::fork_tree::get_stackables_blocks(\n \u0026bc.forks_dbs,\n \u0026bc.current_blockstamp,\n )\n .expect(\"Fatal error : Fail to read ForksDB !\");\n if stackable_blocks.is_empty() {\n break 'blockchain;\n } else {\n for stackable_block in stackable_blocks {\n debug!(\"stackable_block({})\", stackable_block.block.number());\n\n let stackable_block_number = stackable_block.block.number();\n let stackable_block_blockstamp = stackable_block.block.blockstamp();\n\n if let Ok(CheckAndApplyBlockReturn::ValidMainBlock(ValidBlockApplyReqs(\n bc_db_query,\n wot_dbs_queries,\n tx_dbs_queries,\n ))) = check_and_apply_block(bc, stackable_block.block)\n {\n let new_current_block = bc_db_query.get_block_doc_copy();\n let blockstamp = new_current_block.blockstamp();\n // Apply db requests\n bc_db_query\n .apply(\n \u0026bc.blocks_databases.blockchain_db,\n \u0026bc.forks_dbs,\n unwrap!(bc.currency_params).fork_window_size,\n None,\n )\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\");\n for query in \u0026wot_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026unwrap!(bc.currency_params), \u0026bc.wot_databases)\n .expect(\"Fatal error : Fail to apply WotsDBsWriteRequest !\");\n }\n for query in \u0026tx_dbs_queries {\n query\n .apply(\u0026blockstamp, \u0026bc.currency_databases)\n .expect(\"Fatal error : Fail to apply CurrencyDBsWriteRequest !\");\n }\n debug!(\"success to stackable_block({})\", stackable_block_number);\n\n bc.current_blockstamp = stackable_block_blockstamp;\n events::sent::send_event(\n bc,\n \u0026BlockchainEvent::StackUpValidBlock(Box::new(new_current_block)),\n );\n continue 'blockchain;\n } else {\n warn!(\"fail to stackable_block({})\", stackable_block_number);\n }\n }\n // Save databases\n bc.blocks_databases.save_dbs();\n bc.forks_dbs.save_dbs();\n bc.wot_databases.save_dbs();\n bc.currency_databases.save_dbs(true, true);\n break 'blockchain;\n }\n }\n}\n","traces":[{"line":22,"address":4302672,"length":1,"stats":{"Line":0}},{"line":24,"address":4302693,"length":1,"stats":{"Line":0}},{"line":25,"address":4302781,"length":1,"stats":{"Line":0}},{"line":26,"address":4302795,"length":1,"stats":{"Line":0}},{"line":28,"address":4302883,"length":1,"stats":{"Line":0}},{"line":29,"address":4302899,"length":1,"stats":{"Line":0}},{"line":32,"address":4302962,"length":1,"stats":{"Line":0}},{"line":33,"address":4303436,"length":1,"stats":{"Line":0}},{"line":35,"address":4303852,"length":1,"stats":{"Line":0}},{"line":36,"address":4303880,"length":1,"stats":{"Line":0}},{"line":38,"address":4304054,"length":1,"stats":{"Line":0}},{"line":39,"address":4304102,"length":1,"stats":{"Line":0}},{"line":40,"address":4304159,"length":1,"stats":{"Line":0}},{"line":41,"address":4304207,"length":1,"stats":{"Line":0}},{"line":42,"address":4303907,"length":1,"stats":{"Line":0}},{"line":44,"address":4304255,"length":1,"stats":{"Line":0}},{"line":45,"address":4304337,"length":1,"stats":{"Line":0}},{"line":47,"address":4304399,"length":1,"stats":{"Line":0}},{"line":55,"address":4304768,"length":1,"stats":{"Line":0}},{"line":56,"address":4304973,"length":1,"stats":{"Line":0}},{"line":60,"address":4305161,"length":1,"stats":{"Line":0}},{"line":61,"address":4305475,"length":1,"stats":{"Line":0}},{"line":65,"address":4305526,"length":1,"stats":{"Line":0}},{"line":67,"address":4305955,"length":1,"stats":{"Line":0}},{"line":69,"address":4306052,"length":1,"stats":{"Line":0}},{"line":70,"address":4306060,"length":1,"stats":{"Line":0}},{"line":72,"address":4306365,"length":1,"stats":{"Line":0}},{"line":73,"address":4306255,"length":1,"stats":{"Line":0}},{"line":74,"address":4306370,"length":1,"stats":{"Line":0}},{"line":78,"address":4306837,"length":1,"stats":{"Line":0}},{"line":79,"address":4306863,"length":1,"stats":{"Line":0}},{"line":80,"address":4306891,"length":1,"stats":{"Line":0}},{"line":81,"address":4306919,"length":1,"stats":{"Line":0}},{"line":82,"address":4306954,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":34},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module managing the Duniter blockchain.\n\n#![allow(clippy::large_enum_variant)]\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n//#[macro_use]\n//extern crate failure;\n#[macro_use]\nextern crate log;\n\nmod constants;\nmod dbex;\nmod dubp;\nmod dunp;\nmod events;\nmod fork;\nmod requests;\nmod responses;\nmod sync;\n\nuse std::collections::{HashMap, HashSet};\nuse std::path::PathBuf;\nuse std::str;\nuse std::sync::mpsc;\nuse std::time::{Duration, SystemTime, UNIX_EPOCH};\n\nuse crate::constants::*;\npub use crate::dbex::{DBExQuery, DBExTxQuery, DBExWotQuery};\nuse crate::dubp::apply::ValidBlockApplyReqs;\nuse crate::dubp::*;\nuse crate::fork::*;\nuse dubp_documents::documents::block::BlockDocument;\nuse dubp_documents::*;\nuse dup_crypto::keys::*;\nuse dup_currency_params::{CurrencyName, CurrencyParameters};\nuse durs_blockchain_dal::*;\nuse durs_common_tools::fatal_error;\nuse durs_message::events::*;\nuse durs_message::requests::*;\nuse durs_message::responses::*;\nuse durs_message::*;\nuse durs_module::*;\nuse durs_network::{\n cli::sync::SyncOpt,\n events::NetworkEvent,\n requests::{NetworkResponse, OldNetworkRequest},\n};\n// use durs_wot::data::rusty::RustyWebOfTrust;\nuse durs_wot::operations::distance::RustyDistanceCalculator;\nuse durs_wot::NodeId;\nuse failure::Error;\n\n/// The blocks are requested by packet groups. This constant sets the block packet size.\npub static CHUNK_SIZE: \u0026'static u32 = \u002650;\n/// Necessary to instantiate the wot object before knowing the currency parameters\npub static INFINITE_SIG_STOCK: \u0026'static usize = \u00264_000_000_000;\n/// The blocks are requested by packet groups. This constant sets the number of packets per group.\npub static MAX_BLOCKS_REQUEST: \u0026'static u32 = \u0026500;\n/// The distance calculator\npub static DISTANCE_CALCULATOR: \u0026'static RustyDistanceCalculator = \u0026RustyDistanceCalculator {};\n\n/// Blockchain Module\n#[derive(Debug)]\npub struct BlockchainModule {\n /// Router sender\n pub router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ///Path to the user datas profile\n pub profile_path: PathBuf,\n /// Currency\n pub currency: Option\u003cCurrencyName\u003e,\n /// Blocks Databases\n pub blocks_databases: BlocksV10DBs,\n /// Forks Databases\n pub forks_dbs: ForksDBs,\n /// Wot index\n pub wot_index: HashMap\u003cPubKey, NodeId\u003e,\n /// Wots Databases\n pub wot_databases: WotsV10DBs,\n /// Currency databases\n currency_databases: CurrencyV10DBs,\n /// Currency parameters\n pub currency_params: Option\u003cCurrencyParameters\u003e,\n /// Current blockstamp\n pub current_blockstamp: Blockstamp,\n /// network consensus blockstamp\n pub consensus: Blockstamp,\n /// The block under construction\n pub pending_block: Option\u003cBox\u003cBlockDocument\u003e\u003e,\n /// Memorization of fork whose application fails\n pub invalid_forks: HashSet\u003cBlockstamp\u003e,\n /// pending network requests\n pub pending_network_requests: HashMap\u003cModuleReqId, OldNetworkRequest\u003e,\n /// Last request blocks\n pub last_request_blocks: SystemTime,\n}\n\n#[derive(Debug, Clone)]\n/// Block\npub enum Block {\n /// Block coming from Network\n NetworkBlock(BlockDocument),\n /// Block coming from local database\n LocalBlock(BlockDocument),\n}\n\nimpl Block {\n /// Into block document\n pub fn into_doc(self) -\u003e BlockDocument {\n match self {\n Block::NetworkBlock(block) =\u003e block,\n Block::LocalBlock(block) =\u003e block,\n }\n }\n /// Get block document ref\n pub fn get_doc_ref(\u0026self) -\u003e \u0026BlockDocument {\n match *self {\n Block::NetworkBlock(ref block) =\u003e block,\n Block::LocalBlock(ref block) =\u003e block,\n }\n }\n /// Return blockstamp\n pub fn blockstamp(\u0026self) -\u003e Blockstamp {\n match *self {\n Block::NetworkBlock(ref block) =\u003e block.blockstamp(),\n Block::LocalBlock(ref block) =\u003e block.blockstamp(),\n }\n }\n /// Is from network ?\n pub fn is_from_network(\u0026self) -\u003e bool {\n match *self {\n Block::NetworkBlock(_) =\u003e true,\n _ =\u003e false,\n }\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n/// When synchronizing the blockchain, checking all rules at each block really takes a long time.\n/// The user is therefore offered a fast synchronization that checks only what is strictly necessary for indexing the data.\npub enum SyncVerificationLevel {\n /// Fast sync, checks only what is strictly necessary for indexing the data.\n FastSync(),\n /// Cautious sync, checking all protocol rules (really takes a long time).\n Cautious(),\n}\n\nimpl BlockchainModule {\n /// Return module identifier\n pub fn name() -\u003e ModuleStaticName {\n ModuleStaticName(MODULE_NAME)\n }\n /// Loading blockchain configuration\n pub fn load_blockchain_conf(\n router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n profile_path: PathBuf,\n _keys: RequiredKeysContent,\n ) -\u003e BlockchainModule {\n // Get db path\n let dbs_path = durs_conf::get_blockchain_db_path(profile_path.clone());\n\n // Open databases\n let blocks_databases = BlocksV10DBs::open(Some(\u0026dbs_path));\n let forks_dbs = ForksDBs::open(Some(\u0026dbs_path));\n let wot_databases = WotsV10DBs::open(Some(\u0026dbs_path));\n let currency_databases = CurrencyV10DBs::open(Some(\u0026dbs_path));\n\n // Get current blockstamp\n let current_blockstamp =\n durs_blockchain_dal::readers::block::get_current_blockstamp(\u0026blocks_databases)\n .expect(\"Fatal error : fail to read Blockchain DB !\")\n .unwrap_or_default();\n\n // Get currency parameters\n let (currency_name, currency_params) = if let Some((currency_name, currency_params)) =\n dup_currency_params::db::get_currency_params(durs_conf::get_datas_path(\n profile_path.clone(),\n ))\n .expect(\"Fatal error : fail to read Blockchain DB !\")\n {\n (Some(currency_name), Some(currency_params))\n } else {\n (None, None)\n };\n\n // Get wot index\n let wot_index: HashMap\u003cPubKey, NodeId\u003e =\n readers::identity::get_wot_index(\u0026wot_databases.identities_db)\n .expect(\"Fatal eror : get_wot_index : Fail to read blockchain databases\");\n\n // Instanciate BlockchainModule\n BlockchainModule {\n router_sender,\n profile_path,\n currency: currency_name,\n currency_params,\n current_blockstamp,\n consensus: Blockstamp::default(),\n blocks_databases,\n forks_dbs,\n wot_index,\n wot_databases,\n currency_databases,\n pending_block: None,\n invalid_forks: HashSet::new(),\n pending_network_requests: HashMap::new(),\n last_request_blocks: UNIX_EPOCH,\n }\n }\n /// Databases explorer\n pub fn dbex(profile_path: PathBuf, csv: bool, req: \u0026DBExQuery) {\n dbex::dbex(profile_path, csv, req);\n }\n /// Synchronize blockchain from local duniter json files\n pub fn local_sync\u003cDC: DursConfTrait\u003e(\n conf: \u0026DC,\n currency_name: Option\u003c\u0026CurrencyName\u003e,\n profile_path: PathBuf,\n sync_opts: SyncOpt,\n ) -\u003e Result\u003c(), Error\u003e {\n Ok(sync::local_sync(\n conf,\n currency_name,\n profile_path,\n sync_opts,\n )?)\n }\n /// Start blockchain module.\n pub fn start_blockchain(\n \u0026mut self,\n blockchain_receiver: \u0026mpsc::Receiver\u003cDursMsg\u003e,\n sync_opts: Option\u003cSyncOpt\u003e,\n ) {\n info!(\"BlockchainModule::start_blockchain()\");\n\n // Send currency parameters to other modules\n if let Some(currency_params) = self.currency_params {\n events::sent::send_event(self, \u0026BlockchainEvent::CurrencyParameters(currency_params));\n }\n\n if let Some(_sync_opts) = sync_opts {\n // TODO ...\n } else {\n // Start main loop\n self.main_loop(blockchain_receiver);\n }\n }\n\n /// Start blockchain main loop\n pub fn main_loop(\u0026mut self, blockchain_receiver: \u0026mpsc::Receiver\u003cDursMsg\u003e) {\n // Init main loop datas\n let mut last_get_stackables_blocks = UNIX_EPOCH;\n\n loop {\n // Request Consensus\n requests::sent::request_network_consensus(self);\n // Request next main blocks\n requests::sent::request_next_main_blocks(self);\n match blockchain_receiver.recv_timeout(Duration::from_millis(1000)) {\n Ok(durs_message) =\u003e {\n match durs_message {\n DursMsg::Request {\n req_from,\n req_id,\n req_content,\n ..\n } =\u003e {\n requests::received::receive_req(self, req_from, req_id, req_content);\n }\n DursMsg::Event {\n event_type,\n event_content,\n ..\n } =\u003e events::received::receive_event(self, event_type, event_content),\n DursMsg::Response {\n req_id,\n res_content,\n ..\n } =\u003e responses::received::receive_response(self, req_id, res_content),\n DursMsg::Stop =\u003e break,\n _ =\u003e {} // Others DursMsg variants\n }\n }\n Err(e) =\u003e match e {\n mpsc::RecvTimeoutError::Disconnected =\u003e {\n fatal_error!(\"Disconnected blockchain module !\");\n }\n mpsc::RecvTimeoutError::Timeout =\u003e {}\n },\n }\n // Try to apply local stackable blocks every 20 seconds\n let now = SystemTime::now();\n if now\n .duration_since(last_get_stackables_blocks)\n .expect(\"duration_since error\")\n \u003e Duration::new(20, 0)\n {\n last_get_stackables_blocks = now;\n fork::stackable_blocks::apply_stackable_blocks(self);\n // Print current_blockstamp\n info!(\n \"BlockchainModule : current_blockstamp() = {:?}\",\n self.current_blockstamp\n );\n }\n }\n }\n}\n","traces":[{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":205,"address":null,"length":0,"stats":{"Line":0}},{"line":206,"address":null,"length":0,"stats":{"Line":0}},{"line":207,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":0}},{"line":230,"address":null,"length":0,"stats":{"Line":0}},{"line":235,"address":null,"length":0,"stats":{"Line":0}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":239,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":0}},{"line":247,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":249,"address":null,"length":0,"stats":{"Line":0}},{"line":253,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":261,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":0}},{"line":265,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":269,"address":null,"length":0,"stats":{"Line":0}},{"line":274,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":280,"address":null,"length":0,"stats":{"Line":0}},{"line":282,"address":null,"length":0,"stats":{"Line":0}},{"line":283,"address":null,"length":0,"stats":{"Line":0}},{"line":284,"address":null,"length":0,"stats":{"Line":0}},{"line":285,"address":null,"length":0,"stats":{"Line":0}},{"line":286,"address":null,"length":0,"stats":{"Line":0}},{"line":287,"address":null,"length":0,"stats":{"Line":0}},{"line":288,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":null,"length":0,"stats":{"Line":0}},{"line":290,"address":null,"length":0,"stats":{"Line":0}},{"line":291,"address":null,"length":0,"stats":{"Line":0}},{"line":292,"address":null,"length":0,"stats":{"Line":0}},{"line":294,"address":null,"length":0,"stats":{"Line":0}},{"line":295,"address":null,"length":0,"stats":{"Line":0}},{"line":296,"address":null,"length":0,"stats":{"Line":0}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":0}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":303,"address":null,"length":0,"stats":{"Line":0}},{"line":304,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":316,"address":null,"length":0,"stats":{"Line":0}},{"line":317,"address":null,"length":0,"stats":{"Line":0}},{"line":318,"address":null,"length":0,"stats":{"Line":0}},{"line":319,"address":null,"length":0,"stats":{"Line":0}},{"line":320,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":0}},{"line":325,"address":null,"length":0,"stats":{"Line":0}},{"line":327,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":95},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","requests","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules requests received.\n\nuse crate::*;\nuse dubp_documents::documents::identity::IdentityDocument;\nuse durs_message::requests::*;\nuse durs_module::*;\n\npub fn receive_req(\n bc: \u0026BlockchainModule,\n req_from: ModuleStaticName,\n req_id: ModuleReqId,\n req_content: DursReqContent,\n) {\n if let DursReqContent::BlockchainRequest(blockchain_req) = req_content {\n match blockchain_req {\n BlockchainRequest::CurrentBlockstamp() =\u003e responses::sent::send_req_response(\n bc,\n req_from,\n req_id,\n \u0026BlockchainResponse::CurrentBlockstamp(bc.current_blockstamp),\n ),\n BlockchainRequest::CurrentBlock =\u003e {\n debug!(\"BlockchainModule : receive BlockchainRequest::CurrentBlock()\");\n\n if let Ok(block_opt) = readers::block::get_block(\n \u0026bc.blocks_databases.blockchain_db,\n None,\n \u0026bc.current_blockstamp,\n ) {\n if let Some(dal_block) = block_opt {\n debug!(\n \"BlockchainModule : send_req_response(CurrentBlock({}))\",\n bc.current_blockstamp\n );\n responses::sent::send_req_response(\n bc,\n req_from,\n req_id,\n \u0026BlockchainResponse::CurrentBlock(\n Box::new(dal_block.block),\n bc.current_blockstamp,\n ),\n );\n } else {\n warn!(\"BlockchainModule : Req : fail to get current_block in bdd !\");\n }\n } else {\n fatal_error!(\n \"BlockchainModule: get_block(): fail to read LocalBlockchainV10DB !\"\n )\n }\n }\n BlockchainRequest::BlockByNumber { block_number } =\u003e {\n debug!(\n \"BlockchainModule : receive BlockchainRequest::BlockByNumber(#{})\",\n block_number\n );\n\n if let Ok(block_opt) = readers::block::get_block_in_local_blockchain(\n \u0026bc.blocks_databases.blockchain_db,\n block_number,\n ) {\n if let Some(block) = block_opt {\n debug!(\n \"BlockchainModule : send_req_response(BlockByNumber(#{}))\",\n block_number\n );\n responses::sent::send_req_response(\n bc,\n req_from,\n req_id,\n \u0026BlockchainResponse::BlockByNumber(Box::new(block)),\n );\n } else {\n debug!(\n \"BlockchainModule : Req : not found block #{} in bdd !\",\n block_number\n );\n }\n } else {\n fatal_error!(\n \"BlockchainModule: get_block(): fail to read LocalBlockchainV10DB !\"\n )\n }\n }\n BlockchainRequest::Chunk {\n first_block_number,\n count,\n } =\u003e {\n debug!(\n \"BlockchainModule : receive BlockchainRequest::Chunk(#{}, {})\",\n first_block_number, count\n );\n\n if let Ok(blocks) = readers::block::get_blocks_in_local_blockchain(\n \u0026bc.blocks_databases.blockchain_db,\n first_block_number,\n count,\n ) {\n if blocks.is_empty() {\n debug!(\n \"BlockchainModule : Req : not found chunk (#{}, {}) in bdd !\",\n first_block_number, count,\n );\n } else {\n debug!(\n \"BlockchainModule : send_req_response(Chunk(#{}, {}))\",\n first_block_number,\n blocks.len(),\n );\n responses::sent::send_req_response(\n bc,\n req_from,\n req_id,\n \u0026BlockchainResponse::Chunk(blocks),\n );\n }\n } else {\n fatal_error!(\n \"BlockchainModule: get_block(): fail to read LocalBlockchainV10DB !\"\n )\n }\n }\n BlockchainRequest::UIDs(pubkeys) =\u003e {\n responses::sent::send_req_response(\n bc,\n req_from,\n req_id,\n \u0026BlockchainResponse::UIDs(\n pubkeys\n .into_iter()\n .map(|p| {\n (\n p,\n durs_blockchain_dal::readers::identity::get_uid(\n \u0026bc.wot_databases.identities_db,\n p,\n )\n .expect(\"Fatal error : get_uid : Fail to read WotV10DB !\"),\n )\n })\n .collect(),\n ),\n );\n }\n BlockchainRequest::GetIdentities(filters) =\u003e {\n let identities = durs_blockchain_dal::readers::identity::get_identities(\n \u0026bc.wot_databases.identities_db,\n filters,\n bc.current_blockstamp.id,\n )\n .expect(\"Fatal error : get_identities: Fail to read IdentitiesDB !\")\n .into_iter()\n .map(|dal_idty| IdentityDocument::V10(dal_idty.idty_doc))\n .collect::\u003cVec\u003cIdentityDocument\u003e\u003e();\n responses::sent::send_req_response(\n bc,\n req_from,\n req_id,\n \u0026BlockchainResponse::Identities(identities),\n );\n }\n }\n }\n}\n","traces":[{"line":23,"address":5347344,"length":1,"stats":{"Line":0}},{"line":29,"address":5347388,"length":1,"stats":{"Line":0}},{"line":30,"address":5347942,"length":1,"stats":{"Line":0}},{"line":32,"address":5347732,"length":1,"stats":{"Line":0}},{"line":33,"address":5347740,"length":1,"stats":{"Line":0}},{"line":34,"address":5347756,"length":1,"stats":{"Line":0}},{"line":35,"address":5347763,"length":1,"stats":{"Line":0}},{"line":38,"address":5347947,"length":1,"stats":{"Line":0}},{"line":40,"address":5348276,"length":1,"stats":{"Line":0}},{"line":41,"address":5348237,"length":1,"stats":{"Line":0}},{"line":42,"address":5348249,"length":1,"stats":{"Line":0}},{"line":43,"address":5348261,"length":1,"stats":{"Line":0}},{"line":45,"address":5348434,"length":1,"stats":{"Line":0}},{"line":46,"address":5348524,"length":1,"stats":{"Line":0}},{"line":48,"address":5348802,"length":1,"stats":{"Line":0}},{"line":51,"address":5348991,"length":1,"stats":{"Line":0}},{"line":52,"address":5348999,"length":1,"stats":{"Line":0}},{"line":53,"address":5349015,"length":1,"stats":{"Line":0}},{"line":54,"address":5349249,"length":1,"stats":{"Line":0}},{"line":55,"address":5349022,"length":1,"stats":{"Line":0}},{"line":56,"address":5349190,"length":1,"stats":{"Line":0}},{"line":60,"address":5349398,"length":1,"stats":{"Line":0}},{"line":63,"address":5349693,"length":1,"stats":{"Line":0}},{"line":68,"address":5350819,"length":1,"stats":{"Line":0}},{"line":69,"address":5350833,"length":1,"stats":{"Line":0}},{"line":74,"address":5351258,"length":1,"stats":{"Line":0}},{"line":75,"address":5351231,"length":1,"stats":{"Line":0}},{"line":76,"address":5351243,"length":1,"stats":{"Line":0}},{"line":78,"address":5351380,"length":1,"stats":{"Line":0}},{"line":79,"address":5351462,"length":1,"stats":{"Line":0}},{"line":84,"address":5351923,"length":1,"stats":{"Line":0}},{"line":85,"address":5351931,"length":1,"stats":{"Line":0}},{"line":86,"address":5351947,"length":1,"stats":{"Line":0}},{"line":87,"address":5351954,"length":1,"stats":{"Line":0}},{"line":90,"address":5352233,"length":1,"stats":{"Line":0}},{"line":96,"address":5352678,"length":1,"stats":{"Line":0}},{"line":102,"address":5353804,"length":1,"stats":{"Line":0}},{"line":103,"address":5353818,"length":1,"stats":{"Line":0}},{"line":105,"address":5353832,"length":1,"stats":{"Line":0}},{"line":110,"address":5354402,"length":1,"stats":{"Line":0}},{"line":111,"address":5354368,"length":1,"stats":{"Line":0}},{"line":112,"address":5354380,"length":1,"stats":{"Line":0}},{"line":113,"address":5354387,"length":1,"stats":{"Line":0}},{"line":115,"address":5354500,"length":1,"stats":{"Line":0}},{"line":116,"address":5354559,"length":1,"stats":{"Line":0}},{"line":121,"address":5355100,"length":1,"stats":{"Line":0}},{"line":124,"address":5355315,"length":1,"stats":{"Line":0}},{"line":127,"address":5355679,"length":1,"stats":{"Line":0}},{"line":128,"address":5355687,"length":1,"stats":{"Line":0}},{"line":129,"address":5355703,"length":1,"stats":{"Line":0}},{"line":130,"address":5355710,"length":1,"stats":{"Line":0}},{"line":134,"address":5355862,"length":1,"stats":{"Line":0}},{"line":139,"address":5356971,"length":1,"stats":{"Line":0}},{"line":141,"address":5357011,"length":1,"stats":{"Line":0}},{"line":142,"address":5357019,"length":1,"stats":{"Line":0}},{"line":143,"address":5357035,"length":1,"stats":{"Line":0}},{"line":144,"address":5357196,"length":1,"stats":{"Line":0}},{"line":145,"address":5357042,"length":1,"stats":{"Line":0}},{"line":147,"address":5288944,"length":1,"stats":{"Line":0}},{"line":149,"address":5288959,"length":1,"stats":{"Line":0}},{"line":150,"address":5289066,"length":1,"stats":{"Line":0}},{"line":151,"address":5289006,"length":1,"stats":{"Line":0}},{"line":152,"address":5289027,"length":1,"stats":{"Line":0}},{"line":161,"address":5357343,"length":1,"stats":{"Line":0}},{"line":162,"address":5357531,"length":1,"stats":{"Line":0}},{"line":163,"address":5357423,"length":1,"stats":{"Line":0}},{"line":164,"address":5357437,"length":1,"stats":{"Line":0}},{"line":165,"address":5357517,"length":1,"stats":{"Line":0}},{"line":169,"address":5289200,"length":1,"stats":{"Line":0}},{"line":172,"address":5357689,"length":1,"stats":{"Line":0}},{"line":173,"address":5357697,"length":1,"stats":{"Line":0}},{"line":174,"address":5357713,"length":1,"stats":{"Line":0}},{"line":175,"address":5357720,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":73},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","requests","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules requests sent.\n\nuse crate::*;\nuse durs_network::requests::OldNetworkRequest;\n\npub fn request_network_consensus(bc: \u0026mut BlockchainModule) {\n let req = OldNetworkRequest::GetConsensus(ModuleReqFullId(\n BlockchainModule::name(),\n ModuleReqId(bc.pending_network_requests.len() as u32),\n ));\n let req_id = dunp::queries::request_network(\n bc,\n ModuleReqId(bc.pending_network_requests.len() as u32),\n \u0026req,\n );\n bc.pending_network_requests.insert(req_id, req);\n}\n\npub fn request_orphan_previous(bc: \u0026mut BlockchainModule, orphan_blockstamp: Blockstamp) {\n let new_pending_network_requests =\n dunp::queries::request_orphan_previous(bc, orphan_blockstamp.id);\n for (new_req_id, new_req) in new_pending_network_requests {\n bc.pending_network_requests.insert(new_req_id, new_req);\n }\n}\n\npub fn request_next_main_blocks(bc: \u0026mut BlockchainModule) {\n // Choose frequency\n let frequency = if bc.consensus.id.0 == 0\n || bc.consensus.id.0 \u003e bc.current_blockstamp.id.0 + *BLOCKS_DELAY_THRESHOLD\n {\n *REQUEST_MAIN_BLOCKS_HIGH_FREQUENCY_IN_SEC\n } else {\n *REQUEST_MAIN_BLOCKS_LOW_FREQUENCY_IN_SEC\n };\n\n // Apply frequency\n let now = SystemTime::now();\n if now\n .duration_since(bc.last_request_blocks)\n .expect(\"duration_since error\")\n \u003e Duration::from_secs(frequency)\n {\n bc.last_request_blocks = now;\n // Request next main blocks\n let to = match bc.consensus.id.0 {\n 0 =\u003e (bc.current_blockstamp.id.0 + *MAX_BLOCKS_REQUEST),\n _ =\u003e bc.consensus.id.0,\n };\n let new_pending_network_requests = dunp::queries::request_blocks_to(bc, BlockNumber(to));\n for (new_req_id, new_req) in new_pending_network_requests {\n bc.pending_network_requests.insert(new_req_id, new_req);\n }\n }\n}\n","traces":[{"line":21,"address":4231408,"length":1,"stats":{"Line":0}},{"line":22,"address":4231478,"length":1,"stats":{"Line":0}},{"line":23,"address":4231420,"length":1,"stats":{"Line":0}},{"line":24,"address":4231435,"length":1,"stats":{"Line":0}},{"line":26,"address":4231609,"length":1,"stats":{"Line":0}},{"line":27,"address":4231560,"length":1,"stats":{"Line":0}},{"line":28,"address":4231565,"length":1,"stats":{"Line":0}},{"line":31,"address":4231638,"length":1,"stats":{"Line":0}},{"line":34,"address":4231760,"length":1,"stats":{"Line":0}},{"line":36,"address":4231772,"length":1,"stats":{"Line":0}},{"line":37,"address":4231819,"length":1,"stats":{"Line":0}},{"line":38,"address":4232202,"length":1,"stats":{"Line":0}},{"line":42,"address":4232368,"length":1,"stats":{"Line":0}},{"line":44,"address":4232380,"length":1,"stats":{"Line":0}},{"line":45,"address":4232401,"length":1,"stats":{"Line":0}},{"line":47,"address":4232502,"length":1,"stats":{"Line":0}},{"line":49,"address":4232519,"length":1,"stats":{"Line":0}},{"line":53,"address":4232534,"length":1,"stats":{"Line":0}},{"line":54,"address":4232572,"length":1,"stats":{"Line":0}},{"line":55,"address":4232553,"length":1,"stats":{"Line":0}},{"line":57,"address":4232623,"length":1,"stats":{"Line":0}},{"line":59,"address":4232683,"length":1,"stats":{"Line":0}},{"line":61,"address":4232773,"length":1,"stats":{"Line":0}},{"line":62,"address":4232712,"length":1,"stats":{"Line":0}},{"line":63,"address":4232775,"length":1,"stats":{"Line":0}},{"line":65,"address":4232793,"length":1,"stats":{"Line":0}},{"line":66,"address":4232832,"length":1,"stats":{"Line":0}},{"line":67,"address":4233224,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":28},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","responses","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules responses received.\n\nuse crate::*;\n\npub fn receive_response(\n bc: \u0026mut BlockchainModule,\n req_id: ModuleReqId,\n res_content: DursResContent,\n) {\n if let DursResContent::NetworkResponse(network_response) = res_content {\n debug!(\"BlockchainModule : receive NetworkResponse() !\");\n if let Some(request) = bc.pending_network_requests.remove(\u0026req_id) {\n match request {\n OldNetworkRequest::GetConsensus(_) =\u003e {\n if let NetworkResponse::Consensus(_, response) = network_response {\n if let Ok(blockstamp) = response {\n bc.consensus = blockstamp;\n }\n }\n }\n OldNetworkRequest::GetBlocks(_, _, _) =\u003e {\n if let NetworkResponse::Chunk(_, _, blocks) = network_response {\n dunp::receiver::receive_blocks(bc, blocks);\n }\n }\n _ =\u003e {}\n }\n }\n }\n}\n","traces":[{"line":20,"address":5433648,"length":1,"stats":{"Line":0}},{"line":25,"address":5433664,"length":1,"stats":{"Line":0}},{"line":26,"address":5433846,"length":1,"stats":{"Line":0}},{"line":27,"address":5434137,"length":1,"stats":{"Line":0}},{"line":28,"address":5434495,"length":1,"stats":{"Line":0}},{"line":29,"address":5434250,"length":1,"stats":{"Line":0}},{"line":30,"address":5434306,"length":1,"stats":{"Line":0}},{"line":31,"address":5434355,"length":1,"stats":{"Line":0}},{"line":32,"address":5434424,"length":1,"stats":{"Line":0}},{"line":37,"address":5434497,"length":1,"stats":{"Line":0}},{"line":38,"address":5434549,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","responses","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules responses sent.\n\nuse crate::*;\nuse durs_common_tools::fatal_error;\n\npub fn send_req_response(\n bc: \u0026BlockchainModule,\n requester: ModuleStaticName,\n req_id: ModuleReqId,\n response: \u0026BlockchainResponse,\n) {\n bc.router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Response {\n res_from: BlockchainModule::name(),\n res_to: requester,\n req_id,\n res_content: DursResContent::BlockchainResponse(response.clone()),\n }))\n .unwrap_or_else(|_| fatal_error!(\"Fail to send ReqRes to router\"));\n}\n","traces":[{"line":21,"address":5346976,"length":1,"stats":{"Line":0}},{"line":27,"address":5347007,"length":1,"stats":{"Line":0}},{"line":34,"address":4759200,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":3},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","sync","apply","blocks_worker.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::sync::*;\nuse pbr::ProgressBar;\nuse std::sync::mpsc;\n\npub fn execute(\n pool: \u0026ThreadPool,\n sender_sync_thread: mpsc::Sender\u003cMessForSyncThread\u003e,\n recv: mpsc::Receiver\u003cSyncJobsMess\u003e,\n blocks_dbs: BlocksV10DBs,\n forks_db: ForksDBs,\n target_blockstamp: Blockstamp,\n mut apply_pb: ProgressBar\u003cstd::io::Stdout\u003e,\n) {\n // Launch blocks_worker thread\n pool.execute(move || {\n let blocks_job_begin = SystemTime::now();\n\n // Listen db requets\n let mut chunk_index = 0;\n let mut all_wait_duration = Duration::from_millis(0);\n let mut wait_begin = SystemTime::now();\n\n if let Ok(SyncJobsMess::ForkWindowSize(fork_window_size)) = recv.recv() {\n log::info!(\n \"Block worker receive fork_window_size={}.\",\n fork_window_size\n );\n loop {\n match recv.recv() {\n Ok(SyncJobsMess::BlocksDBsWriteQuery(req)) =\u003e {\n all_wait_duration += SystemTime::now().duration_since(wait_begin).unwrap();\n\n // Apply db request\n req.apply(\n \u0026blocks_dbs.blockchain_db,\n \u0026forks_db,\n fork_window_size,\n Some(target_blockstamp),\n )\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\");\n\n chunk_index += 1;\n if chunk_index == 250 {\n chunk_index = 0;\n apply_pb.inc();\n }\n wait_begin = SystemTime::now();\n }\n Ok(SyncJobsMess::End) | Err(_) =\u003e {\n log::info!(\"Sync: block worker channel closed.\");\n break;\n }\n Ok(msg) =\u003e fatal_error!(\n \"Dev error: block worker receive unexpected message: {:?}\",\n msg\n ),\n }\n }\n } else {\n fatal_error!(\"Dev error: block worker must first receive fork window size\")\n }\n\n // Increment progress bar (last chunk)\n apply_pb.inc();\n // Save blockchain, and fork databases\n println!();\n println!(\"Write indexs in files...\");\n info!(\"Save blockchain and forks databases in files...\");\n blocks_dbs.save_dbs();\n forks_db.save_dbs();\n\n // Send finish signal\n sender_sync_thread\n .send(MessForSyncThread::ApplyFinish())\n .expect(\"Fatal error : sync_thread unrechable !\");\n let blocks_job_duration =\n SystemTime::now().duration_since(blocks_job_begin).unwrap() - all_wait_duration;\n info!(\n \"blocks_job_duration={},{:03} seconds.\",\n blocks_job_duration.as_secs(),\n blocks_job_duration.subsec_millis()\n );\n });\n}\n","traces":[{"line":20,"address":4488640,"length":1,"stats":{"Line":0}},{"line":30,"address":4488696,"length":1,"stats":{"Line":0}},{"line":31,"address":5436077,"length":1,"stats":{"Line":0}},{"line":34,"address":5436191,"length":1,"stats":{"Line":0}},{"line":35,"address":5436206,"length":1,"stats":{"Line":0}},{"line":36,"address":5436273,"length":1,"stats":{"Line":0}},{"line":38,"address":5436348,"length":1,"stats":{"Line":0}},{"line":39,"address":5436403,"length":1,"stats":{"Line":0}},{"line":43,"address":5436839,"length":1,"stats":{"Line":0}},{"line":44,"address":5436857,"length":1,"stats":{"Line":0}},{"line":45,"address":5436888,"length":1,"stats":{"Line":0}},{"line":46,"address":5437006,"length":1,"stats":{"Line":0}},{"line":49,"address":5437422,"length":1,"stats":{"Line":0}},{"line":50,"address":5437479,"length":1,"stats":{"Line":0}},{"line":51,"address":5437491,"length":1,"stats":{"Line":0}},{"line":52,"address":5437495,"length":1,"stats":{"Line":0}},{"line":53,"address":5437511,"length":1,"stats":{"Line":0}},{"line":57,"address":5437706,"length":1,"stats":{"Line":0}},{"line":58,"address":5437748,"length":1,"stats":{"Line":0}},{"line":59,"address":5437761,"length":1,"stats":{"Line":0}},{"line":60,"address":5437780,"length":1,"stats":{"Line":0}},{"line":62,"address":5437806,"length":1,"stats":{"Line":0}},{"line":65,"address":5437909,"length":1,"stats":{"Line":0}},{"line":68,"address":5437044,"length":1,"stats":{"Line":0}},{"line":75,"address":5439760,"length":1,"stats":{"Line":0}},{"line":79,"address":5440894,"length":1,"stats":{"Line":0}},{"line":81,"address":5440918,"length":1,"stats":{"Line":0}},{"line":82,"address":5440976,"length":1,"stats":{"Line":0}},{"line":83,"address":5441034,"length":1,"stats":{"Line":0}},{"line":84,"address":5441332,"length":1,"stats":{"Line":0}},{"line":85,"address":5441358,"length":1,"stats":{"Line":0}},{"line":88,"address":5441384,"length":1,"stats":{"Line":0}},{"line":89,"address":5441390,"length":1,"stats":{"Line":0}},{"line":92,"address":5441454,"length":1,"stats":{"Line":0}},{"line":93,"address":5441684,"length":1,"stats":{"Line":0}},{"line":95,"address":5441883,"length":1,"stats":{"Line":0}},{"line":96,"address":5441916,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":37},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","sync","apply","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\npub mod blocks_worker;\npub mod txs_worker;\npub mod wot_worker;\n\nuse crate::dubp;\nuse crate::dubp::apply::apply_valid_block;\nuse crate::dubp::apply::ValidBlockApplyReqs;\nuse crate::sync::SyncJobsMess;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\nuse dubp_documents::{BlockNumber, Blockstamp, Document};\nuse dup_crypto::keys::PubKey;\nuse dup_currency_params::{CurrencyName, CurrencyParameters};\nuse durs_blockchain_dal::writers::requests::WotsDBsWriteQuery;\nuse durs_blockchain_dal::{BinDB, CertsExpirV10Datas, WotsV10DBs};\nuse durs_common_tools::fatal_error;\nuse durs_network_documents::url::Url;\nuse durs_wot::data::rusty::RustyWebOfTrust;\nuse durs_wot::data::NodeId;\nuse std::collections::{HashMap, VecDeque};\nuse std::path::PathBuf;\nuse std::sync::mpsc;\nuse std::time::{Duration, SystemTime};\nuse unwrap::unwrap;\n\n// récupérer les métadonnées entre deux utilisation\npub struct BlockApplicator {\n // options\n pub source: Option\u003cUrl\u003e,\n pub currency: CurrencyName,\n pub verif_inner_hash: bool,\n pub currency_params: Option\u003cCurrencyParameters\u003e,\n pub dbs_path: PathBuf,\n pub target_blockstamp: Blockstamp,\n // senders\n pub sender_blocks_thread: mpsc::Sender\u003cSyncJobsMess\u003e,\n pub sender_wot_thread: mpsc::Sender\u003cSyncJobsMess\u003e,\n pub sender_tx_thread: mpsc::Sender\u003cSyncJobsMess\u003e,\n // pool\n pub certs_count: i32,\n pub current_blockstamp: Blockstamp,\n pub blocks_not_expiring: VecDeque\u003cu64\u003e,\n pub last_block_expiring: isize,\n // databases\n pub wot_index: HashMap\u003cPubKey, NodeId\u003e,\n pub wot_databases: WotsV10DBs,\n pub certs_db: BinDB\u003cCertsExpirV10Datas\u003e,\n // time measurement\n pub wait_begin: SystemTime,\n pub all_wait_duration: Duration,\n pub all_verif_block_hashs_duration: Duration,\n pub all_apply_valid_block_duration: Duration,\n}\n\nimpl BlockApplicator {\n pub fn apply(\u0026mut self, block_doc: BlockDocument) {\n self.all_wait_duration += SystemTime::now().duration_since(self.wait_begin).unwrap();\n\n // Verify block hashs\n let verif_block_hashs_begin = SystemTime::now();\n if self.verif_inner_hash {\n dubp::check::hashs::verify_block_hashs(\u0026block_doc)\n .expect(\"Receive wrong block, please reset data and resync !\");\n }\n self.all_verif_block_hashs_duration += SystemTime::now()\n .duration_since(verif_block_hashs_begin)\n .unwrap();\n\n // Push block common_time in blocks_not_expiring\n self.blocks_not_expiring.push_back(block_doc.common_time());\n // Get blocks_expiring\n let mut blocks_expiring = Vec::new();\n while self.blocks_not_expiring.front().cloned()\n \u003c Some(block_doc.common_time() - unwrap!(self.currency_params).sig_validity)\n {\n self.last_block_expiring += 1;\n blocks_expiring.push(BlockNumber(self.last_block_expiring as u32));\n self.blocks_not_expiring.pop_front();\n }\n\n // Find expire_certs\n let expire_certs =\n durs_blockchain_dal::readers::certs::find_expire_certs(\u0026self.certs_db, blocks_expiring)\n .expect(\"find_expire_certs() : DALError\");\n\n // Get block blockstamp\n let blockstamp = block_doc.blockstamp();\n\n // Apply block\n let apply_valid_block_begin = SystemTime::now();\n if let Ok(ValidBlockApplyReqs(block_req, wot_db_reqs, currency_db_reqs)) =\n apply_valid_block::\u003cRustyWebOfTrust\u003e(\n block_doc,\n \u0026mut self.wot_index,\n \u0026self.wot_databases.wot_db,\n \u0026expire_certs,\n )\n {\n self.all_apply_valid_block_duration += SystemTime::now()\n .duration_since(apply_valid_block_begin)\n .unwrap();\n self.current_blockstamp = blockstamp;\n debug!(\"Apply db requests...\");\n // Send block request to blocks worker thread\n self.sender_blocks_thread\n .send(SyncJobsMess::BlocksDBsWriteQuery(block_req.clone()))\n .expect(\n \"Fail to communicate with blocks worker thread, please reset data \u0026 resync !\",\n );\n // Send wot requests to wot worker thread\n for req in wot_db_reqs {\n if let WotsDBsWriteQuery::CreateCert(\n ref _source_pubkey,\n ref source,\n ref target,\n ref created_block_id,\n ref _median_time,\n ) = req\n {\n self.certs_count += 1;\n // Add cert in certs_db\n self.certs_db\n .write(|db| {\n let mut created_certs =\n db.get(\u0026created_block_id).cloned().unwrap_or_default();\n created_certs.insert((*source, *target));\n db.insert(*created_block_id, created_certs);\n })\n .expect(\"RustBreakError : please reset data and resync !\");\n }\n self.sender_wot_thread\n .send(SyncJobsMess::WotsDBsWriteQuery(\n self.current_blockstamp,\n Box::new(unwrap!(self.currency_params)),\n req.clone(),\n ))\n .expect(\n \"Fail to communicate with tx worker thread, please reset data \u0026 resync !\",\n )\n }\n // Send blocks and wot requests to wot worker thread\n for req in currency_db_reqs {\n self.sender_tx_thread\n .send(SyncJobsMess::CurrencyDBsWriteQuery(\n self.current_blockstamp,\n req.clone(),\n ))\n .expect(\n \"Fail to communicate with tx worker thread, please reset data \u0026 resync !\",\n );\n }\n debug!(\"Success to apply block #{}\", self.current_blockstamp.id.0);\n if self.current_blockstamp.id.0 \u003e= self.target_blockstamp.id.0 {\n if self.current_blockstamp == self.target_blockstamp {\n // Sync completed\n return;\n } else {\n fatal_error!(\"Fatal Error : we get a fork, please reset data and sync again !\");\n }\n }\n } else {\n fatal_error!(\n \"Fatal error : fail to stack up block #{}\",\n self.current_blockstamp.id.0 + 1\n )\n }\n self.wait_begin = SystemTime::now();\n }\n}\n","traces":[{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":55},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","sync","apply","txs_worker.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::sync::*;\nuse std::sync::mpsc;\n\npub fn execute(\n pool: \u0026ThreadPool,\n profile_path: PathBuf,\n sender_sync_thread: mpsc::Sender\u003cMessForSyncThread\u003e,\n recv: mpsc::Receiver\u003cSyncJobsMess\u003e,\n) {\n // Launch tx_worker thread\n pool.execute(move || {\n let tx_job_begin = SystemTime::now();\n // Open databases\n let db_path = durs_conf::get_blockchain_db_path(profile_path);\n let databases = CurrencyV10DBs::open(Some(\u0026db_path));\n\n // Listen db requets\n let mut all_wait_duration = Duration::from_millis(0);\n let mut wait_begin = SystemTime::now();\n while let Ok(SyncJobsMess::CurrencyDBsWriteQuery(blockstamp, req)) = recv.recv() {\n all_wait_duration += SystemTime::now().duration_since(wait_begin).unwrap();\n // Apply db request\n req.apply(\u0026blockstamp, \u0026databases)\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\");\n wait_begin = SystemTime::now();\n }\n // Save tx, utxo, du and balances databases\n info!(\"Save tx and sources database in file...\");\n databases.save_dbs(true, true);\n\n // Send finish signal\n sender_sync_thread\n .send(MessForSyncThread::ApplyFinish())\n .expect(\"Fatal error : sync_thread unrechable !\");\n let tx_job_duration =\n SystemTime::now().duration_since(tx_job_begin).unwrap() - all_wait_duration;\n info!(\n \"tx_job_duration={},{:03} seconds.\",\n tx_job_duration.as_secs(),\n tx_job_duration.subsec_millis()\n );\n });\n}\n","traces":[{"line":19,"address":4362016,"length":1,"stats":{"Line":0}},{"line":26,"address":4353520,"length":1,"stats":{"Line":0}},{"line":27,"address":4353527,"length":1,"stats":{"Line":0}},{"line":29,"address":4353625,"length":1,"stats":{"Line":0}},{"line":30,"address":4353719,"length":1,"stats":{"Line":0}},{"line":33,"address":4353758,"length":1,"stats":{"Line":0}},{"line":34,"address":4353828,"length":1,"stats":{"Line":0}},{"line":35,"address":4353881,"length":1,"stats":{"Line":0}},{"line":36,"address":4354029,"length":1,"stats":{"Line":0}},{"line":38,"address":4354294,"length":1,"stats":{"Line":0}},{"line":40,"address":4354365,"length":1,"stats":{"Line":0}},{"line":43,"address":4354454,"length":1,"stats":{"Line":0}},{"line":44,"address":4354783,"length":1,"stats":{"Line":0}},{"line":47,"address":4354819,"length":1,"stats":{"Line":0}},{"line":48,"address":4354823,"length":1,"stats":{"Line":0}},{"line":51,"address":4354887,"length":1,"stats":{"Line":0}},{"line":52,"address":4355117,"length":1,"stats":{"Line":0}},{"line":54,"address":4355316,"length":1,"stats":{"Line":0}},{"line":55,"address":4355349,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":19},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","sync","apply","wot_worker.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::sync::*;\nuse std::ops::Deref;\nuse std::sync::mpsc;\n\npub fn execute(\n pool: \u0026ThreadPool,\n profile_path: PathBuf,\n sender_sync_thread: mpsc::Sender\u003cMessForSyncThread\u003e,\n recv: mpsc::Receiver\u003cSyncJobsMess\u003e,\n) {\n // Launch wot_worker thread\n pool.execute(move || {\n let wot_job_begin = SystemTime::now();\n // Open databases\n let db_path = durs_conf::get_blockchain_db_path(profile_path);\n let databases = WotsV10DBs::open(Some(\u0026db_path));\n\n // Listen db requets\n let mut all_wait_duration = Duration::from_millis(0);\n let mut wait_begin = SystemTime::now();\n while let Ok(mess) = recv.recv() {\n all_wait_duration += SystemTime::now().duration_since(wait_begin).unwrap();\n match mess {\n SyncJobsMess::WotsDBsWriteQuery(blockstamp, currency_params, req) =\u003e req\n .apply(\u0026blockstamp, \u0026currency_params.deref(), \u0026databases)\n .expect(\"Fatal error : Fail to apply DBWriteRequest !\"),\n SyncJobsMess::End =\u003e break,\n _ =\u003e {}\n }\n wait_begin = SystemTime::now();\n }\n // Save wots databases\n info!(\"Save wots databases in files...\");\n databases.save_dbs_except_graph();\n\n // Send finish signal\n sender_sync_thread\n .send(MessForSyncThread::ApplyFinish())\n .expect(\"Fatal error : sync_thread unrechable !\");\n let wot_job_duration =\n SystemTime::now().duration_since(wot_job_begin).unwrap() - all_wait_duration;\n info!(\n \"wot_job_duration={},{:03} seconds.\",\n wot_job_duration.as_secs(),\n wot_job_duration.subsec_millis()\n );\n });\n}\n","traces":[{"line":20,"address":4359280,"length":1,"stats":{"Line":0}},{"line":27,"address":4359309,"length":1,"stats":{"Line":0}},{"line":28,"address":5273165,"length":1,"stats":{"Line":0}},{"line":30,"address":5273319,"length":1,"stats":{"Line":0}},{"line":31,"address":5273413,"length":1,"stats":{"Line":0}},{"line":34,"address":5273452,"length":1,"stats":{"Line":0}},{"line":35,"address":5273522,"length":1,"stats":{"Line":0}},{"line":36,"address":5273575,"length":1,"stats":{"Line":0}},{"line":37,"address":5273706,"length":1,"stats":{"Line":0}},{"line":38,"address":5274319,"length":1,"stats":{"Line":0}},{"line":39,"address":5273940,"length":1,"stats":{"Line":0}},{"line":40,"address":5274147,"length":1,"stats":{"Line":0}},{"line":45,"address":5274389,"length":1,"stats":{"Line":0}},{"line":48,"address":5274472,"length":1,"stats":{"Line":0}},{"line":49,"address":5274736,"length":1,"stats":{"Line":0}},{"line":52,"address":5274763,"length":1,"stats":{"Line":0}},{"line":53,"address":5274767,"length":1,"stats":{"Line":0}},{"line":56,"address":5274831,"length":1,"stats":{"Line":0}},{"line":57,"address":5275061,"length":1,"stats":{"Line":0}},{"line":59,"address":5275260,"length":1,"stats":{"Line":0}},{"line":60,"address":5275293,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":21},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","sync","download","json_reader_worker.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::sync::*;\nuse dubp_documents::parsers::blocks::parse_json_block;\nuse dubp_documents::Blockstamp;\nuse durs_common_tools::fatal_error;\nuse failure::Error;\nuse rayon::prelude::*;\nuse std::collections::HashSet;\nuse std::io::Read;\nuse std::path::{Path, PathBuf};\nuse std::sync::mpsc;\nuse threadpool::ThreadPool;\n\n/// Number of chunk parsed before sending them to the apply workers\nstatic CHUNKS_STEP: \u0026'static usize = \u002616;\n\n/// Json reader worker\npub fn json_reader_worker(\n pool: \u0026ThreadPool,\n profile_path: PathBuf,\n sender_sync_thread: mpsc::Sender\u003cMessForSyncThread\u003e,\n json_chunks_path: PathBuf,\n end: Option\u003cu32\u003e,\n) {\n // Lauch json reader thread\n pool.execute(move || {\n let ts_job_begin = SystemTime::now();\n\n // Get list of json chunk files\n let chunks_set = get_chunks_set(\u0026json_chunks_path);\n if chunks_set.is_empty() {\n fatal_error!(\"json_files_path directory is empty !\");\n }\n\n // Get max chunk number and max block id\n let (max_chunk_number, max_block_id): (usize, u32) = if let Some(end) = end {\n (end as usize / (*crate::constants::CHUNK_SIZE), end)\n } else {\n (\n chunks_set.len() - 1,\n (chunks_set.len() * (*crate::constants::CHUNK_SIZE) - 1) as u32,\n )\n };\n\n // Verify if max chunk exist\n if chunks_set.get(\u0026max_chunk_number).is_none() {\n fatal_error!(\"Missing chunk file n°{}\", max_chunk_number);\n };\n\n // Open chunk file\n let chunk_file_content_result = open_json_chunk_file(\u0026json_chunks_path, max_chunk_number);\n if chunk_file_content_result.is_err() {\n fatal_error!(\"Fail to open chunk file n°{}\", max_chunk_number);\n }\n\n // Parse chunk file content\n let blocks_result = parse_json_chunk(\u0026chunk_file_content_result.expect(\"safe unwrap\"));\n let last_chunk_blocks = match blocks_result {\n Ok(blocks) =\u003e blocks,\n Err(e) =\u003e {\n fatal_error!(\"Fail to parse chunk file n°{} : {}\", max_chunk_number, e);\n }\n };\n\n if last_chunk_blocks.is_empty() {\n fatal_error!(\"Last chunk is empty !\");\n }\n\n let last_block = last_chunk_blocks\n .get(max_block_id as usize % *crate::constants::CHUNK_SIZE)\n .expect(\"safe unwrap because not empty\");\n\n // Send TargetBlockcstamp\n sender_sync_thread\n .send(MessForSyncThread::Target(\n last_block.currency().into(),\n last_block.blockstamp(),\n ))\n .expect(\"Fatal error : sync_thread unrechable !\");\n\n // Get current local blockstamp\n debug!(\"Get local current blockstamp...\");\n let db_path = durs_conf::get_blockchain_db_path(profile_path);\n let blocks_databases = BlocksV10DBs::open(Some(\u0026db_path));\n let current_blockstamp: Blockstamp =\n durs_blockchain_dal::readers::block::get_current_blockstamp(\u0026blocks_databases)\n .expect(\"ForksV10DB : RustBreakError !\")\n .unwrap_or_default();\n info!(\"Local current blockstamp = {}\", current_blockstamp);\n\n // Get first chunk number\n let first_chunk_number: usize =\n current_blockstamp.id.0 as usize / *crate::constants::CHUNK_SIZE;\n\n // Parse chunks\n let mut begin_chunk_number = first_chunk_number;\n while begin_chunk_number \u003c= max_chunk_number {\n let last_chunk_number = if begin_chunk_number + *CHUNKS_STEP \u003c max_chunk_number + 1 {\n begin_chunk_number + *CHUNKS_STEP\n } else {\n max_chunk_number + 1\n };\n let chunks_numbers: Vec\u003c_\u003e = (begin_chunk_number..last_chunk_number).collect();\n let mut chunks_blocks: HashMap\u003cusize, Vec\u003cBlockDocument\u003e\u003e = chunks_numbers\n .par_iter()\n .map(|chunk_number| treat_once_json_chunk(\u0026json_chunks_path, *chunk_number))\n .collect();\n\n // Send blocks\n for chunk_number in chunks_numbers {\n for block in chunks_blocks\n .remove(\u0026chunk_number)\n .expect(\"Dev error: sync: chunk_blocks not contain key chunk_number !\")\n {\n // Verify if the block number is within the expected interval\n let block_id = block.blockstamp().id;\n if (block_id \u003e current_blockstamp.id \u0026\u0026 block_id.0 \u003c= max_block_id)\n || (block_id.0 == 0 \u0026\u0026 current_blockstamp == Blockstamp::default())\n {\n // Send block document\n sender_sync_thread\n .send(MessForSyncThread::BlockDocument(block))\n .expect(\"Fatal error : sync_thread unrechable !\");\n }\n }\n }\n\n begin_chunk_number += *CHUNKS_STEP;\n }\n\n sender_sync_thread\n .send(MessForSyncThread::DownloadFinish())\n .expect(\"Fatal error : sync_thread unrechable !\");\n let ts_job_duration = SystemTime::now()\n .duration_since(ts_job_begin)\n .expect(\"duration_since error\");\n info!(\n \"ts_job_duration={},{:03} seconds.\",\n ts_job_duration.as_secs(),\n ts_job_duration.subsec_millis()\n );\n });\n}\n\n/// Treat one JSON Chunk\nfn treat_once_json_chunk(\n json_chunks_path: \u0026PathBuf,\n chunk_number: usize,\n) -\u003e (usize, Vec\u003cBlockDocument\u003e) {\n // Open chunk file\n let chunk_file_content_result = open_json_chunk_file(json_chunks_path, chunk_number);\n if chunk_file_content_result.is_err() {\n fatal_error!(\"Fail to open chunk file n°{}\", chunk_number);\n }\n\n // Parse chunk file content\n let blocks_result = parse_json_chunk(\u0026chunk_file_content_result.expect(\"safe unwrap\"));\n let blocks = match blocks_result {\n Ok(blocks) =\u003e blocks,\n Err(e) =\u003e {\n fatal_error!(\"Fail to parse chunk file n°{} : {}\", chunk_number, e);\n }\n };\n (chunk_number, blocks)\n}\n\n/// Parse json chunk into BlockDocument Vector\nfn parse_json_chunk(json_chunk_content: \u0026str) -\u003e Result\u003cVec\u003cBlockDocument\u003e, Error\u003e {\n let mut block_doc_vec = Vec::with_capacity(*crate::constants::CHUNK_SIZE);\n\n let json_value = json_pest_parser::parse_json_string(json_chunk_content)?;\n if let Some(json_object) = json_value.to_object() {\n if let Some(blocks) = json_object.get(\"blocks\") {\n if let Some(blocks_array) = blocks.to_array() {\n for json_block in blocks_array {\n block_doc_vec.push(parse_json_block(json_block)?);\n }\n } else {\n fatal_error!(\"Fail to parse json chunk : field \\\"blocks\\\" must be an array !\");\n }\n } else {\n fatal_error!(\"Fail to parse json chunk : field \\\"blocks\\\" don't exist !\");\n }\n } else {\n fatal_error!(\"Fail to parse json chunk : json root node must be an object !\");\n }\n\n Ok(block_doc_vec)\n}\n\nfn get_chunks_set(dir: \u0026Path) -\u003e HashSet\u003cusize\u003e {\n let json_chunk_file_list_result = fs::read_dir(dir);\n if json_chunk_file_list_result.is_err() {\n error!(\"Fail to read dir json_files_path !\");\n fatal_error!(\"Fail to read dir json_files_path !\");\n }\n\n let mut chunks_set = HashSet::new();\n\n for dir_entry in json_chunk_file_list_result.expect(\"Dev error: err case must be treat before.\")\n {\n if let Ok(dir_entry) = dir_entry {\n if let Ok(file_name) = dir_entry.file_name().into_string() {\n let file_name_len = file_name.len();\n\n if let Ok(file_type) = dir_entry.file_type() {\n if file_type.is_file()\n \u0026\u0026 file_name[0..CHUNK_FILE_NAME_BEGIN.len()] == *CHUNK_FILE_NAME_BEGIN\n \u0026\u0026 file_name[file_name_len - CHUNK_FILE_NAME_END.len()..]\n == *CHUNK_FILE_NAME_END\n {\n let chunk_number_result: Result\u003cusize, std::num::ParseIntError\u003e = file_name\n [CHUNK_FILE_NAME_BEGIN.len()\n ..file_name_len - CHUNK_FILE_NAME_END.len()]\n .parse();\n\n if let Ok(chunk_number) = chunk_number_result {\n chunks_set.insert(chunk_number);\n }\n }\n }\n }\n }\n }\n\n chunks_set\n}\n\nfn open_json_chunk_file(\n json_chunks_path: \u0026PathBuf,\n chunk_number: usize,\n) -\u003e std::io::Result\u003c(String)\u003e {\n let mut chunk_file_path = json_chunks_path.clone();\n chunk_file_path.push(\u0026format!(\n \"{}{}{}\",\n CHUNK_FILE_NAME_BEGIN, chunk_number, CHUNK_FILE_NAME_END\n ));\n let file = std::fs::File::open(chunk_file_path)?;\n let mut buf_reader = std::io::BufReader::new(file);\n let mut contents = String::new();\n buf_reader.read_to_string(\u0026mut contents)?;\n\n Ok(contents)\n}\n","traces":[{"line":32,"address":5298480,"length":1,"stats":{"Line":0}},{"line":40,"address":5102512,"length":1,"stats":{"Line":0}},{"line":41,"address":5102525,"length":1,"stats":{"Line":0}},{"line":44,"address":5102671,"length":1,"stats":{"Line":0}},{"line":45,"address":5102745,"length":1,"stats":{"Line":0}},{"line":46,"address":5102793,"length":1,"stats":{"Line":0}},{"line":50,"address":5103927,"length":1,"stats":{"Line":0}},{"line":51,"address":5103956,"length":1,"stats":{"Line":0}},{"line":54,"address":5104067,"length":1,"stats":{"Line":0}},{"line":55,"address":5104122,"length":1,"stats":{"Line":0}},{"line":60,"address":5104307,"length":1,"stats":{"Line":0}},{"line":61,"address":5104376,"length":1,"stats":{"Line":0}},{"line":65,"address":5105879,"length":1,"stats":{"Line":0}},{"line":66,"address":5105926,"length":1,"stats":{"Line":0}},{"line":67,"address":5105956,"length":1,"stats":{"Line":0}},{"line":71,"address":5107459,"length":1,"stats":{"Line":0}},{"line":73,"address":5107645,"length":1,"stats":{"Line":0}},{"line":74,"address":5107791,"length":1,"stats":{"Line":0}},{"line":75,"address":5107831,"length":1,"stats":{"Line":0}},{"line":79,"address":5107774,"length":1,"stats":{"Line":0}},{"line":80,"address":5109630,"length":1,"stats":{"Line":0}},{"line":83,"address":5110756,"length":1,"stats":{"Line":0}},{"line":84,"address":5110791,"length":1,"stats":{"Line":0}},{"line":88,"address":5110953,"length":1,"stats":{"Line":0}},{"line":89,"address":5111062,"length":1,"stats":{"Line":0}},{"line":90,"address":5110957,"length":1,"stats":{"Line":0}},{"line":91,"address":5111035,"length":1,"stats":{"Line":0}},{"line":96,"address":5111227,"length":1,"stats":{"Line":0}},{"line":97,"address":5111517,"length":1,"stats":{"Line":0}},{"line":98,"address":5111592,"length":1,"stats":{"Line":0}},{"line":100,"address":5111627,"length":1,"stats":{"Line":0}},{"line":103,"address":5111745,"length":1,"stats":{"Line":0}},{"line":107,"address":5112153,"length":1,"stats":{"Line":0}},{"line":110,"address":5112233,"length":1,"stats":{"Line":0}},{"line":111,"address":5112249,"length":1,"stats":{"Line":0}},{"line":112,"address":5112319,"length":1,"stats":{"Line":0}},{"line":113,"address":5112410,"length":1,"stats":{"Line":0}},{"line":115,"address":5112466,"length":1,"stats":{"Line":0}},{"line":117,"address":5112514,"length":1,"stats":{"Line":0}},{"line":118,"address":5112597,"length":1,"stats":{"Line":0}},{"line":120,"address":5102448,"length":1,"stats":{"Line":0}},{"line":124,"address":5112698,"length":1,"stats":{"Line":0}},{"line":125,"address":5113044,"length":1,"stats":{"Line":0}},{"line":127,"address":5113214,"length":1,"stats":{"Line":0}},{"line":130,"address":5113532,"length":1,"stats":{"Line":0}},{"line":131,"address":5113562,"length":1,"stats":{"Line":0}},{"line":132,"address":5113586,"length":1,"stats":{"Line":0}},{"line":135,"address":5113782,"length":1,"stats":{"Line":0}},{"line":136,"address":5113786,"length":1,"stats":{"Line":0}},{"line":142,"address":5114031,"length":1,"stats":{"Line":0}},{"line":145,"address":5112275,"length":1,"stats":{"Line":0}},{"line":146,"address":5112279,"length":1,"stats":{"Line":0}},{"line":148,"address":5114140,"length":1,"stats":{"Line":0}},{"line":149,"address":5114199,"length":1,"stats":{"Line":0}},{"line":151,"address":5114338,"length":1,"stats":{"Line":0}},{"line":153,"address":5114537,"length":1,"stats":{"Line":0}},{"line":154,"address":5114570,"length":1,"stats":{"Line":0}},{"line":160,"address":5298624,"length":1,"stats":{"Line":0}},{"line":165,"address":5298650,"length":1,"stats":{"Line":0}},{"line":166,"address":5298747,"length":1,"stats":{"Line":0}},{"line":167,"address":5298777,"length":1,"stats":{"Line":0}},{"line":171,"address":5300280,"length":1,"stats":{"Line":0}},{"line":173,"address":5300458,"length":1,"stats":{"Line":0}},{"line":174,"address":5300730,"length":1,"stats":{"Line":0}},{"line":175,"address":5300770,"length":1,"stats":{"Line":0}},{"line":178,"address":5300607,"length":1,"stats":{"Line":0}},{"line":182,"address":5302800,"length":1,"stats":{"Line":0}},{"line":183,"address":5302832,"length":1,"stats":{"Line":0}},{"line":185,"address":5302924,"length":1,"stats":{"Line":0}},{"line":186,"address":5303395,"length":1,"stats":{"Line":0}},{"line":187,"address":5303447,"length":1,"stats":{"Line":0}},{"line":188,"address":5303550,"length":1,"stats":{"Line":0}},{"line":189,"address":5303641,"length":1,"stats":{"Line":0}},{"line":190,"address":5303854,"length":1,"stats":{"Line":0}},{"line":193,"address":5304404,"length":1,"stats":{"Line":0}},{"line":196,"address":5305530,"length":1,"stats":{"Line":0}},{"line":199,"address":5306656,"length":1,"stats":{"Line":0}},{"line":202,"address":5303881,"length":1,"stats":{"Line":0}},{"line":205,"address":5308128,"length":1,"stats":{"Line":0}},{"line":206,"address":5308154,"length":1,"stats":{"Line":0}},{"line":207,"address":5308283,"length":1,"stats":{"Line":0}},{"line":208,"address":5308313,"length":1,"stats":{"Line":0}},{"line":209,"address":5308603,"length":1,"stats":{"Line":0}},{"line":212,"address":5309737,"length":1,"stats":{"Line":0}},{"line":214,"address":5309744,"length":1,"stats":{"Line":0}},{"line":216,"address":5310262,"length":1,"stats":{"Line":0}},{"line":217,"address":5310319,"length":1,"stats":{"Line":0}},{"line":218,"address":5310524,"length":1,"stats":{"Line":0}},{"line":220,"address":5310555,"length":1,"stats":{"Line":0}},{"line":221,"address":5310641,"length":1,"stats":{"Line":0}},{"line":222,"address":5310757,"length":1,"stats":{"Line":0}},{"line":223,"address":5310680,"length":1,"stats":{"Line":0}},{"line":224,"address":5311033,"length":1,"stats":{"Line":0}},{"line":226,"address":5311207,"length":1,"stats":{"Line":0}},{"line":227,"address":5311086,"length":1,"stats":{"Line":0}},{"line":228,"address":5311112,"length":1,"stats":{"Line":0}},{"line":231,"address":5311277,"length":1,"stats":{"Line":0}},{"line":232,"address":5311313,"length":1,"stats":{"Line":0}},{"line":240,"address":5311430,"length":1,"stats":{"Line":0}},{"line":243,"address":5312208,"length":1,"stats":{"Line":0}},{"line":247,"address":5312234,"length":1,"stats":{"Line":0}},{"line":248,"address":5312312,"length":1,"stats":{"Line":0}},{"line":252,"address":5312757,"length":1,"stats":{"Line":0}},{"line":253,"address":5313656,"length":1,"stats":{"Line":0}},{"line":254,"address":5313136,"length":1,"stats":{"Line":0}},{"line":255,"address":5313167,"length":1,"stats":{"Line":0}},{"line":257,"address":5313458,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":107},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain","src","sync","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nmod apply;\nmod download;\n\nuse crate::*;\nuse apply::BlockApplicator;\nuse dubp_documents::documents::block::BlockDocumentTrait;\nuse dubp_documents::{BlockHash, BlockNumber};\nuse dup_crypto::keys::*;\nuse dup_currency_params::{CurrencyName, CurrencyParameters};\nuse durs_blockchain_dal::writers::requests::*;\nuse durs_blockchain_dal::{open_memory_db, CertsExpirV10Datas};\nuse durs_common_tools::fatal_error;\nuse durs_wot::NodeId;\nuse failure::Fail;\nuse pbr::ProgressBar;\nuse std::collections::{HashMap, VecDeque};\nuse std::sync::mpsc;\nuse std::time::SystemTime;\nuse std::{fs, thread};\nuse threadpool::ThreadPool;\nuse unwrap::unwrap;\n\n/// Number of sync jobs\npub static NB_SYNC_JOBS: \u0026'static usize = \u00264;\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n/// Block header\npub struct BlockHeader {\n pub number: BlockNumber,\n pub hash: BlockHash,\n pub issuer: PubKey,\n}\n\n#[derive(Debug, Clone)]\n/// Message for main sync thread\npub enum MessForSyncThread {\n Target(CurrencyName, Blockstamp),\n BlockDocument(BlockDocument),\n DownloadFinish(),\n ApplyFinish(),\n}\n\n#[derive(Debug)]\n/// Message for a job thread\npub enum SyncJobsMess {\n ForkWindowSize(usize), // informs block worker of fork window size\n BlocksDBsWriteQuery(BlocksDBsWriteQuery),\n WotsDBsWriteQuery(Blockstamp, Box\u003cCurrencyParameters\u003e, WotsDBsWriteQuery),\n CurrencyDBsWriteQuery(Blockstamp, CurrencyDBsWriteQuery),\n End,\n}\n\n#[derive(Clone, Debug, Fail)]\n/// Local sync error\npub enum LocalSyncError {\n #[fail(\n display = \"The folder you specified contains the blockchain of currency {}, \\\n and your node already contains the blockchain of another currency {}. If you \\\n wish to change currency you must reset your data ('reset data' command) or use a different profile (-p option).\",\n found, expected\n )]\n /// Target currency and local currency are different\n InvalidTargetCurrency {\n expected: CurrencyName,\n found: CurrencyName,\n },\n}\n\n/// Sync from local json files\npub fn local_sync\u003cDC: DursConfTrait\u003e(\n conf: \u0026DC,\n currency: Option\u003c\u0026CurrencyName\u003e,\n profile_path: PathBuf,\n sync_opts: SyncOpt,\n) -\u003e Result\u003c(), LocalSyncError\u003e {\n let SyncOpt {\n cautious_mode: cautious,\n end,\n local_path,\n source,\n unsafe_mode,\n ..\n } = sync_opts;\n\n // get json_files_path\n let json_files_path = unwrap!(local_path);\n if !json_files_path.as_path().exists() {\n fatal_error!(\"duniter json chunks folder don't exist !\");\n }\n\n // Get verification level\n let _verif_level = if cautious {\n info!(\"Start cautious sync...\");\n SyncVerificationLevel::Cautious()\n } else {\n info!(\"Start fast sync...\");\n SyncVerificationLevel::FastSync()\n };\n\n // Create sync_thread channels\n let (sender_sync_thread, recv_sync_thread) = mpsc::channel();\n\n // Create ThreadPool\n let nb_cpus = num_cpus::get();\n let nb_workers = if nb_cpus \u003c *NB_SYNC_JOBS {\n nb_cpus\n } else {\n *NB_SYNC_JOBS\n };\n let pool = ThreadPool::new(nb_workers);\n\n if !json_files_path.is_dir() {\n fatal_error!(\"json_files_path must be a directory\");\n }\n\n // Lauch json reader worker\n download::json_reader_worker::json_reader_worker(\n \u0026pool,\n profile_path.clone(),\n sender_sync_thread.clone(),\n json_files_path,\n end,\n );\n\n // Get target blockstamp and target currency\n let (target_currency, target_blockstamp) =\n if let Ok(MessForSyncThread::Target(target_currency, target_blockstamp)) =\n recv_sync_thread.recv()\n {\n (target_currency, target_blockstamp)\n } else {\n fatal_error!(\"Fatal error : no target blockstamp !\");\n };\n\n // Check the consistency between currency and target_currency\n let currency = if let Some(currency) = currency {\n if currency == \u0026target_currency {\n target_currency\n } else {\n return Err(LocalSyncError::InvalidTargetCurrency {\n expected: currency.clone(),\n found: target_currency,\n });\n }\n } else {\n target_currency\n };\n\n // Update DursConf\n let mut conf = conf.clone();\n conf.set_currency(currency.clone());\n\n // Get databases path\n let db_path = durs_conf::get_blockchain_db_path(profile_path.clone());\n\n // Write new conf\n let mut conf_path = profile_path.clone();\n conf_path.push(durs_conf::constants::CONF_FILENAME);\n durs_conf::write_conf_file(conf_path.as_path(), \u0026conf).expect(\"Fail to write new conf !\");\n\n // Open blocks databases\n let blocks_dbs = BlocksV10DBs::open(Some(\u0026db_path));\n\n // Open forks databases\n let forks_dbs = ForksDBs::open(Some(\u0026db_path));\n\n // Open wot databases\n let wot_databases = WotsV10DBs::open(Some(\u0026db_path));\n\n // Get local current blockstamp\n debug!(\"Get local current blockstamp...\");\n let current_blockstamp: Blockstamp =\n durs_blockchain_dal::readers::block::get_current_blockstamp(\u0026blocks_dbs)\n .expect(\"DALError : fail to get current blockstamp !\")\n .unwrap_or_default();\n debug!(\"Success to get local current blockstamp.\");\n\n // Node is already synchronized ?\n if target_blockstamp.id.0 \u003c= current_blockstamp.id.0 {\n println!(\"Your durs node is already synchronized.\");\n return Ok(());\n }\n\n // Get wot index\n let wot_index: HashMap\u003cPubKey, NodeId\u003e =\n readers::identity::get_wot_index(\u0026wot_databases.identities_db)\n .expect(\"Fatal eror : get_wot_index : Fail to read blockchain databases\");\n\n // Start sync\n let sync_start_time = SystemTime::now();\n\n // Count number of blocks and chunks\n let count_blocks = target_blockstamp.id.0 + 1 - current_blockstamp.id.0;\n let count_chunks = if count_blocks % 250 \u003e 0 {\n (count_blocks / 250) + 1\n } else {\n count_blocks / 250\n };\n println!(\n \"Sync from #{} to #{} :\",\n current_blockstamp.id.0, target_blockstamp.id.0\n );\n info!(\n \"Sync from #{} to #{} :\",\n current_blockstamp.id.0, target_blockstamp.id.0\n );\n\n // Createprogess bar\n let mut apply_pb = ProgressBar::new(count_chunks.into());\n apply_pb.format(\"╢▌▌░╟\");\n\n // Create workers threads channels\n let (sender_blocks_thread, recv_blocks_thread) = mpsc::channel();\n let (sender_wot_thread, recv_wot_thread) = mpsc::channel();\n let (sender_tx_thread, recv_tx_thread) = mpsc::channel();\n\n // Launch blocks_worker thread\n apply::blocks_worker::execute(\n \u0026pool,\n sender_sync_thread.clone(),\n recv_blocks_thread,\n blocks_dbs,\n forks_dbs,\n target_blockstamp,\n apply_pb,\n );\n\n // / Launch wot_worker thread\n apply::wot_worker::execute(\n \u0026pool,\n profile_path.clone(),\n sender_sync_thread.clone(),\n recv_wot_thread,\n );\n\n // Launch tx_worker thread\n apply::txs_worker::execute(\n \u0026pool,\n profile_path.clone(),\n sender_sync_thread.clone(),\n recv_tx_thread,\n );\n\n let main_job_begin = SystemTime::now();\n\n // Open databases\n let dbs_path = durs_conf::get_blockchain_db_path(profile_path.clone());\n let certs_db =\n BinDB::Mem(open_memory_db::\u003cCertsExpirV10Datas\u003e().expect(\"Fail to create memory certs_db\"));\n\n // initialise le BlockApplicator\n let mut block_applicator = BlockApplicator {\n source,\n currency,\n currency_params: None,\n dbs_path,\n verif_inner_hash: !unsafe_mode,\n target_blockstamp,\n current_blockstamp,\n sender_blocks_thread,\n sender_wot_thread,\n sender_tx_thread,\n wot_index,\n wot_databases,\n certs_count: 0,\n blocks_not_expiring: VecDeque::with_capacity(200_000),\n last_block_expiring: -1,\n certs_db,\n wait_begin: SystemTime::now(),\n all_wait_duration: Duration::from_millis(0),\n all_verif_block_hashs_duration: Duration::from_millis(0),\n all_apply_valid_block_duration: Duration::from_millis(0),\n };\n\n // main loop\n let mut got_currency_params = false;\n while let Ok(MessForSyncThread::BlockDocument(block_doc)) = recv_sync_thread.recv() {\n // Get and write currency params\n if !got_currency_params {\n let datas_path = durs_conf::get_datas_path(profile_path.clone());\n if block_doc.number() == BlockNumber(0) {\n block_applicator.currency_params = Some(\n durs_blockchain_dal::readers::currency_params::get_and_write_currency_params(\n \u0026datas_path,\n \u0026block_doc,\n ),\n );\n } else {\n block_applicator.currency_params =\n match dup_currency_params::db::get_currency_params(datas_path) {\n Ok(Some((_currency_name, currency_params))) =\u003e Some(currency_params),\n Ok(None) =\u003e {\n fatal_error!(\"Params db corrupted: please reset data and resync !\")\n }\n Err(_) =\u003e fatal_error!(\"Fail to open params db\"),\n }\n }\n got_currency_params = true;\n\n // Sends fork_window_size to block worker\n if block_applicator\n .sender_blocks_thread\n .send(SyncJobsMess::ForkWindowSize(\n unwrap!(block_applicator.currency_params).fork_window_size,\n ))\n .is_err()\n {\n fatal_error!(\"Fail to communicate with blocks worker thread!\");\n }\n }\n\n block_applicator.apply(block_doc);\n }\n\n // Send end signal to workers threads\n block_applicator\n .sender_blocks_thread\n .send(SyncJobsMess::End)\n .expect(\"Sync : Fail to send End signal to blocks worker !\");\n info!(\"Sync : send End signal to blocks job.\");\n block_applicator\n .sender_wot_thread\n .send(SyncJobsMess::End)\n .expect(\"Sync : Fail to send End signal to wot worker !\");\n info!(\"Sync : send End signal to wot job.\");\n block_applicator\n .sender_tx_thread\n .send(SyncJobsMess::End)\n .expect(\"Sync : Fail to send End signal to writer worker !\");\n info!(\"Sync : send End signal to tx job.\");\n\n // Save wot db\n block_applicator\n .wot_databases\n .wot_db\n .save()\n .expect(\"Fail to save wot db\");\n\n let main_job_duration = SystemTime::now().duration_since(main_job_begin).unwrap()\n - block_applicator.all_wait_duration;\n info!(\n \"main_job_duration={},{:03} seconds.\",\n main_job_duration.as_secs(),\n main_job_duration.subsec_millis()\n );\n info!(\n \"all_verif_block_hashs_duration={},{:03} seconds.\",\n block_applicator.all_verif_block_hashs_duration.as_secs(),\n block_applicator\n .all_verif_block_hashs_duration\n .subsec_millis()\n );\n info!(\n \"all_apply_valid_block_duration={},{:03} seconds.\",\n block_applicator.all_apply_valid_block_duration.as_secs(),\n block_applicator\n .all_apply_valid_block_duration\n .subsec_millis()\n );\n\n // Wait recv two finish signals\n let mut wait_jobs = *NB_SYNC_JOBS - 1;\n while wait_jobs \u003e 0 {\n match recv_sync_thread.recv() {\n Ok(MessForSyncThread::ApplyFinish()) =\u003e wait_jobs -= 1,\n Ok(_) =\u003e thread::sleep(Duration::from_millis(50)),\n Err(_) =\u003e wait_jobs -= 1,\n }\n }\n info!(\"All sync jobs finish.\");\n\n // Log sync duration\n debug!(\"certs_count={}\", block_applicator.certs_count);\n let sync_duration = SystemTime::now().duration_since(sync_start_time).unwrap();\n println!(\n \"Sync {} blocks in {}.{:03} seconds.\",\n count_blocks,\n sync_duration.as_secs(),\n sync_duration.subsec_millis(),\n );\n info!(\n \"Sync {} blocks in {}.{:03} seconds.\",\n count_blocks,\n sync_duration.as_secs(),\n sync_duration.subsec_millis(),\n );\n Ok(())\n}\n","traces":[{"line":85,"address":4638192,"length":1,"stats":{"Line":0}},{"line":92,"address":4638229,"length":1,"stats":{"Line":0}},{"line":93,"address":4638428,"length":1,"stats":{"Line":0}},{"line":94,"address":4638458,"length":1,"stats":{"Line":0}},{"line":95,"address":4638483,"length":1,"stats":{"Line":0}},{"line":96,"address":4638572,"length":1,"stats":{"Line":0}},{"line":101,"address":4638587,"length":1,"stats":{"Line":0}},{"line":102,"address":4638792,"length":1,"stats":{"Line":0}},{"line":107,"address":4640041,"length":1,"stats":{"Line":0}},{"line":109,"address":4640345,"length":1,"stats":{"Line":0}},{"line":112,"address":4640648,"length":1,"stats":{"Line":0}},{"line":116,"address":4640664,"length":1,"stats":{"Line":0}},{"line":119,"address":4640735,"length":1,"stats":{"Line":0}},{"line":120,"address":4640806,"length":1,"stats":{"Line":0}},{"line":121,"address":4640822,"length":1,"stats":{"Line":0}},{"line":123,"address":4640847,"length":1,"stats":{"Line":0}},{"line":125,"address":4640861,"length":1,"stats":{"Line":0}},{"line":127,"address":4640888,"length":1,"stats":{"Line":0}},{"line":134,"address":4642135,"length":1,"stats":{"Line":0}},{"line":135,"address":4642158,"length":1,"stats":{"Line":0}},{"line":136,"address":4642181,"length":1,"stats":{"Line":0}},{"line":137,"address":4642221,"length":1,"stats":{"Line":0}},{"line":141,"address":4642665,"length":1,"stats":{"Line":0}},{"line":142,"address":4642335,"length":1,"stats":{"Line":0}},{"line":143,"address":4642324,"length":1,"stats":{"Line":0}},{"line":145,"address":4642479,"length":1,"stats":{"Line":0}},{"line":151,"address":4643984,"length":1,"stats":{"Line":0}},{"line":152,"address":4644008,"length":1,"stats":{"Line":0}},{"line":153,"address":4644063,"length":1,"stats":{"Line":0}},{"line":155,"address":4644211,"length":1,"stats":{"Line":0}},{"line":156,"address":4644132,"length":1,"stats":{"Line":0}},{"line":157,"address":4644155,"length":1,"stats":{"Line":0}},{"line":161,"address":4644480,"length":1,"stats":{"Line":0}},{"line":165,"address":4644544,"length":1,"stats":{"Line":0}},{"line":166,"address":4644583,"length":1,"stats":{"Line":0}},{"line":169,"address":4644651,"length":1,"stats":{"Line":0}},{"line":172,"address":4644701,"length":1,"stats":{"Line":0}},{"line":173,"address":4644708,"length":1,"stats":{"Line":0}},{"line":174,"address":4644759,"length":1,"stats":{"Line":0}},{"line":177,"address":4644891,"length":1,"stats":{"Line":0}},{"line":180,"address":4644942,"length":1,"stats":{"Line":0}},{"line":183,"address":4644993,"length":1,"stats":{"Line":0}},{"line":188,"address":4645326,"length":1,"stats":{"Line":0}},{"line":194,"address":4645709,"length":1,"stats":{"Line":0}},{"line":196,"address":4645795,"length":1,"stats":{"Line":0}},{"line":201,"address":4645976,"length":1,"stats":{"Line":0}},{"line":202,"address":4646042,"length":1,"stats":{"Line":0}},{"line":205,"address":4646050,"length":1,"stats":{"Line":0}},{"line":208,"address":4646109,"length":1,"stats":{"Line":0}},{"line":209,"address":4646187,"length":1,"stats":{"Line":0}},{"line":210,"address":4646222,"length":1,"stats":{"Line":0}},{"line":212,"address":4646292,"length":1,"stats":{"Line":0}},{"line":216,"address":4646393,"length":1,"stats":{"Line":0}},{"line":220,"address":4646912,"length":1,"stats":{"Line":0}},{"line":224,"address":4647169,"length":1,"stats":{"Line":0}},{"line":225,"address":4647226,"length":1,"stats":{"Line":0}},{"line":228,"address":4647265,"length":1,"stats":{"Line":0}},{"line":229,"address":4647360,"length":1,"stats":{"Line":0}},{"line":230,"address":4647490,"length":1,"stats":{"Line":0}},{"line":235,"address":4647617,"length":1,"stats":{"Line":0}},{"line":236,"address":4647675,"length":1,"stats":{"Line":0}},{"line":237,"address":4647699,"length":1,"stats":{"Line":0}},{"line":238,"address":4647819,"length":1,"stats":{"Line":0}},{"line":239,"address":4647911,"length":1,"stats":{"Line":0}},{"line":240,"address":4647959,"length":1,"stats":{"Line":0}},{"line":246,"address":4648159,"length":1,"stats":{"Line":0}},{"line":247,"address":4648182,"length":1,"stats":{"Line":0}},{"line":248,"address":4648205,"length":1,"stats":{"Line":0}},{"line":254,"address":4648304,"length":1,"stats":{"Line":0}},{"line":255,"address":4648327,"length":1,"stats":{"Line":0}},{"line":256,"address":4648350,"length":1,"stats":{"Line":0}},{"line":259,"address":4648433,"length":1,"stats":{"Line":0}},{"line":262,"address":4648508,"length":1,"stats":{"Line":0}},{"line":264,"address":4648550,"length":1,"stats":{"Line":0}},{"line":267,"address":4649772,"length":1,"stats":{"Line":0}},{"line":268,"address":4648720,"length":1,"stats":{"Line":0}},{"line":269,"address":4648824,"length":1,"stats":{"Line":0}},{"line":270,"address":4648864,"length":1,"stats":{"Line":0}},{"line":271,"address":4648876,"length":1,"stats":{"Line":0}},{"line":272,"address":4648916,"length":1,"stats":{"Line":0}},{"line":273,"address":4648926,"length":1,"stats":{"Line":0}},{"line":274,"address":4648972,"length":1,"stats":{"Line":0}},{"line":275,"address":4649018,"length":1,"stats":{"Line":0}},{"line":276,"address":4649058,"length":1,"stats":{"Line":0}},{"line":277,"address":4649098,"length":1,"stats":{"Line":0}},{"line":278,"address":4649138,"length":1,"stats":{"Line":0}},{"line":279,"address":4649210,"length":1,"stats":{"Line":0}},{"line":281,"address":4649276,"length":1,"stats":{"Line":0}},{"line":283,"address":4649283,"length":1,"stats":{"Line":0}},{"line":284,"address":4649403,"length":1,"stats":{"Line":0}},{"line":285,"address":4649608,"length":1,"stats":{"Line":0}},{"line":286,"address":4649694,"length":1,"stats":{"Line":0}},{"line":287,"address":4649750,"length":1,"stats":{"Line":0}},{"line":291,"address":4650482,"length":1,"stats":{"Line":0}},{"line":292,"address":4650506,"length":1,"stats":{"Line":0}},{"line":294,"address":4650607,"length":1,"stats":{"Line":0}},{"line":295,"address":4650666,"length":1,"stats":{"Line":0}},{"line":296,"address":4650748,"length":1,"stats":{"Line":0}},{"line":297,"address":4650853,"length":1,"stats":{"Line":0}},{"line":298,"address":4650818,"length":1,"stats":{"Line":0}},{"line":304,"address":4653610,"length":1,"stats":{"Line":0}},{"line":305,"address":4650910,"length":1,"stats":{"Line":0}},{"line":306,"address":4650985,"length":1,"stats":{"Line":0}},{"line":313,"address":4653666,"length":1,"stats":{"Line":0}},{"line":316,"address":4653674,"length":1,"stats":{"Line":0}},{"line":327,"address":4655171,"length":1,"stats":{"Line":0}},{"line":331,"address":4655322,"length":1,"stats":{"Line":0}},{"line":336,"address":4655689,"length":1,"stats":{"Line":0}},{"line":341,"address":4656056,"length":1,"stats":{"Line":0}},{"line":348,"address":4656423,"length":1,"stats":{"Line":0}},{"line":354,"address":4656481,"length":1,"stats":{"Line":0}},{"line":355,"address":4656637,"length":1,"stats":{"Line":0}},{"line":358,"address":4656946,"length":1,"stats":{"Line":0}},{"line":359,"address":4656985,"length":1,"stats":{"Line":0}},{"line":363,"address":4657566,"length":1,"stats":{"Line":0}},{"line":364,"address":4657605,"length":1,"stats":{"Line":0}},{"line":370,"address":4658194,"length":1,"stats":{"Line":0}},{"line":371,"address":4658233,"length":1,"stats":{"Line":0}},{"line":377,"address":4658614,"length":1,"stats":{"Line":0}},{"line":378,"address":4658660,"length":1,"stats":{"Line":0}},{"line":379,"address":4658731,"length":1,"stats":{"Line":0}},{"line":380,"address":4658738,"length":1,"stats":{"Line":0}},{"line":381,"address":4658845,"length":1,"stats":{"Line":0}},{"line":382,"address":4658948,"length":1,"stats":{"Line":0}},{"line":388,"address":4659480,"length":1,"stats":{"Line":0}},{"line":389,"address":4659673,"length":1,"stats":{"Line":0}},{"line":392,"address":4660030,"length":1,"stats":{"Line":0}},{"line":393,"address":4659867,"length":1,"stats":{"Line":0}},{"line":394,"address":4659906,"length":1,"stats":{"Line":0}},{"line":398,"address":4660767,"length":1,"stats":{"Line":0}},{"line":399,"address":4660616,"length":1,"stats":{"Line":0}},{"line":400,"address":4660649,"length":1,"stats":{"Line":0}},{"line":402,"address":4661095,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":133},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","entities","block.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::*;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\nuse dubp_documents::Document;\nuse dubp_documents::{BlockNumber, Blockstamp};\nuse durs_wot::NodeId;\nuse std::collections::HashMap;\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\n/// A block as it is saved in a database\npub struct DALBlock {\n /// Block document\n pub block: BlockDocument,\n /// List of certifications that expire in this block.\n /// Warning : BlockNumber contain the emission block, not the written block !\n /// HashMap\u003c(Source, Target), BlockNumber\u003e\n pub expire_certs: Option\u003cHashMap\u003c(NodeId, NodeId), BlockNumber\u003e\u003e,\n}\n\nimpl DALBlock {\n /// Get blockstamp\n pub fn blockstamp(\u0026self) -\u003e Blockstamp {\n self.block.blockstamp()\n }\n /// Get previous blockstamp\n pub fn previous_blockstamp(\u0026self) -\u003e PreviousBlockstamp {\n self.block.previous_blockstamp()\n }\n}\n","traces":[{"line":36,"address":null,"length":0,"stats":{"Line":1}},{"line":37,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}}],"covered":2,"coverable":4},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","entities","fork_tree.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Describe fork tree\n\nuse dubp_documents::{BlockHash, BlockNumber, Blockstamp};\nuse serde::de::{self, Deserialize, Deserializer, Visitor};\nuse serde::{Serialize, Serializer};\nuse std::collections::{HashMap, HashSet};\nuse std::fmt;\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// unique identifier of tree node\npub struct TreeNodeId(pub usize);\n\nimpl Serialize for TreeNodeId {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n serializer.serialize_u32(self.0 as u32)\n }\n}\n\nstruct TreeNodeIdVisitor;\n\nimpl\u003c'de\u003e Visitor\u003c'de\u003e for TreeNodeIdVisitor {\n type Value = TreeNodeId;\n\n fn expecting(\u0026self, formatter: \u0026mut fmt::Formatter) -\u003e fmt::Result {\n formatter.write_str(\"an integer between -2^31 and 2^31\")\n }\n\n fn visit_u8\u003cE\u003e(self, value: u8) -\u003e Result\u003cTreeNodeId, E\u003e\n where\n E: de::Error,\n {\n Ok(TreeNodeId(value as usize))\n }\n\n fn visit_u32\u003cE\u003e(self, value: u32) -\u003e Result\u003cTreeNodeId, E\u003e\n where\n E: de::Error,\n {\n Ok(TreeNodeId(value as usize))\n }\n\n fn visit_u64\u003cE\u003e(self, value: u64) -\u003e Result\u003cTreeNodeId, E\u003e\n where\n E: de::Error,\n {\n use std::usize;\n if value \u003e= usize::MIN as u64 \u0026\u0026 value \u003c= usize::MAX as u64 {\n Ok(TreeNodeId(value as usize))\n } else {\n Err(E::custom(format!(\"u32 out of range: {}\", value)))\n }\n }\n}\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for TreeNodeId {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cTreeNodeId, D::Error\u003e\n where\n D: Deserializer\u003c'de\u003e,\n {\n deserializer.deserialize_u32(TreeNodeIdVisitor)\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n/// Tree node\npub struct TreeNode {\n /// Parent node\n pub parent: Option\u003cTreeNodeId\u003e,\n /// Children nodes\n pub children: Vec\u003cTreeNodeId\u003e,\n /// Node data\n pub data: Blockstamp,\n}\n\nimpl TreeNode {\n /// Instantiate new TreeNode\n pub fn new(parent: Option\u003cTreeNodeId\u003e, data: Blockstamp) -\u003e Self {\n TreeNode {\n parent,\n children: Vec::new(),\n data,\n }\n }\n /// Add child to node\n pub fn add_child(\u0026mut self, id: TreeNodeId) {\n self.children.push(id);\n }\n /// Get node children\n pub fn children(\u0026self) -\u003e \u0026[TreeNodeId] {\n \u0026self.children\n }\n}\n\n#[derive(Debug, Clone, Serialize, Deserialize)]\n/// Tree store all forks branchs\npub struct ForkTree {\n main_branch: HashMap\u003cBlockNumber, TreeNodeId\u003e,\n max_depth: usize,\n nodes: Vec\u003cOption\u003cTreeNode\u003e\u003e,\n removed_blockstamps: Vec\u003cBlockstamp\u003e,\n root: Option\u003cTreeNodeId\u003e,\n sheets: HashSet\u003cTreeNodeId\u003e,\n}\n\nimpl Default for ForkTree {\n #[inline]\n fn default() -\u003e Self {\n ForkTree::new(*dup_currency_params::constants::DEFAULT_FORK_WINDOW_SIZE)\n }\n}\n\nimpl ForkTree {\n /// Instanciate new fork tree\n #[inline]\n pub fn new(max_depth: usize) -\u003e Self {\n ForkTree {\n main_branch: HashMap::with_capacity(max_depth + 1),\n max_depth,\n nodes: Vec::with_capacity(max_depth * 2),\n removed_blockstamps: Vec::with_capacity(max_depth),\n root: None,\n sheets: HashSet::new(),\n }\n }\n /// Set max depth\n #[inline]\n pub fn set_max_depth(\u0026mut self, max_depth: usize) {\n self.max_depth = max_depth;\n }\n /// Get tree size\n #[inline]\n pub fn size(\u0026self) -\u003e usize {\n self.nodes\n .iter()\n .map(|n| if n.is_some() { 1 } else { 0 })\n .sum()\n }\n /// Get root node id\n #[inline]\n pub fn get_root_id(\u0026self) -\u003e Option\u003cTreeNodeId\u003e {\n self.root\n }\n /// Get blockstamp for each sheet of tree\n pub fn get_sheets(\u0026self) -\u003e Vec\u003c(TreeNodeId, Blockstamp)\u003e {\n self.sheets\n .iter()\n .map(|s| (*s, self.get_ref_node(*s).data))\n .collect()\n }\n ///\n pub fn get_removed_blockstamps(\u0026self) -\u003e Vec\u003cBlockstamp\u003e {\n self.removed_blockstamps.clone()\n }\n /// Get specific tree node\n #[inline]\n fn get_node(\u0026self, id: TreeNodeId) -\u003e TreeNode {\n self.nodes\n .get(id.0)\n .cloned()\n .expect(\"Dev error: fork tree : get unexist node !\")\n .expect(\"Dev error: fork tree : get removed node !\")\n }\n /// Get reference to a specific tree node\n #[inline]\n fn get_ref_node(\u0026self, id: TreeNodeId) -\u003e \u0026TreeNode {\n if let Some(Some(ref node)) = self.nodes.get(id.0) {\n node\n } else {\n durs_common_tools::fatal_error!(\"Dev error: fork tree : get unexist or removed node !\");\n }\n }\n /// Get mutable reference to a specific tree node\n #[inline]\n fn get_mut_node(\u0026mut self, id: TreeNodeId) -\u003e \u0026mut TreeNode {\n if let Some(Some(ref mut node)) = self.nodes.get_mut(id.0) {\n node\n } else {\n durs_common_tools::fatal_error!(\"Dev error: fork tree : get unexist or removed node !\");\n }\n }\n /// Get free identifier\n fn get_free_node_id(\u0026self) -\u003e Option\u003cTreeNodeId\u003e {\n let mut new_node_id = None;\n for i in 0..self.nodes.len() {\n if self.nodes.get(i).is_none() {\n new_node_id = Some(TreeNodeId(i));\n }\n }\n\n new_node_id\n }\n /// Get main branch node\n #[inline]\n pub fn get_main_branch_node_id(\u0026self, block_id: BlockNumber) -\u003e Option\u003cTreeNodeId\u003e {\n if let Some(node_id) = self.main_branch.get(\u0026block_id) {\n Some(*node_id)\n } else {\n None\n }\n }\n /// Get main branch block hash\n #[inline]\n pub fn get_main_branch_block_hash(\u0026self, block_id: BlockNumber) -\u003e Option\u003cBlockHash\u003e {\n if let Some(node_id) = self.main_branch.get(\u0026block_id) {\n Some(self.get_ref_node(*node_id).data.hash)\n } else {\n None\n }\n }\n /// Get fork branch nodes ids\n pub fn get_fork_branch_nodes_ids(\u0026self, node_id: TreeNodeId) -\u003e Vec\u003cTreeNodeId\u003e {\n let mut branch = Vec::with_capacity(self.max_depth);\n\n let node = self.get_ref_node(node_id);\n if !self.main_branch.contains_key(\u0026node.data.id)\n || self\n .get_main_branch_block_hash(node.data.id)\n .expect(\"safe unwrap\")\n != node.data.hash\n {\n branch.push(node_id);\n }\n\n if let Some(first_parent_id) = node.parent {\n let mut parent = self.get_ref_node(first_parent_id);\n let mut parent_id = first_parent_id;\n while !self.main_branch.contains_key(\u0026parent.data.id)\n || self\n .get_main_branch_block_hash(parent.data.id)\n .expect(\"safe unwrap\")\n != parent.data.hash\n {\n branch.push(parent_id);\n\n if let Some(next_parent_id) = parent.parent {\n parent = self.get_ref_node(next_parent_id);\n parent_id = next_parent_id;\n } else {\n break;\n }\n }\n }\n\n branch.reverse();\n branch\n }\n /// Get fork branch\n pub fn get_fork_branch(\u0026self, node_id: TreeNodeId) -\u003e Vec\u003cBlockstamp\u003e {\n let mut branch = Vec::with_capacity(self.max_depth);\n let node = self.get_ref_node(node_id);\n branch.push(node.data);\n\n if let Some(parent_id) = node.parent {\n let mut parent = self.get_ref_node(parent_id);\n while !self.main_branch.contains_key(\u0026parent.data.id)\n || self\n .get_main_branch_block_hash(parent.data.id)\n .expect(\"safe unwrap\")\n != parent.data.hash\n {\n branch.push(parent.data);\n\n if let Some(parent_id) = parent.parent {\n parent = self.get_ref_node(parent_id);\n } else {\n break;\n }\n }\n }\n\n branch.reverse();\n branch\n }\n /// Modify the main branch (function to call after a successful roolback)\n pub fn change_main_branch(\n \u0026mut self,\n old_current_blockstamp: Blockstamp,\n new_current_blockstamp: Blockstamp,\n ) {\n let target_node = self\n .find_node_with_blockstamp(\u0026new_current_blockstamp)\n .expect(\"Dev error: change main branch: target branch don't exist !\");\n let selected_fork_branch = self.get_fork_branch_nodes_ids(target_node);\n\n // // Delete the part of the old branch at same level to the new branch\n if !selected_fork_branch.is_empty() {\n let selected_fork_branch_first_node_id =\n selected_fork_branch.get(0).copied().expect(\"safe unwrap\");\n let first_fork_block_id = self.nodes[selected_fork_branch_first_node_id.0]\n .clone()\n .expect(\"Dev error: node must exist !\")\n .data\n .id;\n for block_id in first_fork_block_id.0..=new_current_blockstamp.id.0 {\n self.main_branch.remove(\u0026BlockNumber(block_id));\n }\n }\n // Delete the part of the old branch that exceeds the new branch\n if old_current_blockstamp.id \u003e new_current_blockstamp.id {\n for block_id in (new_current_blockstamp.id.0 + 1)..=old_current_blockstamp.id.0 {\n self.main_branch.remove(\u0026BlockNumber(block_id));\n }\n }\n\n for node_id in selected_fork_branch {\n let node = self.nodes[node_id.0]\n .clone()\n .expect(\"Dev error: node must exist !\");\n self.main_branch.insert(node.data.id, node_id);\n if node.data.id \u003e old_current_blockstamp.id {\n self.pruning();\n }\n }\n }\n /// Find node with specific blockstamp\n pub fn find_node_with_blockstamp(\u0026self, blockstamp: \u0026Blockstamp) -\u003e Option\u003cTreeNodeId\u003e {\n for (node_id, node_opt) in self.nodes.iter().enumerate() {\n if let Some(node) = node_opt {\n if node.data == *blockstamp {\n return Some(TreeNodeId(node_id));\n }\n }\n }\n\n None\n }\n /// Insert new node with specified identifier,\n /// return blockstamps of deleted blocks\n pub fn insert_new_node(\n \u0026mut self,\n data: Blockstamp,\n parent: Option\u003cTreeNodeId\u003e,\n main_branch: bool,\n ) {\n let new_node = TreeNode::new(parent, data);\n let mut new_node_id = self.get_free_node_id();\n\n if new_node_id.is_none() {\n new_node_id = Some(TreeNodeId(self.nodes.len()));\n self.nodes.push(Some(new_node));\n } else {\n self.nodes[new_node_id.expect(\"safe unwrap\").0] = Some(new_node);\n }\n let new_node_id = new_node_id.expect(\"safe unwrap\");\n\n if let Some(parent) = parent {\n // Remove previous sheet\n if !self.sheets.is_empty() {\n self.sheets.remove(\u0026parent);\n }\n // Add new node in parent\n self.get_mut_node(parent).add_child(new_node_id);\n } else if self.root.is_none() {\n self.root = Some(new_node_id);\n } else {\n durs_common_tools::fatal_error!(\"Dev error: Insert root node in not empty tree !\")\n }\n\n self.removed_blockstamps.clear();\n if main_branch {\n self.main_branch.insert(data.id, new_node_id);\n if self.main_branch.len() \u003e self.max_depth {\n self.pruning();\n }\n }\n\n // Add new sheet\n self.sheets.insert(new_node_id);\n }\n\n fn pruning(\u0026mut self) {\n // get root node infos\n let root_node_id = self.root.expect(\"safe unwrap\");\n let root_node = self.get_node(root_node_id);\n let root_node_block_id: BlockNumber = root_node.data.id;\n\n // Shift the tree one step : remove root node and all his children except main child\n self.main_branch.remove(\u0026root_node_block_id);\n self.removed_blockstamps.push(root_node.data);\n let root_node_main_child_id = self\n .main_branch\n .get(\u0026BlockNumber(root_node_block_id.0 + 1))\n .copied()\n .expect(\"safe unwrap\");\n let mut children_to_remove = Vec::new();\n for child_id in root_node.children() {\n if *child_id != root_node_main_child_id {\n children_to_remove.push(*child_id);\n }\n }\n\n for child_id in children_to_remove {\n self.remove_node_children(child_id);\n self.removed_blockstamps\n .push(self.nodes[child_id.0].clone().expect(\"safe unwrap\").data);\n self.nodes[child_id.0] = None;\n self.sheets.remove(\u0026child_id);\n }\n\n // Remove root node\n self.nodes[root_node_id.0] = None;\n self.root = Some(root_node_main_child_id);\n }\n\n /// Return removed blockstamps\n fn remove_node_children(\u0026mut self, id: TreeNodeId) {\n let mut ids_to_rm: Vec\u003cTreeNodeId\u003e = Vec::new();\n\n let mut node = self.get_ref_node(id);\n while node.children.len() \u003c= 1 {\n if let Some(child_id) = node.children.get(0) {\n ids_to_rm.push(*child_id);\n node = self.get_ref_node(*child_id);\n } else {\n break;\n }\n }\n\n for child_id in node.children.clone() {\n self.remove_node_children(child_id);\n }\n\n for node_id in ids_to_rm {\n self.removed_blockstamps\n .push(self.nodes[node_id.0].clone().expect(\"safe unwrap\").data);\n self.nodes[node_id.0] = None;\n self.sheets.remove(\u0026node_id);\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n use dup_currency_params::constants::DEFAULT_FORK_WINDOW_SIZE;\n\n #[test]\n fn insert_root_nodes() {\n let mut tree = ForkTree::default();\n assert_eq!(0, tree.size());\n\n let root_blockstamp = Blockstamp {\n id: BlockNumber(0),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),\n };\n tree.insert_new_node(root_blockstamp, None, true);\n assert_eq!(1, tree.size());\n assert_eq!(\n TreeNodeId(0),\n tree.get_root_id().expect(\"tree without root\")\n );\n assert_eq!(vec![(TreeNodeId(0), root_blockstamp)], tree.get_sheets());\n assert_eq!(None, tree.get_free_node_id());\n assert_eq!(\n Some(TreeNodeId(0)),\n tree.get_main_branch_node_id(BlockNumber(0))\n );\n assert_eq!(\n Some(root_blockstamp.hash),\n tree.get_main_branch_block_hash(BlockNumber(0))\n );\n assert_eq!(\n Some(TreeNodeId(0)),\n tree.find_node_with_blockstamp(\u0026root_blockstamp)\n );\n }\n\n #[test]\n fn insert_some_nodes() {\n let mut tree = ForkTree::default();\n let blockstamps = vec![\n Blockstamp {\n id: BlockNumber(0),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),\n },\n Blockstamp {\n id: BlockNumber(1),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),\n },\n Blockstamp {\n id: BlockNumber(2),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('C')),\n },\n ];\n\n tree.insert_new_node(blockstamps[0], None, true);\n tree.insert_new_node(blockstamps[1], Some(TreeNodeId(0)), true);\n tree.insert_new_node(blockstamps[2], Some(TreeNodeId(1)), true);\n assert_eq!(3, tree.size());\n assert_eq!(\n TreeNodeId(0),\n tree.get_root_id().expect(\"tree without root\")\n );\n assert_eq!(vec![(TreeNodeId(2), blockstamps[2])], tree.get_sheets());\n assert_eq!(None, tree.get_free_node_id());\n for i in 0..=2 {\n assert_eq!(\n Some(TreeNodeId(i)),\n tree.get_main_branch_node_id(BlockNumber(i as u32))\n );\n assert_eq!(\n Some(blockstamps[i].hash),\n tree.get_main_branch_block_hash(BlockNumber(i as u32))\n );\n assert_eq!(\n Some(TreeNodeId(i)),\n tree.find_node_with_blockstamp(\u0026blockstamps[i])\n );\n }\n }\n\n #[test]\n fn insert_fork_blocks() {\n // Fill tree with 10 nodes\n let mut tree = ForkTree::default();\n let blockstamps: Vec\u003cBlockstamp\u003e =\n dubp_documents_tests_tools::mocks::generate_blockstamps(10);\n tree.insert_new_node(blockstamps[0], None, true);\n for i in 1..10 {\n tree.insert_new_node(blockstamps[i], Some(TreeNodeId(i - 1)), true);\n }\n assert_eq!(10, tree.size());\n\n // Insert fork block after block 5\n let fork_blockstamp = Blockstamp {\n id: BlockNumber(6),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),\n };\n tree.insert_new_node(\n fork_blockstamp,\n tree.get_main_branch_node_id(BlockNumber(5)),\n false,\n );\n\n // Check that the tree is indeed 2 sheets\n let sheets = tree.get_sheets();\n assert_eq!(2, sheets.len());\n\n // Check sheets content\n let expected_sheets = vec![\n (\n TreeNodeId(9),\n Blockstamp {\n id: BlockNumber(9),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash_from_byte(9u8)),\n },\n ),\n (TreeNodeId(10), fork_blockstamp),\n ];\n assert!(\n (sheets[0] == expected_sheets[0] \u0026\u0026 sheets[1] == expected_sheets[1])\n || (sheets[0] == expected_sheets[1] \u0026\u0026 sheets[1] == expected_sheets[0])\n );\n\n // Get fork branch\n assert_eq!(vec![fork_blockstamp], tree.get_fork_branch(TreeNodeId(10)));\n assert_eq!(\n vec![TreeNodeId(10)],\n tree.get_fork_branch_nodes_ids(TreeNodeId(10))\n );\n\n // Insert child to fork block\n let child_fork_blockstamp = Blockstamp {\n id: BlockNumber(7),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('C')),\n };\n tree.insert_new_node(child_fork_blockstamp, Some(TreeNodeId(10)), false);\n\n // Check that the tree still has 2 leaves\n let sheets = tree.get_sheets();\n assert_eq!(2, sheets.len());\n\n // Check sheets content\n let expected_sheets = vec![\n (\n TreeNodeId(9),\n Blockstamp {\n id: BlockNumber(9),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash_from_byte(9u8)),\n },\n ),\n (TreeNodeId(11), child_fork_blockstamp),\n ];\n assert!(durs_common_tests_tools::collections::slice_same_elems(\n \u0026expected_sheets,\n \u0026sheets\n ));\n\n // Get fork branch\n assert_eq!(\n vec![fork_blockstamp, child_fork_blockstamp],\n tree.get_fork_branch(TreeNodeId(11))\n );\n assert_eq!(\n vec![TreeNodeId(10), TreeNodeId(11)],\n tree.get_fork_branch_nodes_ids(TreeNodeId(11))\n );\n }\n\n #[test]\n fn insert_more_fork_window_size_nodes() {\n let mut tree = ForkTree::default();\n let blockstamps: Vec\u003cBlockstamp\u003e =\n dubp_documents_tests_tools::mocks::generate_blockstamps(*DEFAULT_FORK_WINDOW_SIZE + 2);\n\n // Fill tree with MAX_DEPTH nodes\n tree.insert_new_node(blockstamps[0], None, true);\n for i in 1..*DEFAULT_FORK_WINDOW_SIZE {\n tree.insert_new_node(blockstamps[i], Some(TreeNodeId(i - 1)), true);\n }\n\n // The tree-root must not have been shifted yet\n assert_eq!(*DEFAULT_FORK_WINDOW_SIZE, tree.size());\n assert_eq!(Some(TreeNodeId(0)), tree.get_root_id());\n\n // Inserting a node that exceeds FORK_WIN_SIZE,\n // the tree size must not be increased and the root must shift\n tree.insert_new_node(\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE],\n Some(TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE - 1)),\n true,\n );\n assert_eq!(*DEFAULT_FORK_WINDOW_SIZE, tree.size());\n assert_eq!(Some(TreeNodeId(1)), tree.get_root_id());\n\n // Repeating the insertion of a node that exceeds FORK_WIN_SIZE,\n // the tree size must still not be increased and the root must still shift\n tree.insert_new_node(\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE + 1],\n Some(TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE)),\n true,\n );\n assert_eq!(*DEFAULT_FORK_WINDOW_SIZE, tree.size());\n assert_eq!(Some(TreeNodeId(2)), tree.get_root_id());\n }\n\n #[test]\n fn test_change_main_branch() {\n let mut tree = ForkTree::default();\n let blockstamps: Vec\u003cBlockstamp\u003e =\n dubp_documents_tests_tools::mocks::generate_blockstamps(*DEFAULT_FORK_WINDOW_SIZE + 2);\n\n // Fill tree with MAX_DEPTH nodes\n tree.insert_new_node(blockstamps[0], None, true);\n for i in 1..*DEFAULT_FORK_WINDOW_SIZE {\n tree.insert_new_node(blockstamps[i], Some(TreeNodeId(i - 1)), true);\n }\n\n // Insert 2 forks blocks after block (MAX_DEPTH - 2)\n let fork_blockstamp = Blockstamp {\n id: BlockNumber(*DEFAULT_FORK_WINDOW_SIZE as u32 - 1),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),\n };\n tree.insert_new_node(\n fork_blockstamp,\n tree.get_main_branch_node_id(BlockNumber(*DEFAULT_FORK_WINDOW_SIZE as u32 - 2)),\n false,\n );\n let fork_blockstamp_2 = Blockstamp {\n id: BlockNumber(*DEFAULT_FORK_WINDOW_SIZE as u32),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),\n };\n tree.insert_new_node(\n fork_blockstamp_2,\n Some(TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE)),\n false,\n );\n\n // Check tree size\n assert_eq!(*DEFAULT_FORK_WINDOW_SIZE + 2, tree.size());\n\n // Check that the root of the shaft has not shifted\n assert_eq!(Some(TreeNodeId(0)), tree.get_root_id());\n\n // Check that the tree is indeed 2 sheets\n let sheets = tree.get_sheets();\n assert_eq!(2, sheets.len());\n\n // Check sheets content\n let expected_sheets = vec![\n (\n TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE - 1),\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE - 1],\n ),\n (TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE + 1), fork_blockstamp_2),\n ];\n println!(\"{:?}\", sheets);\n assert!(durs_common_tests_tools::collections::slice_same_elems(\n \u0026expected_sheets,\n \u0026sheets\n ));\n\n // Switch to fork branch\n tree.change_main_branch(\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE - 1],\n fork_blockstamp_2,\n );\n\n // Check that the shaft still has 2 same sheets\n assert!(durs_common_tests_tools::collections::slice_same_elems(\n \u0026expected_sheets,\n \u0026sheets\n ));\n\n // Check that tree size decrease\n assert_eq!(*DEFAULT_FORK_WINDOW_SIZE + 1, tree.size());\n\n // Check that the root of the tree has shifted\n assert_eq!(Some(TreeNodeId(1)), tree.get_root_id());\n }\n\n}\n","traces":[{"line":29,"address":5739552,"length":1,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":5740320,"length":1,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":5740368,"length":1,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":2}},{"line":98,"address":null,"length":0,"stats":{"Line":2}},{"line":103,"address":null,"length":0,"stats":{"Line":2}},{"line":104,"address":null,"length":0,"stats":{"Line":2}},{"line":107,"address":null,"length":0,"stats":{"Line":2}},{"line":108,"address":null,"length":0,"stats":{"Line":2}},{"line":125,"address":null,"length":0,"stats":{"Line":2}},{"line":126,"address":null,"length":0,"stats":{"Line":2}},{"line":133,"address":null,"length":0,"stats":{"Line":2}},{"line":135,"address":null,"length":0,"stats":{"Line":2}},{"line":137,"address":null,"length":0,"stats":{"Line":2}},{"line":138,"address":null,"length":0,"stats":{"Line":2}},{"line":140,"address":null,"length":0,"stats":{"Line":2}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":3}},{"line":151,"address":null,"length":0,"stats":{"Line":3}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":2}},{"line":163,"address":null,"length":0,"stats":{"Line":2}},{"line":169,"address":null,"length":0,"stats":{"Line":2}},{"line":170,"address":null,"length":0,"stats":{"Line":2}},{"line":174,"address":null,"length":0,"stats":{"Line":2}},{"line":175,"address":null,"length":0,"stats":{"Line":2}},{"line":183,"address":null,"length":0,"stats":{"Line":2}},{"line":184,"address":null,"length":0,"stats":{"Line":2}},{"line":185,"address":null,"length":0,"stats":{"Line":2}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":2}},{"line":193,"address":null,"length":0,"stats":{"Line":2}},{"line":194,"address":null,"length":0,"stats":{"Line":2}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":2}},{"line":201,"address":null,"length":0,"stats":{"Line":2}},{"line":202,"address":null,"length":0,"stats":{"Line":2}},{"line":203,"address":null,"length":0,"stats":{"Line":2}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":208,"address":null,"length":0,"stats":{"Line":2}},{"line":212,"address":null,"length":0,"stats":{"Line":2}},{"line":213,"address":null,"length":0,"stats":{"Line":2}},{"line":214,"address":null,"length":0,"stats":{"Line":2}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":2}},{"line":222,"address":null,"length":0,"stats":{"Line":2}},{"line":223,"address":null,"length":0,"stats":{"Line":2}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":1}},{"line":230,"address":null,"length":0,"stats":{"Line":1}},{"line":232,"address":null,"length":0,"stats":{"Line":1}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":234,"address":null,"length":0,"stats":{"Line":1}},{"line":235,"address":null,"length":0,"stats":{"Line":1}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":1}},{"line":239,"address":null,"length":0,"stats":{"Line":1}},{"line":242,"address":null,"length":0,"stats":{"Line":1}},{"line":243,"address":null,"length":0,"stats":{"Line":1}},{"line":244,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":1}},{"line":246,"address":null,"length":0,"stats":{"Line":1}},{"line":247,"address":null,"length":0,"stats":{"Line":1}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":249,"address":null,"length":0,"stats":{"Line":1}},{"line":251,"address":null,"length":0,"stats":{"Line":1}},{"line":253,"address":null,"length":0,"stats":{"Line":1}},{"line":254,"address":null,"length":0,"stats":{"Line":1}},{"line":255,"address":null,"length":0,"stats":{"Line":1}},{"line":256,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":1}},{"line":263,"address":null,"length":0,"stats":{"Line":1}},{"line":266,"address":null,"length":0,"stats":{"Line":2}},{"line":267,"address":null,"length":0,"stats":{"Line":2}},{"line":268,"address":null,"length":0,"stats":{"Line":2}},{"line":269,"address":null,"length":0,"stats":{"Line":2}},{"line":271,"address":null,"length":0,"stats":{"Line":2}},{"line":272,"address":null,"length":0,"stats":{"Line":2}},{"line":273,"address":null,"length":0,"stats":{"Line":2}},{"line":274,"address":null,"length":0,"stats":{"Line":2}},{"line":275,"address":null,"length":0,"stats":{"Line":2}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":277,"address":null,"length":0,"stats":{"Line":2}},{"line":279,"address":null,"length":0,"stats":{"Line":2}},{"line":281,"address":null,"length":0,"stats":{"Line":2}},{"line":282,"address":null,"length":0,"stats":{"Line":2}},{"line":283,"address":null,"length":0,"stats":{"Line":0}},{"line":284,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":null,"length":0,"stats":{"Line":2}},{"line":290,"address":null,"length":0,"stats":{"Line":2}},{"line":293,"address":null,"length":0,"stats":{"Line":1}},{"line":298,"address":null,"length":0,"stats":{"Line":1}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":1}},{"line":304,"address":null,"length":0,"stats":{"Line":1}},{"line":305,"address":null,"length":0,"stats":{"Line":0}},{"line":306,"address":null,"length":0,"stats":{"Line":1}},{"line":307,"address":null,"length":0,"stats":{"Line":1}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":311,"address":null,"length":0,"stats":{"Line":1}},{"line":312,"address":null,"length":0,"stats":{"Line":1}},{"line":313,"address":null,"length":0,"stats":{"Line":1}},{"line":317,"address":null,"length":0,"stats":{"Line":1}},{"line":318,"address":null,"length":0,"stats":{"Line":0}},{"line":319,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":1}},{"line":324,"address":null,"length":0,"stats":{"Line":1}},{"line":325,"address":null,"length":0,"stats":{"Line":0}},{"line":326,"address":null,"length":0,"stats":{"Line":0}},{"line":327,"address":null,"length":0,"stats":{"Line":1}},{"line":328,"address":null,"length":0,"stats":{"Line":1}},{"line":329,"address":null,"length":0,"stats":{"Line":1}},{"line":334,"address":null,"length":0,"stats":{"Line":2}},{"line":335,"address":null,"length":0,"stats":{"Line":2}},{"line":336,"address":null,"length":0,"stats":{"Line":2}},{"line":337,"address":null,"length":0,"stats":{"Line":2}},{"line":338,"address":null,"length":0,"stats":{"Line":2}},{"line":343,"address":null,"length":0,"stats":{"Line":0}},{"line":347,"address":null,"length":0,"stats":{"Line":2}},{"line":353,"address":null,"length":0,"stats":{"Line":2}},{"line":354,"address":null,"length":0,"stats":{"Line":2}},{"line":356,"address":null,"length":0,"stats":{"Line":2}},{"line":357,"address":null,"length":0,"stats":{"Line":2}},{"line":358,"address":null,"length":0,"stats":{"Line":2}},{"line":359,"address":null,"length":0,"stats":{"Line":0}},{"line":360,"address":null,"length":0,"stats":{"Line":0}},{"line":362,"address":null,"length":0,"stats":{"Line":2}},{"line":364,"address":null,"length":0,"stats":{"Line":2}},{"line":366,"address":null,"length":0,"stats":{"Line":2}},{"line":367,"address":null,"length":0,"stats":{"Line":2}},{"line":370,"address":null,"length":0,"stats":{"Line":2}},{"line":371,"address":null,"length":0,"stats":{"Line":2}},{"line":372,"address":null,"length":0,"stats":{"Line":2}},{"line":373,"address":null,"length":0,"stats":{"Line":0}},{"line":374,"address":null,"length":0,"stats":{"Line":0}},{"line":377,"address":null,"length":0,"stats":{"Line":2}},{"line":378,"address":null,"length":0,"stats":{"Line":2}},{"line":379,"address":null,"length":0,"stats":{"Line":2}},{"line":380,"address":null,"length":0,"stats":{"Line":2}},{"line":381,"address":null,"length":0,"stats":{"Line":2}},{"line":386,"address":null,"length":0,"stats":{"Line":2}},{"line":389,"address":null,"length":0,"stats":{"Line":2}},{"line":391,"address":null,"length":0,"stats":{"Line":2}},{"line":392,"address":null,"length":0,"stats":{"Line":2}},{"line":393,"address":null,"length":0,"stats":{"Line":2}},{"line":396,"address":null,"length":0,"stats":{"Line":2}},{"line":397,"address":null,"length":0,"stats":{"Line":2}},{"line":398,"address":null,"length":0,"stats":{"Line":2}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":400,"address":null,"length":0,"stats":{"Line":2}},{"line":401,"address":null,"length":0,"stats":{"Line":0}},{"line":402,"address":null,"length":0,"stats":{"Line":0}},{"line":403,"address":null,"length":0,"stats":{"Line":2}},{"line":404,"address":null,"length":0,"stats":{"Line":2}},{"line":405,"address":null,"length":0,"stats":{"Line":2}},{"line":406,"address":null,"length":0,"stats":{"Line":1}},{"line":410,"address":null,"length":0,"stats":{"Line":2}},{"line":411,"address":null,"length":0,"stats":{"Line":1}},{"line":412,"address":null,"length":0,"stats":{"Line":1}},{"line":413,"address":null,"length":0,"stats":{"Line":1}},{"line":414,"address":null,"length":0,"stats":{"Line":1}},{"line":415,"address":null,"length":0,"stats":{"Line":1}},{"line":419,"address":null,"length":0,"stats":{"Line":2}},{"line":420,"address":null,"length":0,"stats":{"Line":2}},{"line":424,"address":null,"length":0,"stats":{"Line":1}},{"line":425,"address":null,"length":0,"stats":{"Line":1}},{"line":427,"address":null,"length":0,"stats":{"Line":1}},{"line":428,"address":null,"length":0,"stats":{"Line":1}},{"line":429,"address":null,"length":0,"stats":{"Line":1}},{"line":430,"address":null,"length":0,"stats":{"Line":1}},{"line":431,"address":null,"length":0,"stats":{"Line":1}},{"line":432,"address":null,"length":0,"stats":{"Line":0}},{"line":433,"address":null,"length":0,"stats":{"Line":1}},{"line":437,"address":null,"length":0,"stats":{"Line":1}},{"line":438,"address":null,"length":0,"stats":{"Line":0}},{"line":441,"address":null,"length":0,"stats":{"Line":1}},{"line":442,"address":null,"length":0,"stats":{"Line":1}},{"line":443,"address":null,"length":0,"stats":{"Line":1}},{"line":444,"address":null,"length":0,"stats":{"Line":1}},{"line":445,"address":null,"length":0,"stats":{"Line":1}},{"line":457,"address":4593776,"length":1,"stats":{"Line":2}},{"line":458,"address":4593783,"length":1,"stats":{"Line":1}},{"line":459,"address":4593821,"length":1,"stats":{"Line":1}},{"line":461,"address":4594390,"length":1,"stats":{"Line":1}},{"line":462,"address":4594112,"length":1,"stats":{"Line":1}},{"line":463,"address":4594123,"length":1,"stats":{"Line":1}},{"line":465,"address":4594436,"length":1,"stats":{"Line":1}},{"line":466,"address":4594547,"length":1,"stats":{"Line":1}},{"line":467,"address":4595121,"length":1,"stats":{"Line":1}},{"line":469,"address":4594828,"length":1,"stats":{"Line":1}},{"line":471,"address":4595580,"length":1,"stats":{"Line":1}},{"line":472,"address":4596310,"length":1,"stats":{"Line":1}},{"line":473,"address":4596895,"length":1,"stats":{"Line":1}},{"line":475,"address":4596814,"length":1,"stats":{"Line":1}},{"line":477,"address":4597465,"length":1,"stats":{"Line":1}},{"line":478,"address":4597344,"length":1,"stats":{"Line":1}},{"line":479,"address":4597416,"length":1,"stats":{"Line":1}},{"line":481,"address":4597928,"length":1,"stats":{"Line":1}},{"line":483,"address":4597885,"length":1,"stats":{"Line":1}},{"line":488,"address":4598432,"length":1,"stats":{"Line":2}},{"line":489,"address":4598439,"length":1,"stats":{"Line":1}},{"line":490,"address":4598479,"length":1,"stats":{"Line":1}},{"line":491,"address":4598577,"length":1,"stats":{"Line":1}},{"line":492,"address":4598484,"length":1,"stats":{"Line":1}},{"line":493,"address":4598495,"length":1,"stats":{"Line":1}},{"line":495,"address":4598708,"length":1,"stats":{"Line":1}},{"line":496,"address":4598623,"length":1,"stats":{"Line":1}},{"line":497,"address":4598634,"length":1,"stats":{"Line":1}},{"line":499,"address":4598821,"length":1,"stats":{"Line":1}},{"line":500,"address":4598754,"length":1,"stats":{"Line":1}},{"line":501,"address":4598765,"length":1,"stats":{"Line":1}},{"line":505,"address":4598999,"length":1,"stats":{"Line":1}},{"line":506,"address":4599146,"length":1,"stats":{"Line":1}},{"line":507,"address":4599304,"length":1,"stats":{"Line":1}},{"line":508,"address":4599470,"length":1,"stats":{"Line":1}},{"line":509,"address":4600044,"length":1,"stats":{"Line":1}},{"line":511,"address":4599751,"length":1,"stats":{"Line":1}},{"line":513,"address":4600503,"length":1,"stats":{"Line":1}},{"line":514,"address":4601300,"length":1,"stats":{"Line":1}},{"line":515,"address":4601804,"length":1,"stats":{"Line":1}},{"line":516,"address":4602229,"length":1,"stats":{"Line":1}},{"line":517,"address":4602075,"length":1,"stats":{"Line":1}},{"line":518,"address":4602119,"length":1,"stats":{"Line":1}},{"line":520,"address":4602830,"length":1,"stats":{"Line":1}},{"line":521,"address":4602671,"length":1,"stats":{"Line":1}},{"line":522,"address":4602778,"length":1,"stats":{"Line":1}},{"line":524,"address":4603386,"length":1,"stats":{"Line":1}},{"line":525,"address":4603246,"length":1,"stats":{"Line":1}},{"line":526,"address":4603290,"length":1,"stats":{"Line":1}},{"line":532,"address":4603904,"length":1,"stats":{"Line":2}},{"line":534,"address":4603911,"length":1,"stats":{"Line":1}},{"line":536,"address":4603941,"length":1,"stats":{"Line":1}},{"line":537,"address":4603965,"length":1,"stats":{"Line":1}},{"line":538,"address":4604124,"length":1,"stats":{"Line":1}},{"line":539,"address":4604387,"length":1,"stats":{"Line":1}},{"line":541,"address":4604430,"length":1,"stats":{"Line":1}},{"line":544,"address":4605144,"length":1,"stats":{"Line":1}},{"line":545,"address":4604866,"length":1,"stats":{"Line":1}},{"line":546,"address":4604877,"length":1,"stats":{"Line":1}},{"line":548,"address":4605320,"length":1,"stats":{"Line":1}},{"line":549,"address":4605190,"length":1,"stats":{"Line":1}},{"line":550,"address":4605236,"length":1,"stats":{"Line":1}},{"line":555,"address":4605343,"length":1,"stats":{"Line":1}},{"line":556,"address":4605358,"length":1,"stats":{"Line":1}},{"line":559,"address":4605659,"length":1,"stats":{"Line":1}},{"line":561,"address":4605664,"length":1,"stats":{"Line":1}},{"line":562,"address":4605980,"length":1,"stats":{"Line":1}},{"line":563,"address":4605676,"length":1,"stats":{"Line":1}},{"line":564,"address":4605687,"length":1,"stats":{"Line":1}},{"line":567,"address":4606088,"length":1,"stats":{"Line":1}},{"line":569,"address":4606377,"length":1,"stats":{"Line":1}},{"line":570,"address":4606322,"length":1,"stats":{"Line":1}},{"line":571,"address":4606359,"length":1,"stats":{"Line":0}},{"line":575,"address":4606873,"length":1,"stats":{"Line":1}},{"line":576,"address":4607644,"length":1,"stats":{"Line":0}},{"line":577,"address":4607544,"length":1,"stats":{"Line":1}},{"line":578,"address":4607601,"length":1,"stats":{"Line":1}},{"line":582,"address":4608227,"length":1,"stats":{"Line":1}},{"line":583,"address":4608160,"length":1,"stats":{"Line":1}},{"line":584,"address":4608171,"length":1,"stats":{"Line":1}},{"line":586,"address":4608273,"length":1,"stats":{"Line":1}},{"line":589,"address":4608417,"length":1,"stats":{"Line":1}},{"line":590,"address":4608432,"length":1,"stats":{"Line":1}},{"line":593,"address":4608733,"length":1,"stats":{"Line":1}},{"line":595,"address":4608738,"length":1,"stats":{"Line":1}},{"line":596,"address":4609054,"length":1,"stats":{"Line":1}},{"line":597,"address":4608750,"length":1,"stats":{"Line":1}},{"line":598,"address":4608761,"length":1,"stats":{"Line":1}},{"line":601,"address":4609162,"length":1,"stats":{"Line":1}},{"line":603,"address":4609522,"length":1,"stats":{"Line":1}},{"line":604,"address":4609392,"length":1,"stats":{"Line":1}},{"line":605,"address":4609455,"length":1,"stats":{"Line":1}},{"line":609,"address":4609818,"length":1,"stats":{"Line":0}},{"line":610,"address":4609588,"length":1,"stats":{"Line":1}},{"line":611,"address":4609775,"length":1,"stats":{"Line":1}},{"line":613,"address":4610420,"length":1,"stats":{"Line":0}},{"line":614,"address":4610302,"length":1,"stats":{"Line":1}},{"line":615,"address":4610377,"length":1,"stats":{"Line":1}},{"line":620,"address":4611328,"length":1,"stats":{"Line":2}},{"line":621,"address":4611335,"length":1,"stats":{"Line":1}},{"line":623,"address":4611372,"length":1,"stats":{"Line":1}},{"line":626,"address":4611444,"length":1,"stats":{"Line":1}},{"line":627,"address":4611591,"length":1,"stats":{"Line":1}},{"line":628,"address":4611863,"length":1,"stats":{"Line":1}},{"line":632,"address":4611898,"length":1,"stats":{"Line":1}},{"line":633,"address":4612369,"length":1,"stats":{"Line":1}},{"line":637,"address":4613255,"length":1,"stats":{"Line":1}},{"line":638,"address":4613084,"length":1,"stats":{"Line":1}},{"line":639,"address":4613179,"length":1,"stats":{"Line":1}},{"line":642,"address":4613300,"length":1,"stats":{"Line":1}},{"line":643,"address":4613608,"length":1,"stats":{"Line":1}},{"line":647,"address":4614485,"length":1,"stats":{"Line":1}},{"line":648,"address":4614330,"length":1,"stats":{"Line":1}},{"line":649,"address":4614436,"length":1,"stats":{"Line":1}},{"line":652,"address":4614530,"length":1,"stats":{"Line":1}},{"line":653,"address":4614826,"length":1,"stats":{"Line":1}},{"line":657,"address":4615616,"length":1,"stats":{"Line":2}},{"line":658,"address":4615623,"length":1,"stats":{"Line":1}},{"line":660,"address":4615660,"length":1,"stats":{"Line":1}},{"line":663,"address":4615732,"length":1,"stats":{"Line":1}},{"line":664,"address":4615879,"length":1,"stats":{"Line":1}},{"line":665,"address":4616151,"length":1,"stats":{"Line":1}},{"line":669,"address":4616497,"length":1,"stats":{"Line":1}},{"line":670,"address":4616193,"length":1,"stats":{"Line":1}},{"line":671,"address":4616402,"length":1,"stats":{"Line":1}},{"line":673,"address":4616742,"length":1,"stats":{"Line":1}},{"line":674,"address":4616575,"length":1,"stats":{"Line":1}},{"line":675,"address":4616620,"length":1,"stats":{"Line":1}},{"line":678,"address":4616824,"length":1,"stats":{"Line":1}},{"line":679,"address":4616749,"length":1,"stats":{"Line":1}},{"line":680,"address":4616768,"length":1,"stats":{"Line":1}},{"line":682,"address":4616965,"length":1,"stats":{"Line":1}},{"line":683,"address":4616870,"length":1,"stats":{"Line":1}},{"line":684,"address":4616916,"length":1,"stats":{"Line":1}},{"line":689,"address":4617014,"length":1,"stats":{"Line":1}},{"line":692,"address":4617350,"length":1,"stats":{"Line":1}},{"line":695,"address":4618081,"length":1,"stats":{"Line":1}},{"line":696,"address":4618096,"length":1,"stats":{"Line":1}},{"line":699,"address":4618387,"length":1,"stats":{"Line":1}},{"line":701,"address":4618409,"length":1,"stats":{"Line":1}},{"line":702,"address":4618683,"length":1,"stats":{"Line":1}},{"line":704,"address":4618874,"length":1,"stats":{"Line":1}},{"line":706,"address":4619145,"length":1,"stats":{"Line":1}},{"line":707,"address":4619443,"length":1,"stats":{"Line":1}},{"line":708,"address":4619325,"length":1,"stats":{"Line":1}},{"line":709,"address":4619388,"length":1,"stats":{"Line":1}},{"line":713,"address":4619688,"length":1,"stats":{"Line":1}},{"line":714,"address":4619512,"length":1,"stats":{"Line":1}},{"line":715,"address":4619618,"length":1,"stats":{"Line":1}},{"line":719,"address":4619821,"length":1,"stats":{"Line":1}},{"line":720,"address":4619703,"length":1,"stats":{"Line":1}},{"line":721,"address":4619766,"length":1,"stats":{"Line":1}},{"line":725,"address":4619890,"length":1,"stats":{"Line":1}},{"line":728,"address":4620214,"length":1,"stats":{"Line":1}}],"covered":299,"coverable":354},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","entities","sources.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dubp_documents::documents::transaction::*;\nuse dubp_documents::BlockNumber;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::PubKey;\nuse durs_common_tools::fatal_error;\nuse std::cmp::Ordering;\nuse std::ops::{Add, Sub};\n\n#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, PartialOrd, Serialize)]\n/// Source amount\npub struct SourceAmount(pub TxAmount, pub TxBase);\n\nimpl Default for SourceAmount {\n fn default() -\u003e SourceAmount {\n SourceAmount(TxAmount(0), TxBase(0))\n }\n}\n\nimpl Ord for SourceAmount {\n fn cmp(\u0026self, other: \u0026SourceAmount) -\u003e Ordering {\n if self.1 == other.1 {\n self.0.cmp(\u0026other.0)\n } else {\n self.1.cmp(\u0026other.1)\n }\n }\n}\n\nimpl Add for SourceAmount {\n type Output = SourceAmount;\n fn add(self, s2: SourceAmount) -\u003e Self::Output {\n let (mut s_min, s_max) = if self.1 \u003e s2.1 {\n (s2, self)\n } else {\n (self, s2)\n };\n\n while s_min.1 \u003c s_max.1 {\n (s_min.0).0 /= 10;\n (s_min.1).0 += 1;\n }\n\n SourceAmount(s_min.0 + s_max.0, s_max.1)\n }\n}\n\nimpl Sub for SourceAmount {\n type Output = SourceAmount;\n fn sub(self, s2: SourceAmount) -\u003e Self::Output {\n let (mut s_min, s_max) = if self.1 \u003e s2.1 {\n (s2, self)\n } else {\n (self, s2)\n };\n\n while s_min.1 \u003c s_max.1 {\n (s_min.0).0 /= 10;\n (s_min.1).0 += 1;\n }\n\n SourceAmount(s_min.0 - s_max.0, s_max.1)\n }\n}\n\n#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]\n/// UTXOIndexV10\npub struct UTXOIndexV10(pub Hash, pub TxIndex);\n\n/// UTXO content V10\npub type UTXOContentV10 = TransactionOutput;\n\n#[derive(Debug, Clone, Deserialize, Serialize)]\n/// V10 Unused Transaction Output\npub struct UTXOV10(pub UTXOIndexV10, pub UTXOContentV10);\n\nimpl UTXOV10 {\n /// UTXO conditions\n pub fn get_conditions(\u0026self) -\u003e UTXOConditionsGroup {\n self.1.conditions.conditions.clone()\n }\n /// UTXO amount\n pub fn get_amount(\u0026self) -\u003e SourceAmount {\n SourceAmount(self.1.amount, self.1.base)\n }\n}\n\n#[derive(Debug, Clone, Deserialize, Serialize)]\n/// Unused Transaction Output\npub enum UTXO {\n /// V10\n V10(UTXOV10),\n /// V11\n V11(),\n}\n\nimpl UTXO {\n /// UTXO conditions\n pub fn get_conditions(\u0026self) -\u003e UTXOConditionsGroup {\n match *self {\n UTXO::V10(ref utxo_v10) =\u003e utxo_v10.get_conditions(),\n _ =\u003e fatal_error!(\"UTXO version not supported !\"),\n }\n }\n /// UTXO amount\n pub fn get_amount(\u0026self) -\u003e SourceAmount {\n match *self {\n UTXO::V10(ref utxo_v10) =\u003e utxo_v10.get_amount(),\n _ =\u003e fatal_error!(\"UTXO version not supported !\"),\n }\n }\n}\n\n#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]\n/// Index of a V10 source\npub enum SourceIndexV10 {\n /// unused Transaction Output\n UTXO(UTXOIndexV10),\n /// universal Dividend\n UD(PubKey, BlockNumber),\n}\n","traces":[{"line":29,"address":null,"length":0,"stats":{"Line":1}},{"line":30,"address":null,"length":0,"stats":{"Line":1}},{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":0}},{"line":37,"address":null,"length":0,"stats":{"Line":0}},{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":46,"address":null,"length":0,"stats":{"Line":1}},{"line":47,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":1}},{"line":53,"address":null,"length":0,"stats":{"Line":1}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}}],"covered":16,"coverable":37},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","filters","identities.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Define all filters applicable to identities\n\nuse super::PagingFilter;\nuse dup_crypto::keys::*;\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n/// Identities filter\npub struct IdentitiesFilter {\n /// Pagination parameters\n pub paging: PagingFilter,\n /// Filter identities by public key\n pub by_pubkey: Option\u003cPubKey\u003e,\n}\n\nimpl Default for IdentitiesFilter {\n fn default() -\u003e Self {\n IdentitiesFilter {\n paging: PagingFilter::default(),\n by_pubkey: None,\n }\n }\n}\n\nimpl IdentitiesFilter {\n /// Create \"by pubkey\" filter\n pub fn by_pubkey(pubkey: PubKey) -\u003e Self {\n IdentitiesFilter {\n paging: PagingFilter::default(),\n by_pubkey: Some(pubkey),\n }\n }\n}\n","traces":[{"line":31,"address":null,"length":0,"stats":{"Line":1}},{"line":33,"address":null,"length":0,"stats":{"Line":1}},{"line":41,"address":null,"length":0,"stats":{"Line":1}},{"line":43,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":1}}],"covered":5,"coverable":5},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","filters","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\npub mod identities;\n\nuse dubp_documents::BlockNumber;\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\n/// Pagination parameters\npub struct PagingFilter {\n /// Retrieve only the elements created after this block\n pub from: BlockNumber,\n /// Retrieve only the elements created before this block\n pub to: Option\u003cBlockNumber\u003e,\n /// Number of elements per page\n pub page_size: usize,\n /// Number of the page requested\n pub page_number: usize,\n}\n\nimpl Default for PagingFilter {\n fn default() -\u003e Self {\n PagingFilter {\n from: BlockNumber(0),\n to: None,\n page_size: *crate::constants::DEFAULT_PAGE_SIZE,\n page_number: 0,\n }\n }\n}\n\nimpl PagingFilter {\n #[inline]\n /// Checks if a given element has been created in the requested period\n pub fn check_created_on(\u0026self, created_on: BlockNumber, current_block_id: BlockNumber) -\u003e bool {\n created_on \u003e= self.from \u0026\u0026 created_on \u003c= self.to.unwrap_or(current_block_id)\n }\n #[inline]\n /// Checks if a given element index is located in the current page\n pub fn is_in_page(\u0026self, i: usize) -\u003e bool {\n i \u003e= self.page_size * self.page_number \u0026\u0026 i \u003c self.page_size * (self.page_number + 1)\n }\n}\n","traces":[{"line":34,"address":null,"length":0,"stats":{"Line":1}},{"line":36,"address":null,"length":0,"stats":{"Line":1}},{"line":38,"address":null,"length":0,"stats":{"Line":1}},{"line":47,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":1}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}}],"covered":5,"coverable":7},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Datas Access Layer\n\n#![allow(clippy::large_enum_variant)]\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde_derive;\n\n/// Define crate constants\npub mod constants;\n\n/// Contains all entities stored in databases\npub mod entities;\n\n/// Define all filters applicable to entities\npub mod filters;\n\n/// Contains all read databases functions\npub mod readers;\n\n/// Tools\npub mod tools;\n\n/// Contains all write databases functions\npub mod writers;\n\nuse dubp_documents::documents::transaction::*;\nuse dubp_documents::{BlockNumber, Blockstamp, PreviousBlockstamp};\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\nuse durs_wot::data::{rusty::RustyWebOfTrust, NodeId};\nuse fnv::FnvHashMap;\nuse rustbreak::backend::{FileBackend, MemoryBackend};\nuse rustbreak::error::{RustbreakError, RustbreakErrorKind};\nuse rustbreak::{deser::Bincode, Database, FileDatabase, MemoryDatabase};\nuse serde::de::DeserializeOwned;\nuse serde::Serialize;\nuse std::collections::{HashMap, HashSet};\nuse std::default::Default;\nuse std::fmt::Debug;\nuse std::fs;\nuse std::panic::UnwindSafe;\nuse std::path::PathBuf;\n\nuse crate::entities::block::DALBlock;\nuse crate::entities::identity::DALIdentity;\nuse crate::entities::sources::{SourceAmount, UTXOContentV10, UTXOIndexV10};\nuse crate::writers::transaction::DALTxV10;\n\n/// All blocks of local blockchain indexed by block number\npub type LocalBlockchainV10Datas = FnvHashMap\u003cBlockNumber, DALBlock\u003e;\n/// Forks tree meta datas (block number and hash only)\npub type ForksTreeV10Datas = entities::fork_tree::ForkTree;\n/// Forks blocks referenced in tree indexed by their blockstamp\npub type ForksBlocksV10Datas = HashMap\u003cBlockstamp, DALBlock\u003e;\n/// Blocks orphaned (no parent block) indexed by their previous blockstamp\npub type OrphanBlocksV10Datas = HashMap\u003cPreviousBlockstamp, Vec\u003cDALBlock\u003e\u003e;\n/// Database containing the wot graph (each node of the graph in an u32)\npub type WotDB = RustyWebOfTrust;\n/// V10 Identities indexed by public key\npub type IdentitiesV10Datas = HashMap\u003cPubKey, DALIdentity\u003e;\n/// Memberships sorted by created block\npub type MsExpirV10Datas = FnvHashMap\u003cBlockNumber, HashSet\u003cNodeId\u003e\u003e;\n/// Certifications sorted by created block\npub type CertsExpirV10Datas = FnvHashMap\u003cBlockNumber, HashSet\u003c(NodeId, NodeId)\u003e\u003e;\n/// V10 Transactions indexed by their hashs\npub type TxV10Datas = HashMap\u003cHash, DALTxV10\u003e;\n/// V10 Unused Transaction Output (=sources)\npub type UTXOsV10Datas = HashMap\u003cUTXOIndexV10, UTXOContentV10\u003e;\n/// V10 UDs sources\npub type UDsV10Datas = HashMap\u003cPubKey, HashSet\u003cBlockNumber\u003e\u003e;\n/// V10 Balances accounts\npub type BalancesV10Datas = HashMap\u003cUTXOConditionsGroup, (SourceAmount, HashSet\u003cUTXOIndexV10\u003e)\u003e;\n\n#[derive(Debug)]\n/// Database\npub enum BinDB\u003cD: Serialize + DeserializeOwned + Debug + Default + Clone + Send\u003e {\n /// File database\n File(Database\u003cD, FileBackend, Bincode\u003e),\n /// Memory database\n Mem(Database\u003cD, MemoryBackend, Bincode\u003e),\n}\n\nimpl\u003cD: Serialize + DeserializeOwned + Debug + Default + Clone + Send\u003e BinDB\u003cD\u003e {\n /// Flush the data structure to the backend\n pub fn save(\u0026self) -\u003e Result\u003c(), RustbreakError\u003e {\n match *self {\n BinDB::File(ref file_db) =\u003e file_db.save(),\n BinDB::Mem(ref mem_db) =\u003e mem_db.save(),\n }\n }\n /// Read lock the database and get write access to the Data container\n /// This gives you a read-only lock on the database. You can have as many readers in parallel as you wish.\n pub fn read\u003cT, R\u003e(\u0026self, task: T) -\u003e Result\u003cR, RustbreakError\u003e\n where\n T: FnOnce(\u0026D) -\u003e R,\n {\n match *self {\n BinDB::File(ref file_db) =\u003e file_db.read(task),\n BinDB::Mem(ref mem_db) =\u003e mem_db.read(task),\n }\n }\n /// Write lock the database and get write access to the Data container\n /// This gives you an exclusive lock on the memory object. Trying to open the database in writing will block if it is currently being written to.\n pub fn write\u003cT\u003e(\u0026self, task: T) -\u003e Result\u003c(), RustbreakError\u003e\n where\n T: FnOnce(\u0026mut D),\n {\n match *self {\n BinDB::File(ref file_db) =\u003e file_db.write(task),\n BinDB::Mem(ref mem_db) =\u003e mem_db.write(task),\n }\n }\n /// Write lock the database and get write access to the Data container in a safe way (clone of the internal data is made).\n pub fn write_safe\u003cT\u003e(\u0026self, task: T) -\u003e Result\u003c(), RustbreakError\u003e\n where\n T: FnOnce(\u0026mut D) + UnwindSafe,\n {\n match *self {\n BinDB::File(ref file_db) =\u003e file_db.write_safe(task),\n BinDB::Mem(ref mem_db) =\u003e mem_db.write_safe(task),\n }\n }\n /// Load the Data from the backend\n pub fn load(\u0026self) -\u003e Result\u003c(), RustbreakError\u003e {\n match *self {\n BinDB::File(ref file_db) =\u003e file_db.load(),\n BinDB::Mem(ref mem_db) =\u003e mem_db.load(),\n }\n }\n}\n\n#[derive(Debug)]\n/// Set of databases storing block information\npub struct BlocksV10DBs {\n /// Local blockchain database\n pub blockchain_db: BinDB\u003cLocalBlockchainV10Datas\u003e,\n}\n\nimpl BlocksV10DBs {\n /// Open blocks databases from their respective files\n pub fn open(db_path: Option\u003c\u0026PathBuf\u003e) -\u003e BlocksV10DBs {\n BlocksV10DBs {\n blockchain_db: open_db::\u003cLocalBlockchainV10Datas\u003e(db_path, \"blockchain.db\")\n .expect(\"Fail to open LocalBlockchainV10DB\"),\n }\n }\n /// Save blocks databases in their respective files\n pub fn save_dbs(\u0026self) {\n info!(\"BLOCKCHAIN-DAL: Save LocalBlockchainV10DB.\");\n self.blockchain_db\n .save()\n .expect(\"Fatal error : fail to save LocalBlockchainV10DB !\");\n }\n}\n\n#[derive(Debug)]\n/// Set of databases storing forks informations\npub struct ForksDBs {\n /// Fork tree (store only blockstamp)\n pub fork_tree_db: BinDB\u003cForksTreeV10Datas\u003e,\n /// Blocks in fork tree\n pub fork_blocks_db: BinDB\u003cForksBlocksV10Datas\u003e,\n /// Orphan blocks\n pub orphan_blocks_db: BinDB\u003cOrphanBlocksV10Datas\u003e,\n}\n\nimpl ForksDBs {\n /// Open fork databases from their respective files\n pub fn open(db_path: Option\u003c\u0026PathBuf\u003e) -\u003e ForksDBs {\n ForksDBs {\n fork_tree_db: open_db::\u003cForksTreeV10Datas\u003e(db_path, \"fork_tree.db\")\n .expect(\"Fail to open ForksTreeV10Datas\"),\n fork_blocks_db: open_db::\u003cForksBlocksV10Datas\u003e(db_path, \"fork_blocks.db\")\n .expect(\"Fail to open ForkForksBlocksV10DatassV10DB\"),\n orphan_blocks_db: open_db::\u003cOrphanBlocksV10Datas\u003e(db_path, \"orphan_blocks.db\")\n .expect(\"Fail to open OrphanBlocksV10Datas\"),\n }\n }\n /// Save fork databases in their respective files\n pub fn save_dbs(\u0026self) {\n info!(\"BLOCKCHAIN-DAL: Save ForksDBs.\");\n self.fork_tree_db\n .save()\n .expect(\"Fatal error : fail to save ForksTreeV10Datas !\");\n self.fork_blocks_db\n .save()\n .expect(\"Fatal error : fail to save ForkForksBlocksV10DatassV10DB !\");\n self.orphan_blocks_db\n .save()\n .expect(\"Fatal error : fail to save OrphanBlocksV10Datas !\");\n }\n}\n\n#[derive(Debug)]\n/// Set of databases storing web of trust information\npub struct WotsV10DBs {\n /// Store wot graph\n pub wot_db: BinDB\u003cWotDB\u003e,\n /// Store idrntities\n pub identities_db: BinDB\u003cIdentitiesV10Datas\u003e,\n /// Store memberships created_block_id (Use only to detect expirations)\n pub ms_db: BinDB\u003cMsExpirV10Datas\u003e,\n /// Store certifications created_block_id (Use only to detect expirations)\n pub certs_db: BinDB\u003cCertsExpirV10Datas\u003e,\n}\n\nimpl WotsV10DBs {\n /// Open wot databases from their respective files\n pub fn open(db_path: Option\u003c\u0026PathBuf\u003e) -\u003e WotsV10DBs {\n WotsV10DBs {\n wot_db: open_db::\u003cRustyWebOfTrust\u003e(db_path, \"wot.db\").expect(\"Fail to open WotDB\"),\n identities_db: open_db::\u003cIdentitiesV10Datas\u003e(db_path, \"identities.db\")\n .expect(\"Fail to open IdentitiesV10DB\"),\n ms_db: open_db::\u003cMsExpirV10Datas\u003e(db_path, \"ms.db\").expect(\"Fail to open MsExpirV10DB\"),\n certs_db: open_db::\u003cCertsExpirV10Datas\u003e(db_path, \"certs.db\")\n .expect(\"Fail to open CertsExpirV10DB\"),\n }\n }\n /// Save wot databases from their respective files\n pub fn save_dbs(\u0026self) {\n info!(\"BLOCKCHAIN-DAL: Save WotsV10DBs.\");\n self.wot_db\n .save()\n .expect(\"Fatal error : fail to save WotDB !\");\n self.save_dbs_except_graph();\n }\n /// Save wot databases from their respective files (except wot graph)\n pub fn save_dbs_except_graph(\u0026self) {\n self.identities_db\n .save()\n .expect(\"Fatal error : fail to save IdentitiesV10DB !\");\n self.ms_db\n .save()\n .expect(\"Fatal error : fail to save MsExpirV10DB !\");\n self.certs_db\n .save()\n .expect(\"Fatal error : fail to save CertsExpirV10DB !\");\n }\n}\n\n#[derive(Debug)]\n/// Set of databases storing currency information\npub struct CurrencyV10DBs {\n /// Store all UD sources\n pub du_db: BinDB\u003cUDsV10Datas\u003e,\n /// Store all Transactions\n pub tx_db: BinDB\u003cTxV10Datas\u003e,\n /// Store all UTXOs\n pub utxos_db: BinDB\u003cUTXOsV10Datas\u003e,\n /// Store balances of all address (and theirs UTXOs indexs)\n pub balances_db: BinDB\u003cBalancesV10Datas\u003e,\n}\n\nimpl CurrencyV10DBs {\n /// Open currency databases from their respective files\n pub fn open(db_path: Option\u003c\u0026PathBuf\u003e) -\u003e CurrencyV10DBs {\n CurrencyV10DBs {\n du_db: open_db::\u003cUDsV10Datas\u003e(db_path, \"du.db\").expect(\"Fail to open UDsV10DB\"),\n tx_db: open_db::\u003cTxV10Datas\u003e(db_path, \"tx.db\")\n .unwrap_or_else(|_| fatal_error!(\"Fail to open TxV10DB\")),\n utxos_db: open_db::\u003cUTXOsV10Datas\u003e(db_path, \"sources.db\")\n .expect(\"Fail to open UTXOsV10DB\"),\n balances_db: open_db::\u003cBalancesV10Datas\u003e(db_path, \"balances.db\")\n .expect(\"Fail to open BalancesV10DB\"),\n }\n }\n /// Save currency databases in their respective files\n pub fn save_dbs(\u0026self, tx: bool, du: bool) {\n if tx {\n info!(\"BLOCKCHAIN-DAL: Save CurrencyV10DBs.\");\n self.tx_db\n .save()\n .expect(\"Fatal error : fail to save LocalBlockchainV10DB !\");\n self.utxos_db\n .save()\n .expect(\"Fatal error : fail to save UTXOsV10DB !\");\n self.balances_db\n .save()\n .expect(\"Fatal error : fail to save BalancesV10DB !\");\n }\n if du {\n self.du_db\n .save()\n .expect(\"Fatal error : fail to save UDsV10DB !\");\n }\n }\n}\n\n#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq, Hash, Serialize)]\n/// Data Access Layer Error\npub enum DALError {\n /// Error in write operation\n WriteError,\n /// Error in read operation\n ReadError,\n /// A database is corrupted, you have to reset the data completely\n DBCorrupted,\n /// Error with the file system\n FileSystemError,\n /// Capturing a panic signal during a write operation\n WritePanic,\n /// Unknown error\n UnknowError,\n}\n\nimpl From\u003cRustbreakError\u003e for DALError {\n fn from(rust_break_error: RustbreakError) -\u003e DALError {\n match rust_break_error.kind() {\n RustbreakErrorKind::Serialization =\u003e DALError::WriteError,\n RustbreakErrorKind::Deserialization =\u003e DALError::ReadError,\n RustbreakErrorKind::Poison =\u003e DALError::DBCorrupted,\n RustbreakErrorKind::Backend =\u003e DALError::FileSystemError,\n RustbreakErrorKind::WritePanic =\u003e DALError::WritePanic,\n _ =\u003e DALError::UnknowError,\n }\n }\n}\n\n/*#[derive(Debug, Clone)]\npub struct WotStats {\n pub block_number: u32,\n pub block_hash: String,\n pub sentries_count: usize,\n pub average_density: usize,\n pub average_distance: usize,\n pub distances: Vec\u003cusize\u003e,\n pub average_connectivity: usize,\n pub connectivities: Vec\u003cusize\u003e,\n pub average_centrality: usize,\n pub centralities: Vec\u003cu64\u003e,\n}*/\n\n/// Open Rustbreak database\npub fn open_db\u003cD: Serialize + DeserializeOwned + Debug + Default + Clone + Send\u003e(\n dbs_folder_path: Option\u003c\u0026PathBuf\u003e,\n db_file_name: \u0026str,\n) -\u003e Result\u003cBinDB\u003cD\u003e, DALError\u003e {\n if let Some(dbs_folder_path) = dbs_folder_path {\n Ok(BinDB::File(open_file_db::\u003cD\u003e(\n dbs_folder_path,\n db_file_name,\n )?))\n } else {\n Ok(BinDB::Mem(open_memory_db::\u003cD\u003e()?))\n }\n}\n\n/// Open Rustbreak memory database\npub fn open_memory_db\u003cD: Serialize + DeserializeOwned + Debug + Default + Clone + Send\u003e(\n) -\u003e Result\u003cMemoryDatabase\u003cD, Bincode\u003e, DALError\u003e {\n let backend = MemoryBackend::new();\n let db = MemoryDatabase::\u003cD, Bincode\u003e::from_parts(D::default(), backend, Bincode);\n Ok(db)\n}\n\n/// Open Rustbreak file database\npub fn open_file_db\u003cD: Serialize + DeserializeOwned + Debug + Default + Clone + Send\u003e(\n dbs_folder_path: \u0026PathBuf,\n db_file_name: \u0026str,\n) -\u003e Result\u003cFileDatabase\u003cD, Bincode\u003e, DALError\u003e {\n let mut db_path = dbs_folder_path.clone();\n db_path.push(db_file_name);\n let file_path = db_path.as_path();\n if file_path.exists()\n \u0026\u0026 fs::metadata(file_path)\n .expect(\"fail to get file size\")\n .len()\n \u003e 0\n {\n let backend = FileBackend::open(db_path.as_path())?;\n let db = FileDatabase::\u003cD, Bincode\u003e::from_parts(D::default(), backend, Bincode);\n db.load()?;\n Ok(db)\n } else {\n Ok(FileDatabase::\u003cD, Bincode\u003e::from_path(\n db_path.as_path(),\n D::default(),\n )?)\n }\n}\n","traces":[{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":4643952,"length":1,"stats":{"Line":44}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":44}},{"line":128,"address":null,"length":0,"stats":{"Line":44}},{"line":133,"address":4653504,"length":1,"stats":{"Line":20}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":20}},{"line":139,"address":null,"length":0,"stats":{"Line":20}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":200,"address":null,"length":0,"stats":{"Line":1}},{"line":202,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":209,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":238,"address":null,"length":0,"stats":{"Line":0}},{"line":240,"address":null,"length":0,"stats":{"Line":0}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":244,"address":null,"length":0,"stats":{"Line":0}},{"line":249,"address":null,"length":0,"stats":{"Line":0}},{"line":250,"address":null,"length":0,"stats":{"Line":0}},{"line":251,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":261,"address":null,"length":0,"stats":{"Line":0}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":285,"address":null,"length":0,"stats":{"Line":1}},{"line":287,"address":null,"length":0,"stats":{"Line":1}},{"line":288,"address":null,"length":0,"stats":{"Line":1}},{"line":290,"address":null,"length":0,"stats":{"Line":1}},{"line":292,"address":null,"length":0,"stats":{"Line":1}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":0}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":303,"address":null,"length":0,"stats":{"Line":0}},{"line":306,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":311,"address":null,"length":0,"stats":{"Line":0}},{"line":336,"address":null,"length":0,"stats":{"Line":0}},{"line":337,"address":null,"length":0,"stats":{"Line":0}},{"line":338,"address":null,"length":0,"stats":{"Line":0}},{"line":339,"address":null,"length":0,"stats":{"Line":0}},{"line":340,"address":null,"length":0,"stats":{"Line":0}},{"line":341,"address":null,"length":0,"stats":{"Line":0}},{"line":342,"address":null,"length":0,"stats":{"Line":0}},{"line":343,"address":null,"length":0,"stats":{"Line":0}},{"line":363,"address":4662832,"length":1,"stats":{"Line":9}},{"line":367,"address":4662872,"length":1,"stats":{"Line":9}},{"line":368,"address":4662969,"length":1,"stats":{"Line":0}},{"line":369,"address":4662945,"length":1,"stats":{"Line":0}},{"line":370,"address":4662953,"length":1,"stats":{"Line":0}},{"line":372,"address":4663005,"length":1,"stats":{"Line":0}},{"line":373,"address":4663365,"length":1,"stats":{"Line":9}},{"line":378,"address":4674928,"length":1,"stats":{"Line":10}},{"line":380,"address":4674938,"length":1,"stats":{"Line":10}},{"line":381,"address":4675003,"length":1,"stats":{"Line":10}},{"line":382,"address":4675072,"length":1,"stats":{"Line":10}},{"line":386,"address":4678592,"length":1,"stats":{"Line":0}},{"line":390,"address":4678626,"length":1,"stats":{"Line":0}},{"line":391,"address":4678691,"length":1,"stats":{"Line":0}},{"line":392,"address":4678726,"length":1,"stats":{"Line":0}},{"line":393,"address":4678808,"length":1,"stats":{"Line":0}},{"line":394,"address":4678852,"length":1,"stats":{"Line":0}},{"line":399,"address":4679000,"length":1,"stats":{"Line":0}},{"line":400,"address":4679476,"length":1,"stats":{"Line":0}},{"line":401,"address":4679530,"length":1,"stats":{"Line":0}},{"line":402,"address":4679902,"length":1,"stats":{"Line":0}},{"line":403,"address":4679457,"length":1,"stats":{"Line":0}},{"line":404,"address":4680097,"length":1,"stats":{"Line":0}},{"line":405,"address":4680007,"length":1,"stats":{"Line":0}},{"line":406,"address":4680064,"length":1,"stats":{"Line":0}}],"covered":24,"coverable":92},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","readers","balance.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::sources::*;\nuse crate::*;\n\n/// Get address balance\npub fn get_address_balance(\n balances_db: \u0026BinDB\u003cBalancesV10Datas\u003e,\n address: \u0026UTXOConditionsGroup,\n) -\u003e Result\u003cOption\u003cSourceAmount\u003e, DALError\u003e {\n Ok(balances_db.read(|db| {\n if let Some(balance_and_utxos) = db.get(address) {\n Some(balance_and_utxos.0)\n } else {\n None\n }\n })?)\n}\n","traces":[{"line":20,"address":4239328,"length":1,"stats":{"Line":0}},{"line":24,"address":4239348,"length":1,"stats":{"Line":0}},{"line":25,"address":9026705,"length":1,"stats":{"Line":0}},{"line":26,"address":9026776,"length":1,"stats":{"Line":0}},{"line":28,"address":9026815,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":5},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","readers","block.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::*;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\nuse dubp_documents::Document;\nuse dubp_documents::{BlockHash, BlockNumber, Blockstamp};\nuse dup_crypto::keys::*;\nuse std::collections::HashMap;\nuse unwrap::unwrap;\n\n/// get current blockstamp\npub fn get_current_blockstamp(blocks_db: \u0026BlocksV10DBs) -\u003e Result\u003cOption\u003cBlockstamp\u003e, DALError\u003e {\n Ok(blocks_db.blockchain_db.read(|db| {\n let blockchain_len = db.len() as u32;\n if blockchain_len == 0 {\n None\n } else if let Some(dal_block) = db.get(\u0026BlockNumber(blockchain_len - 1)) {\n Some(dal_block.blockstamp())\n } else {\n None\n }\n })?)\n}\n\n/// Get block hash\npub fn get_block_hash(\n db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n block_number: BlockNumber,\n) -\u003e Result\u003cOption\u003cBlockHash\u003e, DALError\u003e {\n Ok(db.read(|db| {\n if let Some(dal_block) = db.get(\u0026block_number) {\n dal_block.block.hash()\n } else {\n None\n }\n })?)\n}\n/// Return true if the node already knows this block\npub fn already_have_block(\n blockchain_db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n forks_dbs: \u0026ForksDBs,\n blockstamp: Blockstamp,\n previous_hash: Option\u003cHash\u003e,\n) -\u003e Result\u003cbool, DALError\u003e {\n let previous_blockstamp = if blockstamp.id.0 \u003e 0 {\n Blockstamp {\n id: BlockNumber(blockstamp.id.0 - 1),\n hash: BlockHash(unwrap!(previous_hash)),\n }\n } else {\n Blockstamp::default()\n };\n\n if forks_dbs\n .fork_blocks_db\n .read(|db| db.contains_key(\u0026blockstamp))?\n {\n return Ok(true);\n } else if let Some(orphan_blockstamps) = forks_dbs.orphan_blocks_db.read(|db| {\n if let Some(orphan_blocks) = db.get(\u0026previous_blockstamp) {\n let orphan_blockstamps: Vec\u003cBlockstamp\u003e =\n orphan_blocks.iter().map(DALBlock::blockstamp).collect();\n Some(orphan_blockstamps)\n } else {\n None\n }\n })? {\n for orphan_blockstamp in orphan_blockstamps {\n if orphan_blockstamp == blockstamp {\n return Ok(true);\n }\n }\n } else {\n return Ok(blockchain_db.read(|db| {\n if let Some(dal_block) = db.get(\u0026blockstamp.id) {\n if dal_block.block.hash().unwrap_or_default() == blockstamp.hash {\n return true;\n }\n }\n false\n })?);\n }\n\n Ok(false)\n}\n\n/// Get block\npub fn get_block(\n blockchain_db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n forks_blocks_db: Option\u003c\u0026BinDB\u003cForksBlocksV10Datas\u003e\u003e,\n blockstamp: \u0026Blockstamp,\n) -\u003e Result\u003cOption\u003cDALBlock\u003e, DALError\u003e {\n let dal_block = blockchain_db.read(|db| db.get(\u0026blockstamp.id).cloned())?;\n if dal_block.is_none() \u0026\u0026 forks_blocks_db.is_some() {\n Ok(forks_blocks_db\n .expect(\"safe unwrap\")\n .read(|db| db.get(\u0026blockstamp).cloned())?)\n } else {\n Ok(dal_block)\n }\n}\n\n/// Get block in local blockchain\n#[inline]\npub fn get_block_in_local_blockchain(\n db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n block_id: BlockNumber,\n) -\u003e Result\u003cOption\u003cBlockDocument\u003e, DALError\u003e {\n Ok(db.read(|db| {\n if let Some(dal_block) = db.get(\u0026block_id) {\n Some(dal_block.block.clone())\n } else {\n None\n }\n })?)\n}\n\n/// Get several blocks in local blockchain\n#[inline]\npub fn get_blocks_in_local_blockchain(\n db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n first_block_number: BlockNumber,\n count: u32,\n) -\u003e Result\u003cVec\u003cBlockDocument\u003e, DALError\u003e {\n Ok(db.read(|db| {\n let mut blocks = Vec::with_capacity(count as usize);\n let mut current_block_number = first_block_number;\n while let Some(dal_block) = db.get(\u0026current_block_number) {\n blocks.push(dal_block.block.clone());\n current_block_number = BlockNumber(current_block_number.0 + 1);\n }\n blocks\n })?)\n}\n\n/// Get current frame of calculating members\npub fn get_current_frame(\n current_block: \u0026DALBlock,\n db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n) -\u003e Result\u003cHashMap\u003cPubKey, usize\u003e, DALError\u003e {\n let frame_begin =\n current_block.block.number().0 - current_block.block.current_frame_size() as u32;\n Ok(db.read(|db| {\n let mut current_frame: HashMap\u003cPubKey, usize\u003e = HashMap::new();\n for block_number in frame_begin..current_block.block.number().0 {\n let issuer = db\n .get(\u0026BlockNumber(block_number))\n .unwrap_or_else(|| fatal_error!(\"Fail to get block #{} !\", block_number))\n .block\n .issuers()[0];\n let issuer_count_blocks = if let Some(issuer_count_blocks) = current_frame.get(\u0026issuer)\n {\n issuer_count_blocks + 1\n } else {\n 1\n };\n current_frame.insert(issuer, issuer_count_blocks);\n }\n current_frame\n })?)\n}\n","traces":[{"line":25,"address":4538960,"length":1,"stats":{"Line":0}},{"line":26,"address":4538975,"length":1,"stats":{"Line":0}},{"line":27,"address":4919535,"length":1,"stats":{"Line":0}},{"line":28,"address":4919575,"length":1,"stats":{"Line":0}},{"line":29,"address":4919587,"length":1,"stats":{"Line":0}},{"line":30,"address":4919598,"length":1,"stats":{"Line":0}},{"line":31,"address":4919695,"length":1,"stats":{"Line":0}},{"line":33,"address":4919759,"length":1,"stats":{"Line":0}},{"line":39,"address":4539504,"length":1,"stats":{"Line":0}},{"line":43,"address":4539523,"length":1,"stats":{"Line":0}},{"line":44,"address":4919825,"length":1,"stats":{"Line":0}},{"line":45,"address":4919894,"length":1,"stats":{"Line":0}},{"line":47,"address":4919919,"length":1,"stats":{"Line":0}},{"line":52,"address":4540112,"length":1,"stats":{"Line":0}},{"line":58,"address":4540135,"length":1,"stats":{"Line":0}},{"line":59,"address":4540480,"length":1,"stats":{"Line":0}},{"line":60,"address":4540216,"length":1,"stats":{"Line":0}},{"line":61,"address":4540268,"length":1,"stats":{"Line":0}},{"line":64,"address":4540560,"length":1,"stats":{"Line":0}},{"line":67,"address":4540574,"length":1,"stats":{"Line":0}},{"line":69,"address":4540596,"length":1,"stats":{"Line":0}},{"line":71,"address":4541023,"length":1,"stats":{"Line":0}},{"line":72,"address":4541041,"length":1,"stats":{"Line":0}},{"line":73,"address":4920132,"length":1,"stats":{"Line":0}},{"line":75,"address":4920205,"length":1,"stats":{"Line":0}},{"line":76,"address":4920299,"length":1,"stats":{"Line":0}},{"line":78,"address":4920385,"length":1,"stats":{"Line":0}},{"line":81,"address":4541638,"length":1,"stats":{"Line":0}},{"line":82,"address":4542045,"length":1,"stats":{"Line":0}},{"line":83,"address":4542081,"length":1,"stats":{"Line":0}},{"line":87,"address":4542193,"length":1,"stats":{"Line":0}},{"line":88,"address":4919950,"length":1,"stats":{"Line":0}},{"line":89,"address":4920006,"length":1,"stats":{"Line":0}},{"line":90,"address":4920076,"length":1,"stats":{"Line":0}},{"line":93,"address":4920097,"length":1,"stats":{"Line":0}},{"line":101,"address":4543296,"length":1,"stats":{"Line":0}},{"line":106,"address":4543336,"length":1,"stats":{"Line":0}},{"line":107,"address":4543843,"length":1,"stats":{"Line":0}},{"line":108,"address":4543927,"length":1,"stats":{"Line":0}},{"line":110,"address":4543967,"length":1,"stats":{"Line":0}},{"line":111,"address":4544026,"length":1,"stats":{"Line":0}},{"line":112,"address":4544447,"length":1,"stats":{"Line":0}},{"line":118,"address":4544864,"length":1,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":4545488,"length":1,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":4546080,"length":1,"stats":{"Line":0}},{"line":155,"address":4546100,"length":1,"stats":{"Line":0}},{"line":156,"address":4546201,"length":1,"stats":{"Line":0}},{"line":157,"address":4922623,"length":1,"stats":{"Line":0}},{"line":158,"address":4922667,"length":1,"stats":{"Line":0}},{"line":159,"address":4922925,"length":1,"stats":{"Line":0}},{"line":160,"address":4922933,"length":1,"stats":{"Line":0}},{"line":161,"address":4921136,"length":1,"stats":{"Line":0}},{"line":164,"address":4923142,"length":1,"stats":{"Line":0}},{"line":166,"address":4923231,"length":1,"stats":{"Line":0}},{"line":168,"address":4923271,"length":1,"stats":{"Line":0}},{"line":170,"address":4923283,"length":1,"stats":{"Line":0}},{"line":172,"address":4922976,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":69},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","readers","certs.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::{BinDB, CertsExpirV10Datas, DALError};\nuse dubp_documents::BlockNumber;\nuse durs_wot::NodeId;\nuse std::collections::HashMap;\n\n/// Find certifications that emitted in indicated blocks expiring\npub fn find_expire_certs(\n certs_db: \u0026BinDB\u003cCertsExpirV10Datas\u003e,\n blocks_expiring: Vec\u003cBlockNumber\u003e,\n) -\u003e Result\u003cHashMap\u003c(NodeId, NodeId), BlockNumber\u003e, DALError\u003e {\n Ok(certs_db.read(|db| {\n let mut all_expire_certs = HashMap::new();\n for expire_block_id in blocks_expiring {\n if let Some(expire_certs) = db.get(\u0026expire_block_id) {\n for (source, target) in expire_certs {\n all_expire_certs.insert((*source, *target), expire_block_id);\n }\n }\n }\n all_expire_certs\n })?)\n}\n","traces":[{"line":22,"address":6128224,"length":1,"stats":{"Line":0}},{"line":26,"address":4802896,"length":1,"stats":{"Line":0}},{"line":27,"address":4802911,"length":1,"stats":{"Line":0}},{"line":28,"address":4802957,"length":1,"stats":{"Line":0}},{"line":29,"address":4803238,"length":1,"stats":{"Line":0}},{"line":30,"address":4803342,"length":1,"stats":{"Line":0}},{"line":31,"address":4803552,"length":1,"stats":{"Line":0}},{"line":35,"address":4803650,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":8},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","readers","currency_params.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::*;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\nuse dubp_documents::Document;\nuse dup_currency_params::db::write_currency_params;\nuse dup_currency_params::genesis_block_params::GenesisBlockParams;\nuse dup_currency_params::CurrencyParameters;\nuse unwrap::unwrap;\n\n/// Get and write currency params\npub fn get_and_write_currency_params(\n db_path: \u0026PathBuf,\n genesis_block: \u0026BlockDocument,\n) -\u003e CurrencyParameters {\n if genesis_block.number().0 != 0 {\n fatal_error!(\"The genesis block must have number equal to zero !\");\n }\n\n match genesis_block {\n BlockDocument::V10(genesis_block_v10) =\u003e {\n if genesis_block_v10.parameters.is_none() {\n fatal_error!(\"The genesis block must have parameters !\");\n } else if let Err(e) = write_currency_params(\n db_path.clone(),\n genesis_block_v10.currency().into(),\n GenesisBlockParams::V10(unwrap!(genesis_block_v10.parameters)),\n ) {\n fatal_error!(\"Fail to write currency parameters: {}\", e);\n } else {\n CurrencyParameters::from((\n \u0026genesis_block_v10.currency().into(),\n unwrap!(genesis_block_v10.parameters),\n ))\n }\n }\n }\n}\n","traces":[{"line":25,"address":8301728,"length":1,"stats":{"Line":0}},{"line":29,"address":8301757,"length":1,"stats":{"Line":0}},{"line":30,"address":8301868,"length":1,"stats":{"Line":0}},{"line":34,"address":8302848,"length":1,"stats":{"Line":0}},{"line":35,"address":8302864,"length":1,"stats":{"Line":0}},{"line":36,"address":8302916,"length":1,"stats":{"Line":0}},{"line":37,"address":8304200,"length":1,"stats":{"Line":0}},{"line":38,"address":8303896,"length":1,"stats":{"Line":0}},{"line":39,"address":8303925,"length":1,"stats":{"Line":0}},{"line":40,"address":8303995,"length":1,"stats":{"Line":0}},{"line":42,"address":8304429,"length":1,"stats":{"Line":0}},{"line":44,"address":8306095,"length":1,"stats":{"Line":0}},{"line":45,"address":8305888,"length":1,"stats":{"Line":0}},{"line":46,"address":8305938,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":14},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","readers","fork_tree.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::*;\nuse dubp_documents::Blockstamp;\n\n/// Get stackables blocks\npub fn get_stackables_blocks(\n forks_dbs: \u0026ForksDBs,\n current_blockstamp: \u0026Blockstamp,\n) -\u003e Result\u003cVec\u003cDALBlock\u003e, DALError\u003e {\n if let Some(stackables_blocks) = forks_dbs\n .orphan_blocks_db\n .read(|db| db.get(\u0026current_blockstamp).cloned())?\n {\n Ok(stackables_blocks)\n } else {\n Ok(vec![])\n }\n}\n","traces":[{"line":20,"address":7977712,"length":1,"stats":{"Line":0}},{"line":24,"address":7977732,"length":1,"stats":{"Line":0}},{"line":26,"address":4591984,"length":1,"stats":{"Line":0}},{"line":28,"address":7978311,"length":1,"stats":{"Line":0}},{"line":30,"address":7978414,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":5},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","readers","identity.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::identity::DALIdentity;\nuse crate::filters::identities::IdentitiesFilter;\nuse crate::{BinDB, DALError, IdentitiesV10Datas};\nuse dubp_documents::{BlockNumber, Document};\nuse dup_crypto::keys::*;\nuse durs_wot::NodeId;\nuse std::collections::HashMap;\n\n/// Get identities in databases\npub fn get_identities(\n db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n filters: IdentitiesFilter,\n current_block_id: BlockNumber,\n) -\u003e Result\u003cVec\u003cDALIdentity\u003e, DALError\u003e {\n if let Some(pubkey) = filters.by_pubkey {\n if let Some(idty) = db.read(|db| db.get(\u0026pubkey).cloned())? {\n Ok(vec![idty])\n } else {\n Ok(vec![])\n }\n } else {\n Ok(db.read(|db| {\n let mut identities: Vec\u003c\u0026DALIdentity\u003e = db\n .values()\n .filter(|idty| {\n filters\n .paging\n .check_created_on(idty.idty_doc.blockstamp().id, current_block_id)\n })\n .collect();\n identities.sort_by(|i1, i2| {\n i1.idty_doc\n .blockstamp()\n .id\n .cmp(\u0026i2.idty_doc.blockstamp().id)\n });\n identities\n .into_iter()\n .skip(filters.paging.page_size * filters.paging.page_number)\n .take(filters.paging.page_size)\n .cloned()\n .collect()\n })?)\n }\n}\n\n/// Get identity in databases\npub fn get_identity(\n db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n pubkey: \u0026PubKey,\n) -\u003e Result\u003cOption\u003cDALIdentity\u003e, DALError\u003e {\n Ok(db.read(|db| {\n if let Some(member_datas) = db.get(\u0026pubkey) {\n Some(member_datas.clone())\n } else {\n None\n }\n })?)\n}\n\n/// Get uid from pubkey\npub fn get_uid(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n pubkey: PubKey,\n) -\u003e Result\u003cOption\u003cString\u003e, DALError\u003e {\n Ok(identities_db.read(|db| {\n if let Some(dal_idty) = db.get(\u0026pubkey) {\n Some(String::from(dal_idty.idty_doc.username()))\n } else {\n None\n }\n })?)\n}\n\n/// Get pubkey from uid\npub fn get_pubkey_from_uid(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n uid: \u0026str,\n) -\u003e Result\u003cOption\u003cPubKey\u003e, DALError\u003e {\n Ok(identities_db.read(|db| {\n for (pubkey, dal_idty) in db {\n if uid == dal_idty.idty_doc.username() {\n return Some(*pubkey);\n }\n }\n None\n })?)\n}\n\n/// Get wot_id index\npub fn get_wot_index(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n) -\u003e Result\u003cHashMap\u003cPubKey, NodeId\u003e, DALError\u003e {\n Ok(identities_db.read(|db| {\n db.iter()\n .map(|(pubkey, member_datas)| (*pubkey, member_datas.wot_id))\n .collect()\n })?)\n}\n\n#[cfg(test)]\nmod test {\n\n use super::*;\n use crate::entities::identity::*;\n use crate::filters::PagingFilter;\n use crate::*;\n use dubp_documents::Blockstamp;\n use dup_crypto_tests_tools::mocks::pubkey;\n use durs_common_tests_tools::collections::slice_same_elems;\n\n fn gen_mock_dal_idty(pubkey: PubKey, created_block_id: BlockNumber) -\u003e DALIdentity {\n DALIdentity {\n hash: \"\".to_owned(),\n state: DALIdentityState::Member(vec![]),\n joined_on: Blockstamp::default(),\n expired_on: None,\n revoked_on: None,\n idty_doc: dubp_documents_tests_tools::mocks::identity::gen_mock_idty(\n pubkey,\n created_block_id,\n ),\n wot_id: NodeId(0),\n ms_created_block_id: BlockNumber(0),\n ms_chainable_on: vec![],\n cert_chainable_on: vec![],\n }\n }\n\n #[test]\n fn test_get_identities() -\u003e Result\u003c(), DALError\u003e {\n // Create mock identities\n let mock_identities = vec![\n gen_mock_dal_idty(pubkey('A'), BlockNumber(0)),\n gen_mock_dal_idty(pubkey('B'), BlockNumber(1)),\n gen_mock_dal_idty(pubkey('C'), BlockNumber(3)),\n gen_mock_dal_idty(pubkey('D'), BlockNumber(4)),\n gen_mock_dal_idty(pubkey('E'), BlockNumber(5)),\n ];\n\n // Write mock identities in DB\n let identities_db =\n BinDB::Mem(open_memory_db::\u003cIdentitiesV10Datas\u003e().expect(\"Fail to create memory DB !\"));\n for idty in \u0026mock_identities {\n identities_db.write(|db| {\n db.insert(idty.idty_doc.issuers()[0], idty.clone());\n })?;\n }\n\n // Test default filters\n let mut filters = IdentitiesFilter::default();\n assert!(slice_same_elems(\n \u0026mock_identities,\n \u0026get_identities(\u0026identities_db, filters, BlockNumber(5))?\n ));\n\n // Test by pubkey filter\n filters = IdentitiesFilter::by_pubkey(pubkey('A'));\n assert_eq!(\n vec![mock_identities[0].clone()],\n get_identities(\u0026identities_db, filters, BlockNumber(5))?\n );\n filters = IdentitiesFilter::by_pubkey(pubkey('C'));\n assert_eq!(\n vec![mock_identities[2].clone()],\n get_identities(\u0026identities_db, filters, BlockNumber(5))?\n );\n\n // Test paging filter with little page size\n filters = IdentitiesFilter {\n paging: PagingFilter {\n from: BlockNumber(0),\n to: None,\n page_size: 2,\n page_number: 1,\n },\n by_pubkey: None,\n };\n assert!(slice_same_elems(\n \u0026vec![mock_identities[2].clone(), mock_identities[3].clone()],\n \u0026get_identities(\u0026identities_db, filters, BlockNumber(5))?\n ));\n\n // Test paging filter with limited interval\n filters = IdentitiesFilter {\n paging: PagingFilter {\n from: BlockNumber(2),\n to: Some(BlockNumber(3)),\n page_size: 50,\n page_number: 0,\n },\n by_pubkey: None,\n };\n assert_eq!(\n vec![mock_identities[2].clone()],\n get_identities(\u0026identities_db, filters, BlockNumber(5))?\n );\n\n Ok(())\n }\n}\n","traces":[{"line":25,"address":4759616,"length":1,"stats":{"Line":1}},{"line":30,"address":4759647,"length":1,"stats":{"Line":1}},{"line":31,"address":4226736,"length":1,"stats":{"Line":2}},{"line":32,"address":4760361,"length":1,"stats":{"Line":1}},{"line":34,"address":4760557,"length":1,"stats":{"Line":0}},{"line":36,"address":4759914,"length":1,"stats":{"Line":0}},{"line":37,"address":4227040,"length":1,"stats":{"Line":2}},{"line":38,"address":4227065,"length":1,"stats":{"Line":1}},{"line":40,"address":4226816,"length":1,"stats":{"Line":2}},{"line":41,"address":4226830,"length":1,"stats":{"Line":1}},{"line":43,"address":4226838,"length":1,"stats":{"Line":1}},{"line":46,"address":4226928,"length":1,"stats":{"Line":2}},{"line":47,"address":4226947,"length":1,"stats":{"Line":1}},{"line":50,"address":4226978,"length":1,"stats":{"Line":1}},{"line":52,"address":4227251,"length":1,"stats":{"Line":1}},{"line":53,"address":4227308,"length":1,"stats":{"Line":1}},{"line":54,"address":4227316,"length":1,"stats":{"Line":1}},{"line":55,"address":4227417,"length":1,"stats":{"Line":1}},{"line":63,"address":4761664,"length":1,"stats":{"Line":0}},{"line":67,"address":4227648,"length":1,"stats":{"Line":0}},{"line":68,"address":4227668,"length":1,"stats":{"Line":0}},{"line":69,"address":4227738,"length":1,"stats":{"Line":0}},{"line":71,"address":4227786,"length":1,"stats":{"Line":0}},{"line":77,"address":4762272,"length":1,"stats":{"Line":0}},{"line":81,"address":4227808,"length":1,"stats":{"Line":0}},{"line":82,"address":4227825,"length":1,"stats":{"Line":0}},{"line":83,"address":4227893,"length":1,"stats":{"Line":0}},{"line":85,"address":4227977,"length":1,"stats":{"Line":0}},{"line":91,"address":4762832,"length":1,"stats":{"Line":0}},{"line":95,"address":4228000,"length":1,"stats":{"Line":0}},{"line":96,"address":4228020,"length":1,"stats":{"Line":0}},{"line":97,"address":4228248,"length":1,"stats":{"Line":0}},{"line":98,"address":4228343,"length":1,"stats":{"Line":0}},{"line":101,"address":4228305,"length":1,"stats":{"Line":0}},{"line":106,"address":4763440,"length":1,"stats":{"Line":0}},{"line":109,"address":4228608,"length":1,"stats":{"Line":0}},{"line":110,"address":4228620,"length":1,"stats":{"Line":0}},{"line":111,"address":4228448,"length":1,"stats":{"Line":0}},{"line":127,"address":4710288,"length":1,"stats":{"Line":1}},{"line":129,"address":4710307,"length":1,"stats":{"Line":1}},{"line":130,"address":4710374,"length":1,"stats":{"Line":1}},{"line":131,"address":4710461,"length":1,"stats":{"Line":1}},{"line":134,"address":4710556,"length":1,"stats":{"Line":1}},{"line":138,"address":4710597,"length":1,"stats":{"Line":1}},{"line":139,"address":4710609,"length":1,"stats":{"Line":1}},{"line":140,"address":4710629,"length":1,"stats":{"Line":1}},{"line":141,"address":4710696,"length":1,"stats":{"Line":1}},{"line":146,"address":4239104,"length":1,"stats":{"Line":2}},{"line":148,"address":4711197,"length":1,"stats":{"Line":1}},{"line":149,"address":4711260,"length":1,"stats":{"Line":1}},{"line":150,"address":4711340,"length":1,"stats":{"Line":1}},{"line":151,"address":4711423,"length":1,"stats":{"Line":1}},{"line":152,"address":4711503,"length":1,"stats":{"Line":1}},{"line":153,"address":4711583,"length":1,"stats":{"Line":1}},{"line":158,"address":4711918,"length":1,"stats":{"Line":1}},{"line":159,"address":4712110,"length":1,"stats":{"Line":1}},{"line":160,"address":4239152,"length":1,"stats":{"Line":2}},{"line":161,"address":4239169,"length":1,"stats":{"Line":1}},{"line":166,"address":4712384,"length":1,"stats":{"Line":1}},{"line":167,"address":4713121,"length":1,"stats":{"Line":0}},{"line":168,"address":4712824,"length":1,"stats":{"Line":1}},{"line":169,"address":4712863,"length":1,"stats":{"Line":1}},{"line":173,"address":4713520,"length":1,"stats":{"Line":1}},{"line":174,"address":4713915,"length":1,"stats":{"Line":0}},{"line":175,"address":4713657,"length":1,"stats":{"Line":1}},{"line":176,"address":4713786,"length":1,"stats":{"Line":1}},{"line":178,"address":4714760,"length":1,"stats":{"Line":1}},{"line":179,"address":4715156,"length":1,"stats":{"Line":0}},{"line":180,"address":4714897,"length":1,"stats":{"Line":1}},{"line":181,"address":4715027,"length":1,"stats":{"Line":1}},{"line":185,"address":4716097,"length":1,"stats":{"Line":1}},{"line":186,"address":4716023,"length":1,"stats":{"Line":1}},{"line":187,"address":4716001,"length":1,"stats":{"Line":1}},{"line":188,"address":4716012,"length":1,"stats":{"Line":1}},{"line":192,"address":4716089,"length":1,"stats":{"Line":1}},{"line":194,"address":4716494,"length":1,"stats":{"Line":0}},{"line":195,"address":4716185,"length":1,"stats":{"Line":1}},{"line":196,"address":4716528,"length":1,"stats":{"Line":1}},{"line":200,"address":4717298,"length":1,"stats":{"Line":1}},{"line":201,"address":4717224,"length":1,"stats":{"Line":1}},{"line":202,"address":4717177,"length":1,"stats":{"Line":1}},{"line":203,"address":4717188,"length":1,"stats":{"Line":1}},{"line":207,"address":4717290,"length":1,"stats":{"Line":1}},{"line":209,"address":4717636,"length":1,"stats":{"Line":0}},{"line":210,"address":4717386,"length":1,"stats":{"Line":1}},{"line":211,"address":4717507,"length":1,"stats":{"Line":1}},{"line":214,"address":4718418,"length":1,"stats":{"Line":1}}],"covered":60,"coverable":87},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","tools.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::block::DALBlock;\nuse dubp_documents::documents::block::BlockDocumentTrait;\nuse dup_crypto::keys::PubKey;\nuse durs_common_tools::fatal_error;\nuse durs_wot::operations::centrality::{CentralitiesCalculator, UlrikBrandesCentralityCalculator};\nuse durs_wot::operations::distance::{\n DistanceCalculator, RustyDistanceCalculator, WotDistance, WotDistanceParameters,\n};\nuse durs_wot::{NodeId, WebOfTrust};\nuse std::collections::HashMap;\n\n/// CENTRALITY_CALCULATOR\npub static CENTRALITY_CALCULATOR: UlrikBrandesCentralityCalculator =\n UlrikBrandesCentralityCalculator {};\n\n/// DISTANCE_CALCULATOR\npub static DISTANCE_CALCULATOR: RustyDistanceCalculator = RustyDistanceCalculator {};\n\n/// Get sentry requirement\npub fn get_sentry_requirement(members_count: usize, step_max: u32) -\u003e u32 {\n match step_max {\n 5 =\u003e {\n if members_count \u003c 33 {\n 2\n } else if members_count \u003c 244 {\n 3\n } else if members_count \u003c 1_025 {\n 4\n } else if members_count \u003c 3_126 {\n 5\n } else if members_count \u003c 7_777 {\n 6\n } else if members_count \u003c 16_808 {\n 7\n } else if members_count \u003c 32_769 {\n 8\n } else if members_count \u003c 59_050 {\n 9\n } else if members_count \u003c 100_001 {\n 10\n } else if members_count \u003c 161_052 {\n 11\n } else if members_count \u003c 248_833 {\n 12\n } else if members_count \u003c 371_294 {\n 13\n } else if members_count \u003c 537_825 {\n 14\n } else if members_count \u003c 759_376 {\n 15\n } else if members_count \u003c 1_048_577 {\n 16\n } else if members_count \u003c 1_419_858 {\n 17\n } else if members_count \u003c 1_889_569 {\n 18\n } else {\n fatal_error!(\n \"get_sentry_requirement not define for members_count greater than 1_889_569 !\"\n );\n }\n }\n _ =\u003e fatal_error!(\"get_sentry_requirement not define for step_max != 5 !\"),\n }\n}\n\n/// Compute average density\npub fn calculate_average_density\u003cT: WebOfTrust\u003e(wot: \u0026T) -\u003e usize {\n let enabled_members = wot.get_enabled();\n let enabled_members_count = enabled_members.len();\n let mut count_actives_links: usize = 0;\n for member in \u0026enabled_members {\n count_actives_links += wot\n .issued_count(*member)\n .unwrap_or_else(|| fatal_error!(\"Fail to get issued_count of wot_id {}\", (*member).0));\n }\n ((count_actives_links as f32 / enabled_members_count as f32) * 1_000.0) as usize\n}\n\n/// Compute distances\npub fn compute_distances\u003cT: WebOfTrust + Sync\u003e(\n wot: \u0026T,\n sentry_requirement: u32,\n step_max: u32,\n x_percent: f64,\n) -\u003e (usize, Vec\u003cusize\u003e, usize, Vec\u003cusize\u003e) {\n let members_count = wot.get_enabled().len();\n let mut distances = Vec::new();\n let mut average_distance: usize = 0;\n let mut connectivities = Vec::new();\n let mut average_connectivity: usize = 0;\n for i in 0..wot.size() {\n let distance_datas: WotDistance = DISTANCE_CALCULATOR\n .compute_distance(\n wot,\n WotDistanceParameters {\n node: NodeId(i),\n sentry_requirement,\n step_max,\n x_percent,\n },\n )\n .expect(\"Fatal Error: compute_distance return None !\");\n let distance = ((f64::from(distance_datas.success)\n / (x_percent * f64::from(distance_datas.sentries)))\n * 100.0) as usize;\n distances.push(distance);\n average_distance += distance;\n let connectivity = ((f64::from(distance_datas.success - distance_datas.success_at_border)\n / (x_percent * f64::from(distance_datas.sentries)))\n * 100.0) as usize;\n connectivities.push(connectivity);\n average_connectivity += connectivity;\n }\n average_distance /= members_count;\n average_connectivity /= members_count;\n (\n average_distance,\n distances,\n average_connectivity,\n connectivities,\n )\n}\n\n/// Compute distance stress centralities\npub fn calculate_distance_stress_centralities\u003cT: WebOfTrust\u003e(wot: \u0026T, step_max: u32) -\u003e Vec\u003cu64\u003e {\n CENTRALITY_CALCULATOR.distance_stress_centralities(wot, step_max as usize)\n}\n\n/// Compute median issuers frame\npub fn compute_median_issuers_frame\u003cS: std::hash::BuildHasher\u003e(\n current_block: \u0026DALBlock,\n current_frame: \u0026HashMap\u003cPubKey, usize, S\u003e,\n) -\u003e usize {\n if !current_frame.is_empty() {\n let mut current_frame_vec: Vec\u003c_\u003e = current_frame.values().cloned().collect();\n current_frame_vec.sort_unstable();\n\n // Calculate median\n let mut median_index = match current_block.block.issuers_count() % 2 {\n 1 =\u003e (current_block.block.issuers_count() / 2) + 1,\n _ =\u003e current_block.block.issuers_count() / 2,\n };\n if median_index \u003e= current_block.block.issuers_count() {\n median_index = current_block.block.issuers_count() - 1;\n }\n current_frame_vec[median_index]\n\n /*// Calculate second tiercile index\n let mut second_tiercile_index = match self.block.issuers_count % 3 {\n 1 | 2 =\u003e (self.block.issuers_count as f64 * (2.0 / 3.0)) as usize + 1,\n _ =\u003e (self.block.issuers_count as f64 * (2.0 / 3.0)) as usize,\n };\n if second_tiercile_index \u003e= self.block.issuers_count {\n second_tiercile_index = self.block.issuers_count - 1;\n }\n self.second_tiercile_frame = current_frame_vec[second_tiercile_index];*/\n } else {\n 0\n }\n}\n","traces":[{"line":35,"address":7911024,"length":1,"stats":{"Line":0}},{"line":37,"address":7911049,"length":1,"stats":{"Line":0}},{"line":38,"address":7911063,"length":1,"stats":{"Line":0}},{"line":39,"address":7911074,"length":1,"stats":{"Line":0}},{"line":40,"address":7911090,"length":1,"stats":{"Line":0}},{"line":41,"address":7911104,"length":1,"stats":{"Line":0}},{"line":42,"address":7911120,"length":1,"stats":{"Line":0}},{"line":43,"address":7911134,"length":1,"stats":{"Line":0}},{"line":44,"address":7911150,"length":1,"stats":{"Line":0}},{"line":45,"address":7911164,"length":1,"stats":{"Line":0}},{"line":46,"address":7911180,"length":1,"stats":{"Line":0}},{"line":47,"address":7911194,"length":1,"stats":{"Line":0}},{"line":48,"address":7911210,"length":1,"stats":{"Line":0}},{"line":49,"address":7911224,"length":1,"stats":{"Line":0}},{"line":50,"address":7911240,"length":1,"stats":{"Line":0}},{"line":51,"address":7911254,"length":1,"stats":{"Line":0}},{"line":52,"address":7911270,"length":1,"stats":{"Line":0}},{"line":53,"address":7911284,"length":1,"stats":{"Line":0}},{"line":54,"address":7911300,"length":1,"stats":{"Line":0}},{"line":55,"address":7911314,"length":1,"stats":{"Line":0}},{"line":56,"address":7911330,"length":1,"stats":{"Line":0}},{"line":57,"address":7911344,"length":1,"stats":{"Line":0}},{"line":58,"address":7911360,"length":1,"stats":{"Line":0}},{"line":59,"address":7911374,"length":1,"stats":{"Line":0}},{"line":60,"address":7911390,"length":1,"stats":{"Line":0}},{"line":61,"address":7911404,"length":1,"stats":{"Line":0}},{"line":62,"address":7911420,"length":1,"stats":{"Line":0}},{"line":63,"address":7911434,"length":1,"stats":{"Line":0}},{"line":64,"address":7911450,"length":1,"stats":{"Line":0}},{"line":65,"address":7911464,"length":1,"stats":{"Line":0}},{"line":66,"address":7911480,"length":1,"stats":{"Line":0}},{"line":67,"address":7911494,"length":1,"stats":{"Line":0}},{"line":68,"address":7911510,"length":1,"stats":{"Line":0}},{"line":69,"address":7911524,"length":1,"stats":{"Line":0}},{"line":70,"address":7911540,"length":1,"stats":{"Line":0}},{"line":71,"address":7911554,"length":1,"stats":{"Line":0}},{"line":73,"address":7911577,"length":1,"stats":{"Line":0}},{"line":78,"address":7912600,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":38},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","block.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::block::DALBlock;\nuse crate::*;\nuse crate::{BinDB, DALError, LocalBlockchainV10Datas};\nuse dubp_documents::documents::block::BlockDocumentTrait;\nuse dubp_documents::Document;\nuse unwrap::unwrap;\n\n/// Insert new head Block in databases\npub fn insert_new_head_block(\n blockchain_db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n forks_dbs: \u0026ForksDBs,\n dal_block: DALBlock,\n) -\u003e Result\u003c(), DALError\u003e {\n // Insert head block in blockchain\n blockchain_db.write(|db| {\n db.insert(dal_block.block.number(), dal_block.clone());\n })?;\n\n // Insert head block in fork tree\n let removed_blockstamps = crate::writers::fork_tree::insert_new_head_block(\n \u0026forks_dbs.fork_tree_db,\n dal_block.blockstamp(),\n )?;\n\n // Insert head block in ForksBlocks\n forks_dbs.fork_blocks_db.write(|db| {\n db.insert(dal_block.blockstamp(), dal_block);\n })?;\n\n // Remove too old blocks\n forks_dbs.fork_blocks_db.write(|db| {\n for blockstamp in removed_blockstamps {\n db.remove(\u0026blockstamp);\n }\n })?;\n\n Ok(())\n}\n\n/// Insert new fork Block in databases\npub fn insert_new_fork_block(forks_dbs: \u0026ForksDBs, dal_block: DALBlock) -\u003e Result\u003cbool, DALError\u003e {\n if crate::writers::fork_tree::insert_new_fork_block(\n \u0026forks_dbs.fork_tree_db,\n dal_block.block.blockstamp(),\n unwrap!(dal_block.block.previous_hash()),\n )? {\n // Insert in ForksBlocks\n forks_dbs.fork_blocks_db.write(|db| {\n db.insert(dal_block.blockstamp(), dal_block.clone());\n })?;\n\n // As long as orphan blocks can succeed the last inserted block, they are inserted\n if let Some(stackables_blocks) = forks_dbs\n .orphan_blocks_db\n .read(|db| db.get(\u0026dal_block.blockstamp()).cloned())?\n {\n for stackable_block in stackables_blocks {\n let _ = insert_new_fork_block(forks_dbs, stackable_block);\n }\n }\n\n Ok(true)\n } else {\n let previous_blockstamp = dal_block.previous_blockstamp();\n\n // Get orphanBlocks vector\n let mut orphan_blocks = if let Some(orphan_blocks) = forks_dbs\n .orphan_blocks_db\n .read(|db| db.get(\u0026previous_blockstamp).cloned())?\n {\n orphan_blocks\n } else {\n Vec::new()\n };\n\n // Add fork block\n orphan_blocks.push(dal_block);\n\n // Update OrphanBlocks DB\n forks_dbs.orphan_blocks_db.write(|db| {\n db.insert(previous_blockstamp, orphan_blocks);\n })?;\n\n Ok(false)\n }\n}\n","traces":[{"line":24,"address":6786928,"length":1,"stats":{"Line":1}},{"line":30,"address":4726976,"length":1,"stats":{"Line":2}},{"line":31,"address":4726993,"length":1,"stats":{"Line":1}},{"line":35,"address":6787504,"length":1,"stats":{"Line":1}},{"line":36,"address":6787444,"length":1,"stats":{"Line":1}},{"line":37,"address":6787473,"length":1,"stats":{"Line":1}},{"line":41,"address":4727088,"length":1,"stats":{"Line":2}},{"line":42,"address":4727100,"length":1,"stats":{"Line":1}},{"line":46,"address":4727328,"length":1,"stats":{"Line":2}},{"line":47,"address":4727340,"length":1,"stats":{"Line":1}},{"line":48,"address":4727692,"length":1,"stats":{"Line":1}},{"line":52,"address":6788626,"length":1,"stats":{"Line":1}},{"line":56,"address":6789168,"length":1,"stats":{"Line":1}},{"line":57,"address":6789189,"length":1,"stats":{"Line":1}},{"line":58,"address":6789269,"length":1,"stats":{"Line":1}},{"line":59,"address":6789277,"length":1,"stats":{"Line":1}},{"line":60,"address":6789346,"length":1,"stats":{"Line":1}},{"line":63,"address":4727792,"length":1,"stats":{"Line":2}},{"line":64,"address":4727809,"length":1,"stats":{"Line":1}},{"line":68,"address":6790230,"length":1,"stats":{"Line":1}},{"line":70,"address":4727920,"length":1,"stats":{"Line":2}},{"line":72,"address":6790858,"length":1,"stats":{"Line":0}},{"line":73,"address":6793052,"length":1,"stats":{"Line":0}},{"line":79,"address":6791383,"length":1,"stats":{"Line":0}},{"line":82,"address":6791390,"length":1,"stats":{"Line":0}},{"line":84,"address":4728032,"length":1,"stats":{"Line":0}},{"line":86,"address":6792028,"length":1,"stats":{"Line":0}},{"line":88,"address":6792094,"length":1,"stats":{"Line":0}},{"line":92,"address":6793598,"length":1,"stats":{"Line":0}},{"line":95,"address":4728112,"length":1,"stats":{"Line":0}},{"line":96,"address":4728121,"length":1,"stats":{"Line":0}}],"covered":21,"coverable":31},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","certification.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::{BinDB, CertsExpirV10Datas, DALError, IdentitiesV10Datas};\nuse dubp_documents::documents::certification::CompactCertificationDocumentV10;\nuse dubp_documents::BlockNumber;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyParameters;\nuse durs_wot::NodeId;\n\n/// Apply \"certification\" event in databases\npub fn write_certification(\n currency_params: \u0026CurrencyParameters,\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n certs_db: \u0026BinDB\u003cCertsExpirV10Datas\u003e,\n source_pubkey: PubKey,\n source: NodeId,\n target: NodeId,\n created_block_id: BlockNumber,\n written_timestamp: u64,\n) -\u003e Result\u003c(), DALError\u003e {\n // Get cert_chainable_on\n let mut member_datas = identities_db.read(|db| {\n db.get(\u0026source_pubkey)\n .expect(\"Database Corrupted, please reset data !\")\n .clone()\n })?;\n // Push new cert_chainable_on\n member_datas\n .cert_chainable_on\n .push(written_timestamp + currency_params.sig_period);\n // Write new identity datas\n identities_db.write(|db| {\n db.insert(source_pubkey, member_datas);\n })?;\n // Add cert in certs_db\n certs_db.write(|db| {\n let mut created_certs = db.get(\u0026created_block_id).cloned().unwrap_or_default();\n created_certs.insert((source, target));\n db.insert(created_block_id, created_certs);\n })?;\n Ok(())\n}\n\n/// Revert writtent certification\npub fn revert_write_cert(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n certs_db: \u0026BinDB\u003cCertsExpirV10Datas\u003e,\n compact_doc: CompactCertificationDocumentV10,\n source: NodeId,\n target: NodeId,\n) -\u003e Result\u003c(), DALError\u003e {\n // Remove CertsExpirV10Datas entry\n certs_db.write(|db| {\n let mut certs = db\n .get(\u0026compact_doc.block_number)\n .cloned()\n .unwrap_or_default();\n certs.remove(\u0026(source, target));\n db.insert(compact_doc.block_number, certs);\n })?;\n // Pop last cert_chainable_on\n identities_db.write(|db| {\n if let Some(mut member_datas) = db.get(\u0026compact_doc.issuer).cloned() {\n member_datas.cert_chainable_on.pop();\n db.insert(compact_doc.issuer, member_datas);\n }\n })?;\n Ok(())\n}\n\n/// Revert \"certification expiry\" event in databases\npub fn revert_expire_cert(\n certs_db: \u0026BinDB\u003cCertsExpirV10Datas\u003e,\n source: NodeId,\n target: NodeId,\n created_block_id: BlockNumber,\n) -\u003e Result\u003c(), DALError\u003e {\n // Reinsert CertsExpirV10Datas entry\n certs_db.write(|db| {\n let mut certs = db.get(\u0026created_block_id).cloned().unwrap_or_default();\n certs.insert((source, target));\n db.insert(created_block_id, certs);\n })?;\n Ok(())\n}\n\n/// Apply \"certification expiry\" event in databases\npub fn expire_certs(\n certs_db: \u0026BinDB\u003cCertsExpirV10Datas\u003e,\n created_block_id: BlockNumber,\n) -\u003e Result\u003c(), DALError\u003e {\n // Remove CertsExpirV10Datas entries\n certs_db.write(|db| {\n db.remove(\u0026created_block_id);\n })?;\n Ok(())\n}\n","traces":[{"line":24,"address":6735200,"length":1,"stats":{"Line":0}},{"line":35,"address":4237120,"length":1,"stats":{"Line":0}},{"line":36,"address":4237137,"length":1,"stats":{"Line":0}},{"line":41,"address":6735777,"length":1,"stats":{"Line":0}},{"line":43,"address":6735769,"length":1,"stats":{"Line":0}},{"line":45,"address":4237232,"length":1,"stats":{"Line":0}},{"line":46,"address":4237244,"length":1,"stats":{"Line":0}},{"line":49,"address":4237392,"length":1,"stats":{"Line":0}},{"line":50,"address":4237404,"length":1,"stats":{"Line":0}},{"line":51,"address":4237501,"length":1,"stats":{"Line":0}},{"line":52,"address":4237563,"length":1,"stats":{"Line":0}},{"line":54,"address":6736688,"length":1,"stats":{"Line":0}},{"line":58,"address":6737088,"length":1,"stats":{"Line":0}},{"line":66,"address":4237760,"length":1,"stats":{"Line":0}},{"line":67,"address":4237772,"length":1,"stats":{"Line":0}},{"line":68,"address":4237785,"length":1,"stats":{"Line":0}},{"line":70,"address":4237856,"length":1,"stats":{"Line":0}},{"line":71,"address":4237869,"length":1,"stats":{"Line":0}},{"line":72,"address":4237923,"length":1,"stats":{"Line":0}},{"line":75,"address":4238112,"length":1,"stats":{"Line":0}},{"line":76,"address":4238129,"length":1,"stats":{"Line":0}},{"line":77,"address":4238281,"length":1,"stats":{"Line":0}},{"line":78,"address":4238311,"length":1,"stats":{"Line":0}},{"line":81,"address":6737906,"length":1,"stats":{"Line":0}},{"line":85,"address":6738032,"length":1,"stats":{"Line":0}},{"line":92,"address":4238688,"length":1,"stats":{"Line":0}},{"line":93,"address":4238700,"length":1,"stats":{"Line":0}},{"line":94,"address":4238797,"length":1,"stats":{"Line":0}},{"line":95,"address":4238859,"length":1,"stats":{"Line":0}},{"line":97,"address":6738455,"length":1,"stats":{"Line":0}},{"line":101,"address":6738528,"length":1,"stats":{"Line":0}},{"line":106,"address":4239056,"length":1,"stats":{"Line":0}},{"line":107,"address":4239069,"length":1,"stats":{"Line":0}},{"line":109,"address":6738909,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":34},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","dividend.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::sources::SourceAmount;\nuse crate::*;\nuse dubp_documents::documents::transaction::*;\nuse dubp_documents::BlockNumber;\nuse dup_crypto::keys::PubKey;\nuse std::collections::{HashMap, HashSet};\n\n/// Apply UD creation in databases\npub fn create_du(\n du_db: \u0026BinDB\u003cUDsV10Datas\u003e,\n balances_db: \u0026BinDB\u003cBalancesV10Datas\u003e,\n du_amount: \u0026SourceAmount,\n du_block_id: BlockNumber,\n members: \u0026[PubKey],\n revert: bool,\n) -\u003e Result\u003c(), DALError\u003e {\n debug!(\n \"create_du(amount, block_id, members, revert)=({:?}, {}, {:?}, {})\",\n du_amount, du_block_id.0, members, revert\n );\n // Insert/Remove UD sources in UDsV10DB\n du_db.write(|db| {\n for pubkey in members {\n let mut pubkey_dus = db.get(\u0026pubkey).cloned().unwrap_or_default();\n if revert {\n pubkey_dus.remove(\u0026du_block_id);\n } else {\n pubkey_dus.insert(du_block_id);\n }\n db.insert(*pubkey, pubkey_dus);\n }\n })?;\n // Get members balances\n let members_balances: HashMap\u003cPubKey, (SourceAmount, HashSet\u003cUTXOIndexV10\u003e)\u003e = balances_db\n .read(|db| {\n let mut members_balances = HashMap::new();\n for pubkey in members {\n members_balances.insert(\n *pubkey,\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(*pubkey),\n ))\n .cloned()\n .unwrap_or_default(),\n );\n }\n members_balances\n })?;\n // Increase/Decrease members balance\n let members_balances: Vec\u003c(PubKey, (SourceAmount, HashSet\u003cUTXOIndexV10\u003e))\u003e = members_balances\n .iter()\n .map(|(pubkey, (balance, utxos_indexs))| {\n let new_balance = if revert {\n *balance - *du_amount\n } else {\n *balance + *du_amount\n };\n (*pubkey, (new_balance, utxos_indexs.clone()))\n })\n .collect();\n // Write new members balance\n balances_db.write(|db| {\n for (pubkey, (balance, utxos_indexs)) in members_balances {\n db.insert(\n UTXOConditionsGroup::Single(TransactionOutputCondition::Sig(pubkey)),\n (balance, utxos_indexs),\n );\n }\n })?;\n Ok(())\n}\n","traces":[{"line":24,"address":9024256,"length":1,"stats":{"Line":1}},{"line":32,"address":9024334,"length":1,"stats":{"Line":1}},{"line":37,"address":6382160,"length":1,"stats":{"Line":2}},{"line":38,"address":6382172,"length":1,"stats":{"Line":1}},{"line":39,"address":6382342,"length":1,"stats":{"Line":1}},{"line":40,"address":6382424,"length":1,"stats":{"Line":1}},{"line":41,"address":6382438,"length":1,"stats":{"Line":0}},{"line":43,"address":6382469,"length":1,"stats":{"Line":1}},{"line":45,"address":6382497,"length":1,"stats":{"Line":1}},{"line":49,"address":9025430,"length":1,"stats":{"Line":1}},{"line":50,"address":6382736,"length":1,"stats":{"Line":2}},{"line":51,"address":6382756,"length":1,"stats":{"Line":1}},{"line":52,"address":6382797,"length":1,"stats":{"Line":1}},{"line":53,"address":6383340,"length":1,"stats":{"Line":1}},{"line":54,"address":6383008,"length":1,"stats":{"Line":1}},{"line":55,"address":6383049,"length":1,"stats":{"Line":1}},{"line":56,"address":6383054,"length":1,"stats":{"Line":1}},{"line":62,"address":6383237,"length":1,"stats":{"Line":1}},{"line":65,"address":9025911,"length":1,"stats":{"Line":1}},{"line":67,"address":6383472,"length":1,"stats":{"Line":2}},{"line":68,"address":6383531,"length":1,"stats":{"Line":1}},{"line":69,"address":6383554,"length":1,"stats":{"Line":0}},{"line":71,"address":6383606,"length":1,"stats":{"Line":1}},{"line":73,"address":6383658,"length":1,"stats":{"Line":1}},{"line":77,"address":6383872,"length":1,"stats":{"Line":2}},{"line":78,"address":6383884,"length":1,"stats":{"Line":1}},{"line":79,"address":6384638,"length":1,"stats":{"Line":1}},{"line":80,"address":6384643,"length":1,"stats":{"Line":1}},{"line":81,"address":6384803,"length":1,"stats":{"Line":1}},{"line":85,"address":9026389,"length":1,"stats":{"Line":1}}],"covered":28,"coverable":30},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","fork_tree.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::fork_tree::ForkTree;\nuse crate::*;\nuse dubp_documents::*;\n\n/// Insert new head Block in fork tree,\n/// return vector of removed blockstamps\npub fn insert_new_head_block(\n fork_tree_db: \u0026BinDB\u003cForksTreeV10Datas\u003e,\n blockstamp: Blockstamp,\n) -\u003e Result\u003cVec\u003cBlockstamp\u003e, DALError\u003e {\n fork_tree_db.write(|fork_tree| {\n let parent_id_opt = if blockstamp.id.0 \u003e 0 \u0026\u0026 fork_tree.size() \u003e 0 {\n Some(fork_tree.get_main_branch_node_id(BlockNumber(blockstamp.id.0 - 1))\n .expect(\"Fatal error: fail to insert new head block : previous block not exist in main branch\"))\n } else {\n None\n };\n fork_tree.insert_new_node(blockstamp, parent_id_opt, true);\n })?;\n\n Ok(fork_tree_db.read(ForkTree::get_removed_blockstamps)?)\n}\n\n/// Insert new fork block in fork tree only if parent exist in fork tree (orphan block not inserted)\n/// Returns true if block has a parent and has therefore been inserted, return false if block is orphaned\npub fn insert_new_fork_block(\n fork_tree_db: \u0026BinDB\u003cForksTreeV10Datas\u003e,\n blockstamp: Blockstamp,\n previous_hash: Hash,\n) -\u003e Result\u003cbool, DALError\u003e {\n let previous_blockstamp = Blockstamp {\n id: BlockNumber(blockstamp.id.0 - 1),\n hash: BlockHash(previous_hash),\n };\n\n let parent_id_opt =\n fork_tree_db.read(|fork_tree| fork_tree.find_node_with_blockstamp(\u0026previous_blockstamp))?;\n\n if let Some(parent_id) = parent_id_opt {\n fork_tree_db.write(|fork_tree| {\n fork_tree.insert_new_node(blockstamp, Some(parent_id), false);\n })?;\n Ok(true)\n } else {\n Ok(false)\n }\n}\n\n/// Modify the main branch (function to call after a successful roolback)\npub fn change_main_branch(\n forks_dbs: \u0026ForksDBs,\n old_current_blockstamp: Blockstamp,\n new_current_blockstamp: Blockstamp,\n) -\u003e Result\u003c(), DALError\u003e {\n forks_dbs.fork_tree_db.write(|tree| {\n tree.change_main_branch(old_current_blockstamp, new_current_blockstamp);\n })?;\n\n let removed_blockstamps = forks_dbs\n .fork_tree_db\n .read(ForkTree::get_removed_blockstamps)?;\n\n // Remove too old blocks\n forks_dbs.fork_blocks_db.write(|db| {\n for blockstamp in removed_blockstamps {\n db.remove(\u0026blockstamp);\n }\n })?;\n\n Ok(())\n}\n\n#[cfg(test)]\nmod test {\n\n use super::*;\n use crate::entities::fork_tree::TreeNodeId;\n use dup_currency_params::constants::DEFAULT_FORK_WINDOW_SIZE;\n\n #[test]\n fn test_insert_new_head_block() -\u003e Result\u003c(), DALError\u003e {\n // Create mock datas\n let blockstamps =\n dubp_documents_tests_tools::mocks::generate_blockstamps(*DEFAULT_FORK_WINDOW_SIZE + 2);\n let fork_tree_db = open_db::\u003cForksTreeV10Datas\u003e(None, \"\")?;\n\n // Insert genesis block\n assert_eq!(\n Ok(vec![]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[0])\n );\n\n // Check tree state\n assert_eq!(1, fork_tree_db.read(|tree| tree.size())?);\n assert_eq!(\n vec![(TreeNodeId(0), blockstamps[0])],\n fork_tree_db.read(|tree| tree.get_sheets())?\n );\n\n // Insert FORK_WINDOW_SIZE blocks\n for i in 1..*DEFAULT_FORK_WINDOW_SIZE {\n assert_eq!(\n Ok(vec![]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[i])\n );\n }\n\n // Check tree state\n assert_eq!(\n *DEFAULT_FORK_WINDOW_SIZE,\n fork_tree_db.read(|tree| tree.size())?\n );\n assert_eq!(\n vec![(\n TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE - 1),\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE - 1]\n )],\n fork_tree_db.read(|tree| tree.get_sheets())?\n );\n\n // Insert blocks after FORK_WINDOW_SIZE (firsts blocks must be removed)\n assert_eq!(\n Ok(vec![blockstamps[0]]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[*DEFAULT_FORK_WINDOW_SIZE])\n );\n assert_eq!(\n Ok(vec![blockstamps[1]]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[*DEFAULT_FORK_WINDOW_SIZE + 1])\n );\n\n Ok(())\n }\n\n #[test]\n fn test_insert_new_fork_block() -\u003e Result\u003c(), DALError\u003e {\n // Create mock datas\n let blockstamps =\n dubp_documents_tests_tools::mocks::generate_blockstamps(*DEFAULT_FORK_WINDOW_SIZE + 3);\n let fork_tree_db = open_db::\u003cForksTreeV10Datas\u003e(None, \"\")?;\n\n // Insert 4 main blocks\n for i in 0..4 {\n assert_eq!(\n Ok(vec![]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[i])\n );\n }\n\n // Check tree state\n assert_eq!(4, fork_tree_db.read(|tree| tree.size())?);\n assert_eq!(\n vec![(TreeNodeId(3), blockstamps[3])],\n fork_tree_db.read(|tree| tree.get_sheets())?\n );\n\n // Insert first fork block at child of block 2\n let fork_blockstamp = Blockstamp {\n id: BlockNumber(3),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('A')),\n };\n assert_eq!(\n Ok(true),\n insert_new_fork_block(\u0026fork_tree_db, fork_blockstamp, blockstamps[2].hash.0)\n );\n\n // Check tree state\n assert_eq!(5, fork_tree_db.read(|tree| tree.size())?);\n assert!(durs_common_tests_tools::collections::slice_same_elems(\n \u0026vec![\n (TreeNodeId(3), blockstamps[3]),\n (TreeNodeId(4), fork_blockstamp)\n ],\n \u0026fork_tree_db.read(|tree| tree.get_sheets())?\n ));\n\n // Insert second fork block at child of first fork block\n let fork_blockstamp_2 = Blockstamp {\n id: BlockNumber(4),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash('B')),\n };\n assert_eq!(\n Ok(true),\n insert_new_fork_block(\u0026fork_tree_db, fork_blockstamp_2, fork_blockstamp.hash.0)\n );\n\n // Check tree state\n assert_eq!(6, fork_tree_db.read(|tree| tree.size())?);\n assert!(durs_common_tests_tools::collections::slice_same_elems(\n \u0026vec![\n (TreeNodeId(3), blockstamps[3]),\n (TreeNodeId(5), fork_blockstamp_2)\n ],\n \u0026fork_tree_db.read(|tree| tree.get_sheets())?\n ));\n\n // Insert FORK_WINDOW_SIZE blocks\n for i in 4..*DEFAULT_FORK_WINDOW_SIZE {\n assert_eq!(\n Ok(vec![]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[i])\n );\n }\n\n // Check tree state\n assert_eq!(\n *DEFAULT_FORK_WINDOW_SIZE + 2,\n fork_tree_db.read(|tree| tree.size())?\n );\n assert!(durs_common_tests_tools::collections::slice_same_elems(\n \u0026vec![\n (\n TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE + 1),\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE - 1]\n ),\n (TreeNodeId(5), fork_blockstamp_2)\n ],\n \u0026fork_tree_db.read(|tree| tree.get_sheets())?\n ));\n\n // Insert 2 new main blocks (too old blocks must be removed)\n for i in 0..2 {\n assert_eq!(\n Ok(vec![blockstamps[i]]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[*DEFAULT_FORK_WINDOW_SIZE + i])\n );\n }\n\n // Insert one new main block (fork branch must be removed)\n assert_eq!(\n Ok(vec![blockstamps[2], fork_blockstamp_2, fork_blockstamp]),\n insert_new_head_block(\u0026fork_tree_db, blockstamps[*DEFAULT_FORK_WINDOW_SIZE + 2])\n );\n\n // Check tree state\n assert_eq!(\n *DEFAULT_FORK_WINDOW_SIZE,\n fork_tree_db.read(|tree| tree.size())?\n );\n assert_eq!(\n vec![(\n TreeNodeId(*DEFAULT_FORK_WINDOW_SIZE + 4),\n blockstamps[*DEFAULT_FORK_WINDOW_SIZE + 2]\n )],\n fork_tree_db.read(|tree| tree.get_sheets())?\n );\n\n Ok(())\n }\n}\n","traces":[{"line":22,"address":4722992,"length":1,"stats":{"Line":2}},{"line":26,"address":4723007,"length":1,"stats":{"Line":4}},{"line":27,"address":7972385,"length":1,"stats":{"Line":2}},{"line":28,"address":7972452,"length":1,"stats":{"Line":2}},{"line":31,"address":7972576,"length":1,"stats":{"Line":2}},{"line":33,"address":7972585,"length":1,"stats":{"Line":2}},{"line":36,"address":4723390,"length":1,"stats":{"Line":2}},{"line":41,"address":4724000,"length":1,"stats":{"Line":2}},{"line":46,"address":4724187,"length":1,"stats":{"Line":2}},{"line":47,"address":4724012,"length":1,"stats":{"Line":2}},{"line":48,"address":4724076,"length":1,"stats":{"Line":2}},{"line":52,"address":4724250,"length":1,"stats":{"Line":4}},{"line":54,"address":4724676,"length":1,"stats":{"Line":2}},{"line":55,"address":4724707,"length":1,"stats":{"Line":4}},{"line":56,"address":7972774,"length":1,"stats":{"Line":2}},{"line":65,"address":4725296,"length":1,"stats":{"Line":0}},{"line":70,"address":4725308,"length":1,"stats":{"Line":0}},{"line":71,"address":7972902,"length":1,"stats":{"Line":0}},{"line":74,"address":4725727,"length":1,"stats":{"Line":0}},{"line":76,"address":4725771,"length":1,"stats":{"Line":0}},{"line":79,"address":4726189,"length":1,"stats":{"Line":0}},{"line":80,"address":7973036,"length":1,"stats":{"Line":0}},{"line":81,"address":7973388,"length":1,"stats":{"Line":0}},{"line":85,"address":4726514,"length":1,"stats":{"Line":0}},{"line":96,"address":7920816,"length":1,"stats":{"Line":2}},{"line":98,"address":7920836,"length":1,"stats":{"Line":1}},{"line":99,"address":7920884,"length":1,"stats":{"Line":1}},{"line":100,"address":7920953,"length":1,"stats":{"Line":1}},{"line":103,"address":7921446,"length":1,"stats":{"Line":0}},{"line":104,"address":7921375,"length":1,"stats":{"Line":1}},{"line":105,"address":7921415,"length":1,"stats":{"Line":1}},{"line":109,"address":7922047,"length":1,"stats":{"Line":2}},{"line":110,"address":7923210,"length":1,"stats":{"Line":0}},{"line":111,"address":7922918,"length":1,"stats":{"Line":1}},{"line":112,"address":7923203,"length":1,"stats":{"Line":2}},{"line":116,"address":7924137,"length":1,"stats":{"Line":1}},{"line":117,"address":7924585,"length":1,"stats":{"Line":0}},{"line":118,"address":7924424,"length":1,"stats":{"Line":1}},{"line":119,"address":7924550,"length":1,"stats":{"Line":1}},{"line":124,"address":7924466,"length":1,"stats":{"Line":1}},{"line":126,"address":7924500,"length":1,"stats":{"Line":2}},{"line":128,"address":7926361,"length":1,"stats":{"Line":0}},{"line":129,"address":7926016,"length":1,"stats":{"Line":1}},{"line":130,"address":7926038,"length":1,"stats":{"Line":1}},{"line":131,"address":7926099,"length":1,"stats":{"Line":1}},{"line":133,"address":7926354,"length":1,"stats":{"Line":2}},{"line":137,"address":7927571,"length":1,"stats":{"Line":0}},{"line":138,"address":7927298,"length":1,"stats":{"Line":1}},{"line":139,"address":7927531,"length":1,"stats":{"Line":1}},{"line":141,"address":7928396,"length":1,"stats":{"Line":0}},{"line":142,"address":7928151,"length":1,"stats":{"Line":1}},{"line":143,"address":7928367,"length":1,"stats":{"Line":1}},{"line":146,"address":7928968,"length":1,"stats":{"Line":1}},{"line":150,"address":7930176,"length":1,"stats":{"Line":2}},{"line":152,"address":7930196,"length":1,"stats":{"Line":1}},{"line":153,"address":7930316,"length":1,"stats":{"Line":1}},{"line":154,"address":7930385,"length":1,"stats":{"Line":1}},{"line":157,"address":7930815,"length":1,"stats":{"Line":1}},{"line":158,"address":7931193,"length":1,"stats":{"Line":0}},{"line":159,"address":7931050,"length":1,"stats":{"Line":1}},{"line":160,"address":7931158,"length":1,"stats":{"Line":1}},{"line":165,"address":7931108,"length":1,"stats":{"Line":2}},{"line":166,"address":7932923,"length":1,"stats":{"Line":0}},{"line":167,"address":7932650,"length":1,"stats":{"Line":1}},{"line":168,"address":7932916,"length":1,"stats":{"Line":2}},{"line":172,"address":7933917,"length":1,"stats":{"Line":1}},{"line":173,"address":7933850,"length":1,"stats":{"Line":1}},{"line":174,"address":7933861,"length":1,"stats":{"Line":1}},{"line":176,"address":7934146,"length":1,"stats":{"Line":1}},{"line":178,"address":7933963,"length":1,"stats":{"Line":1}},{"line":182,"address":7934611,"length":1,"stats":{"Line":2}},{"line":183,"address":7935904,"length":1,"stats":{"Line":0}},{"line":184,"address":7935467,"length":1,"stats":{"Line":1}},{"line":185,"address":7935472,"length":1,"stats":{"Line":1}},{"line":186,"address":7935646,"length":1,"stats":{"Line":1}},{"line":188,"address":7935970,"length":1,"stats":{"Line":2}},{"line":192,"address":7936642,"length":1,"stats":{"Line":1}},{"line":193,"address":7936575,"length":1,"stats":{"Line":1}},{"line":194,"address":7936586,"length":1,"stats":{"Line":1}},{"line":196,"address":7936839,"length":1,"stats":{"Line":1}},{"line":198,"address":7936688,"length":1,"stats":{"Line":1}},{"line":202,"address":7937304,"length":1,"stats":{"Line":2}},{"line":203,"address":7938597,"length":1,"stats":{"Line":0}},{"line":204,"address":7938160,"length":1,"stats":{"Line":1}},{"line":205,"address":7938165,"length":1,"stats":{"Line":1}},{"line":206,"address":7938339,"length":1,"stats":{"Line":1}},{"line":208,"address":7938663,"length":1,"stats":{"Line":2}},{"line":212,"address":7939268,"length":1,"stats":{"Line":1}},{"line":213,"address":7939714,"length":1,"stats":{"Line":0}},{"line":214,"address":7939555,"length":1,"stats":{"Line":1}},{"line":215,"address":7939679,"length":1,"stats":{"Line":1}},{"line":220,"address":7940431,"length":1,"stats":{"Line":0}},{"line":221,"address":7939604,"length":1,"stats":{"Line":1}},{"line":222,"address":7940339,"length":1,"stats":{"Line":2}},{"line":224,"address":7941690,"length":1,"stats":{"Line":0}},{"line":225,"address":7941181,"length":1,"stats":{"Line":1}},{"line":227,"address":7941203,"length":1,"stats":{"Line":1}},{"line":228,"address":7941264,"length":1,"stats":{"Line":1}},{"line":230,"address":7941432,"length":1,"stats":{"Line":1}},{"line":232,"address":7941756,"length":1,"stats":{"Line":2}},{"line":236,"address":7942361,"length":1,"stats":{"Line":1}},{"line":237,"address":7942993,"length":1,"stats":{"Line":0}},{"line":238,"address":7942640,"length":1,"stats":{"Line":1}},{"line":239,"address":7942957,"length":1,"stats":{"Line":1}},{"line":244,"address":7943986,"length":1,"stats":{"Line":0}},{"line":245,"address":7942717,"length":1,"stats":{"Line":1}},{"line":246,"address":7943954,"length":1,"stats":{"Line":1}},{"line":250,"address":7944609,"length":1,"stats":{"Line":1}},{"line":252,"address":7944643,"length":1,"stats":{"Line":2}},{"line":254,"address":7945788,"length":1,"stats":{"Line":0}},{"line":255,"address":7945470,"length":1,"stats":{"Line":1}},{"line":256,"address":7945492,"length":1,"stats":{"Line":1}},{"line":257,"address":7945544,"length":1,"stats":{"Line":1}},{"line":259,"address":7945781,"length":1,"stats":{"Line":2}},{"line":262,"address":7946650,"length":1,"stats":{"Line":1}}],"covered":90,"coverable":115},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","identity.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::identity::{DALIdentity, DALIdentityState};\nuse crate::{BinDB, DALError, IdentitiesV10Datas, MsExpirV10Datas};\nuse dubp_documents::documents::identity::IdentityDocumentV10;\nuse dubp_documents::Document;\nuse dubp_documents::{BlockNumber, Blockstamp};\nuse dup_crypto::keys::PubKey;\nuse dup_currency_params::CurrencyParameters;\nuse durs_common_tools::fatal_error;\nuse durs_wot::NodeId;\n\n/// Remove identity from databases\npub fn revert_create_identity(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n ms_db: \u0026BinDB\u003cMsExpirV10Datas\u003e,\n pubkey: \u0026PubKey,\n) -\u003e Result\u003c(), DALError\u003e {\n let dal_idty = identities_db.read(|db| {\n db.get(\u0026pubkey)\n .expect(\"Fatal error : try to revert unknow identity !\")\n .clone()\n })?;\n // Remove membership\n ms_db.write(|db| {\n let mut memberships = db\n .get(\u0026dal_idty.ms_created_block_id)\n .cloned()\n .expect(\"Try to revert a membership that does not exist !\");\n memberships.remove(\u0026dal_idty.wot_id);\n db.insert(dal_idty.ms_created_block_id, memberships);\n })?;\n // Remove identity\n identities_db.write(|db| {\n db.remove(\u0026dal_idty.idty_doc.issuers()[0]);\n })?;\n Ok(())\n}\n\n/// Write identity in databases\npub fn create_identity(\n currency_params: \u0026CurrencyParameters,\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n ms_db: \u0026BinDB\u003cMsExpirV10Datas\u003e,\n idty_doc: \u0026IdentityDocumentV10,\n ms_created_block_id: BlockNumber,\n wot_id: NodeId,\n current_blockstamp: Blockstamp,\n current_bc_time: u64,\n) -\u003e Result\u003c(), DALError\u003e {\n let mut idty_doc = idty_doc.clone();\n idty_doc.reduce();\n let idty = DALIdentity {\n hash: \"0\".to_string(),\n state: DALIdentityState::Member(vec![0]),\n joined_on: current_blockstamp,\n expired_on: None,\n revoked_on: None,\n idty_doc,\n wot_id,\n ms_created_block_id,\n ms_chainable_on: vec![current_bc_time + currency_params.ms_period],\n cert_chainable_on: vec![],\n };\n // Write Identity\n identities_db.write(|db| {\n db.insert(idty.idty_doc.issuers()[0], idty.clone());\n })?;\n // Write membership\n ms_db.write(|db| {\n let mut memberships = db.get(\u0026ms_created_block_id).cloned().unwrap_or_default();\n memberships.insert(wot_id);\n db.insert(ms_created_block_id, memberships);\n })?;\n Ok(())\n}\n\n/// Apply \"exclude identity\" event\npub fn exclude_identity(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n pubkey: \u0026PubKey,\n exclusion_blockstamp: \u0026Blockstamp,\n revert: bool,\n) -\u003e Result\u003c(), DALError\u003e {\n let mut idty_datas = identities_db\n .read(|db| db.get(pubkey).cloned())?\n .expect(\"Fatal error : try to renewal unknow identity !\");\n idty_datas.state = if revert {\n match idty_datas.state {\n DALIdentityState::ExpireMember(renewed_counts) =\u003e {\n DALIdentityState::Member(renewed_counts)\n }\n _ =\u003e fatal_error!(\"Try to revert exclusion for a no excluded identity !\"),\n }\n } else {\n match idty_datas.state {\n DALIdentityState::Member(renewed_counts) =\u003e {\n DALIdentityState::ExpireMember(renewed_counts)\n }\n _ =\u003e fatal_error!(\"Try to exclude for an already excluded/revoked identity !\"),\n }\n };\n idty_datas.expired_on = if revert {\n None\n } else {\n Some(*exclusion_blockstamp)\n };\n // Write new identity datas\n identities_db.write(|db| {\n db.insert(*pubkey, idty_datas);\n })?;\n Ok(())\n}\n\n/// Apply \"revoke identity\" event\npub fn revoke_identity(\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n pubkey: \u0026PubKey,\n renewal_blockstamp: \u0026Blockstamp,\n explicit: bool,\n revert: bool,\n) -\u003e Result\u003c(), DALError\u003e {\n let mut member_datas = identities_db\n .read(|db| db.get(pubkey).cloned())?\n .expect(\"Fatal error : Try to revoke unknow idty !\");\n\n member_datas.state = if revert {\n match member_datas.state {\n DALIdentityState::ExplicitRevoked(renewed_counts) =\u003e {\n DALIdentityState::Member(renewed_counts)\n }\n DALIdentityState::ExplicitExpireRevoked(renewed_counts)\n | DALIdentityState::ImplicitRevoked(renewed_counts) =\u003e {\n DALIdentityState::ExpireMember(renewed_counts)\n }\n _ =\u003e fatal_error!(\"Try to revert revoke_identity() for a no revoked idty !\"),\n }\n } else {\n match member_datas.state {\n DALIdentityState::ExpireMember(renewed_counts) =\u003e {\n DALIdentityState::ExplicitExpireRevoked(renewed_counts)\n }\n DALIdentityState::Member(renewed_counts) =\u003e {\n if explicit {\n DALIdentityState::ExplicitRevoked(renewed_counts)\n } else {\n DALIdentityState::ImplicitRevoked(renewed_counts)\n }\n }\n _ =\u003e fatal_error!(\"Try to revert revoke an already revoked idty !\"),\n }\n };\n member_datas.revoked_on = if revert {\n None\n } else {\n Some(*renewal_blockstamp)\n };\n\n identities_db.write(|db| {\n db.insert(*pubkey, member_datas);\n })?;\n Ok(())\n}\n\n/// Apply \"renewal identity\" event in databases\npub fn renewal_identity(\n currency_params: \u0026CurrencyParameters,\n identities_db: \u0026BinDB\u003cIdentitiesV10Datas\u003e,\n ms_db: \u0026BinDB\u003cMsExpirV10Datas\u003e,\n pubkey: \u0026PubKey,\n idty_wot_id: NodeId,\n renewal_timestamp: u64,\n ms_created_block_id: BlockNumber,\n revert: bool,\n) -\u003e Result\u003c(), DALError\u003e {\n // Get idty_datas\n let mut idty_datas = identities_db\n .read(|db| db.get(pubkey).cloned())?\n .expect(\"Fatal error : try to renewal unknow identity !\");\n // Calculate new state value\n idty_datas.state = if revert {\n match idty_datas.state {\n DALIdentityState::Member(renewed_counts) =\u003e {\n let mut new_renewed_counts = renewed_counts.clone();\n new_renewed_counts[renewed_counts.len() - 1] -= 1;\n if new_renewed_counts[renewed_counts.len() - 1] \u003e 0 {\n DALIdentityState::Member(new_renewed_counts)\n } else {\n DALIdentityState::ExpireMember(new_renewed_counts)\n }\n }\n _ =\u003e fatal_error!(\"Try to revert renewal_identity() for an excluded or revoked idty !\"),\n }\n } else {\n match idty_datas.state {\n DALIdentityState::Member(renewed_counts) =\u003e {\n let mut new_renewed_counts = renewed_counts.clone();\n new_renewed_counts[renewed_counts.len() - 1] += 1;\n DALIdentityState::Member(new_renewed_counts)\n }\n DALIdentityState::ExpireMember(renewed_counts) =\u003e {\n let mut new_renewed_counts = renewed_counts.clone();\n new_renewed_counts.push(0);\n DALIdentityState::Member(new_renewed_counts)\n }\n _ =\u003e fatal_error!(\"Try to renewed a revoked identity !\"),\n }\n };\n // Calculate new ms_chainable_on value\n if revert {\n idty_datas.ms_chainable_on.pop();\n } else {\n idty_datas\n .ms_chainable_on\n .push(renewal_timestamp + currency_params.ms_period);\n }\n // Write new identity datas\n identities_db.write(|db| {\n db.insert(*pubkey, idty_datas);\n })?;\n // Update MsExpirV10DB\n ms_db.write(|db| {\n let mut memberships = db.get(\u0026ms_created_block_id).cloned().unwrap_or_default();\n memberships.insert(idty_wot_id);\n db.insert(ms_created_block_id, memberships);\n })?;\n Ok(())\n}\n\n/// Remove identity from databases\npub fn remove_identity(db: \u0026BinDB\u003cIdentitiesV10Datas\u003e, pubkey: PubKey) -\u003e Result\u003c(), DALError\u003e {\n db.write(|db| {\n db.remove(\u0026pubkey);\n })?;\n Ok(())\n}\n","traces":[{"line":27,"address":6105088,"length":1,"stats":{"Line":0}},{"line":32,"address":4757440,"length":1,"stats":{"Line":0}},{"line":33,"address":4757457,"length":1,"stats":{"Line":0}},{"line":38,"address":4757552,"length":1,"stats":{"Line":0}},{"line":39,"address":4757569,"length":1,"stats":{"Line":0}},{"line":40,"address":4757582,"length":1,"stats":{"Line":0}},{"line":42,"address":4757666,"length":1,"stats":{"Line":0}},{"line":43,"address":4757674,"length":1,"stats":{"Line":0}},{"line":44,"address":4757708,"length":1,"stats":{"Line":0}},{"line":47,"address":4757904,"length":1,"stats":{"Line":0}},{"line":48,"address":4757921,"length":1,"stats":{"Line":0}},{"line":50,"address":6106340,"length":1,"stats":{"Line":0}},{"line":54,"address":6106656,"length":1,"stats":{"Line":0}},{"line":64,"address":6106727,"length":1,"stats":{"Line":0}},{"line":65,"address":6106797,"length":1,"stats":{"Line":0}},{"line":66,"address":6107330,"length":1,"stats":{"Line":0}},{"line":67,"address":6106816,"length":1,"stats":{"Line":0}},{"line":68,"address":6106848,"length":1,"stats":{"Line":0}},{"line":69,"address":6106987,"length":1,"stats":{"Line":0}},{"line":70,"address":6107019,"length":1,"stats":{"Line":0}},{"line":71,"address":6107030,"length":1,"stats":{"Line":0}},{"line":72,"address":6107041,"length":1,"stats":{"Line":0}},{"line":73,"address":6107081,"length":1,"stats":{"Line":0}},{"line":74,"address":6107089,"length":1,"stats":{"Line":0}},{"line":75,"address":6107097,"length":1,"stats":{"Line":0}},{"line":76,"address":6107275,"length":1,"stats":{"Line":0}},{"line":79,"address":4758016,"length":1,"stats":{"Line":0}},{"line":80,"address":4758033,"length":1,"stats":{"Line":0}},{"line":83,"address":4758192,"length":1,"stats":{"Line":0}},{"line":84,"address":4758214,"length":1,"stats":{"Line":0}},{"line":85,"address":4758297,"length":1,"stats":{"Line":0}},{"line":86,"address":4758325,"length":1,"stats":{"Line":0}},{"line":88,"address":6108525,"length":1,"stats":{"Line":0}},{"line":92,"address":6108864,"length":1,"stats":{"Line":0}},{"line":98,"address":6108912,"length":1,"stats":{"Line":0}},{"line":99,"address":4758512,"length":1,"stats":{"Line":0}},{"line":100,"address":6109134,"length":1,"stats":{"Line":0}},{"line":101,"address":6109823,"length":1,"stats":{"Line":0}},{"line":103,"address":6109644,"length":1,"stats":{"Line":0}},{"line":104,"address":6109715,"length":1,"stats":{"Line":0}},{"line":106,"address":6109828,"length":1,"stats":{"Line":0}},{"line":110,"address":6110954,"length":1,"stats":{"Line":0}},{"line":111,"address":6111025,"length":1,"stats":{"Line":0}},{"line":113,"address":6111138,"length":1,"stats":{"Line":0}},{"line":116,"address":6112312,"length":1,"stats":{"Line":0}},{"line":117,"address":6112301,"length":1,"stats":{"Line":0}},{"line":119,"address":6112314,"length":1,"stats":{"Line":0}},{"line":122,"address":4758592,"length":1,"stats":{"Line":0}},{"line":123,"address":4758604,"length":1,"stats":{"Line":0}},{"line":125,"address":6112961,"length":1,"stats":{"Line":0}},{"line":129,"address":6114144,"length":1,"stats":{"Line":0}},{"line":136,"address":6114208,"length":1,"stats":{"Line":0}},{"line":137,"address":4758752,"length":1,"stats":{"Line":0}},{"line":138,"address":6114478,"length":1,"stats":{"Line":0}},{"line":140,"address":6116660,"length":1,"stats":{"Line":0}},{"line":141,"address":6115294,"length":1,"stats":{"Line":0}},{"line":142,"address":6115036,"length":1,"stats":{"Line":0}},{"line":143,"address":6115186,"length":1,"stats":{"Line":0}},{"line":145,"address":6115412,"length":1,"stats":{"Line":0}},{"line":146,"address":6115473,"length":1,"stats":{"Line":0}},{"line":147,"address":6115299,"length":1,"stats":{"Line":0}},{"line":149,"address":6115534,"length":1,"stats":{"Line":0}},{"line":152,"address":6116884,"length":1,"stats":{"Line":0}},{"line":153,"address":6116665,"length":1,"stats":{"Line":0}},{"line":154,"address":6116776,"length":1,"stats":{"Line":0}},{"line":156,"address":6116889,"length":1,"stats":{"Line":0}},{"line":157,"address":6116945,"length":1,"stats":{"Line":0}},{"line":158,"address":6116955,"length":1,"stats":{"Line":0}},{"line":160,"address":6117065,"length":1,"stats":{"Line":0}},{"line":163,"address":6117178,"length":1,"stats":{"Line":0}},{"line":166,"address":6118357,"length":1,"stats":{"Line":0}},{"line":167,"address":6118346,"length":1,"stats":{"Line":0}},{"line":169,"address":6118359,"length":1,"stats":{"Line":0}},{"line":172,"address":4758832,"length":1,"stats":{"Line":0}},{"line":173,"address":4758844,"length":1,"stats":{"Line":0}},{"line":175,"address":6119054,"length":1,"stats":{"Line":0}},{"line":179,"address":6120704,"length":1,"stats":{"Line":0}},{"line":190,"address":6120789,"length":1,"stats":{"Line":0}},{"line":191,"address":4758992,"length":1,"stats":{"Line":0}},{"line":192,"address":6121035,"length":1,"stats":{"Line":0}},{"line":194,"address":6123269,"length":1,"stats":{"Line":0}},{"line":196,"address":6121537,"length":1,"stats":{"Line":0}},{"line":197,"address":6121616,"length":1,"stats":{"Line":0}},{"line":198,"address":6121623,"length":1,"stats":{"Line":0}},{"line":199,"address":6121787,"length":1,"stats":{"Line":0}},{"line":200,"address":6121891,"length":1,"stats":{"Line":0}},{"line":202,"address":6122001,"length":1,"stats":{"Line":0}},{"line":205,"address":6122135,"length":1,"stats":{"Line":0}},{"line":208,"address":6123671,"length":1,"stats":{"Line":0}},{"line":209,"address":6123274,"length":1,"stats":{"Line":0}},{"line":210,"address":6123392,"length":1,"stats":{"Line":0}},{"line":211,"address":6123399,"length":1,"stats":{"Line":0}},{"line":212,"address":6123563,"length":1,"stats":{"Line":0}},{"line":214,"address":6123676,"length":1,"stats":{"Line":0}},{"line":215,"address":6123740,"length":1,"stats":{"Line":0}},{"line":216,"address":6123747,"length":1,"stats":{"Line":0}},{"line":217,"address":6123770,"length":1,"stats":{"Line":0}},{"line":219,"address":6123901,"length":1,"stats":{"Line":0}},{"line":223,"address":6125107,"length":1,"stats":{"Line":0}},{"line":224,"address":6125076,"length":1,"stats":{"Line":0}},{"line":226,"address":6125109,"length":1,"stats":{"Line":0}},{"line":228,"address":6125123,"length":1,"stats":{"Line":0}},{"line":231,"address":4759072,"length":1,"stats":{"Line":0}},{"line":232,"address":4759084,"length":1,"stats":{"Line":0}},{"line":235,"address":4759232,"length":1,"stats":{"Line":0}},{"line":236,"address":4759254,"length":1,"stats":{"Line":0}},{"line":237,"address":4759337,"length":1,"stats":{"Line":0}},{"line":238,"address":4759365,"length":1,"stats":{"Line":0}},{"line":240,"address":6126159,"length":1,"stats":{"Line":0}},{"line":244,"address":6127776,"length":1,"stats":{"Line":0}},{"line":245,"address":4759552,"length":1,"stats":{"Line":0}},{"line":246,"address":4759568,"length":1,"stats":{"Line":0}},{"line":248,"address":6128161,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":113},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","requests.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::entities::block::DALBlock;\nuse crate::entities::sources::SourceAmount;\nuse crate::writers::transaction::DALTxV10;\nuse crate::*;\nuse dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\nuse dubp_documents::documents::certification::CompactCertificationDocumentV10;\nuse dubp_documents::documents::identity::IdentityDocumentV10;\nuse dubp_documents::Blockstamp;\nuse dup_crypto::keys::PubKey;\nuse dup_currency_params::CurrencyParameters;\nuse durs_wot::NodeId;\nuse std::ops::Deref;\n\n#[derive(Debug, Clone)]\n/// Contain a pending write request for databases\npub enum DBsWriteRequest {\n /// Contain a pending write request for blocks database\n BlocksDB(BlocksDBsWriteQuery),\n /// Contain a pending write request for wots databases\n WotDBs(WotsDBsWriteQuery),\n /// Contain a pending write request for currency databases\n CurrencyDBs(CurrencyDBsWriteQuery),\n}\n\n#[derive(Debug, Clone)]\n/// Contain a pending write request for blocks databases\npub enum BlocksDBsWriteQuery {\n /// Write block\n WriteBlock(DALBlock),\n /// Revert block\n RevertBlock(DALBlock),\n}\n\nimpl BlocksDBsWriteQuery {\n /// Get copy of block document\n pub fn get_block_doc_copy(\u0026self) -\u003e BlockDocument {\n match self {\n BlocksDBsWriteQuery::WriteBlock(dal_block) =\u003e dal_block.block.clone(),\n BlocksDBsWriteQuery::RevertBlock(dal_block) =\u003e dal_block.block.clone(),\n }\n }\n /// BlocksDBsWriteQuery\n pub fn apply(\n self,\n blockchain_db: \u0026BinDB\u003cLocalBlockchainV10Datas\u003e,\n forks_db: \u0026ForksDBs,\n fork_window_size: usize,\n sync_target: Option\u003cBlockstamp\u003e,\n ) -\u003e Result\u003c(), DALError\u003e {\n match self {\n BlocksDBsWriteQuery::WriteBlock(dal_block) =\u003e {\n let dal_block: DALBlock = dal_block;\n trace!(\"BlocksDBsWriteQuery::WriteBlock...\");\n if sync_target.is_none()\n || dal_block.blockstamp().id.0 + fork_window_size as u32\n \u003e= sync_target.expect(\"safe unwrap\").id.0\n {\n super::block::insert_new_head_block(blockchain_db, forks_db, dal_block)?;\n } else {\n // Insert block in blockchain\n blockchain_db.write(|db| {\n db.insert(dal_block.block.number(), dal_block);\n })?;\n }\n }\n BlocksDBsWriteQuery::RevertBlock(dal_block) =\u003e {\n trace!(\"BlocksDBsWriteQuery::WriteBlock...\");\n // Remove block in blockchain\n blockchain_db.write(|db| {\n db.remove(\u0026dal_block.block.number());\n })?;\n trace!(\"BlocksDBsWriteQuery::WriteBlock...finish\");\n }\n }\n Ok(())\n }\n}\n\n#[derive(Debug, Clone)]\n/// Contain a pending write request for wots databases\npub enum WotsDBsWriteQuery {\n /// Newcomer (wot_id, blockstamp, current_bc_time, idty_doc, ms_created_block_id)\n CreateIdentity(\n NodeId,\n Blockstamp,\n u64,\n Box\u003cIdentityDocumentV10\u003e,\n BlockNumber,\n ),\n /// Revert newcomer event (wot_id, blockstamp, current_bc_time, idty_doc, ms_created_block_id)\n RevertCreateIdentity(PubKey),\n /// Active (pubKey, idty_wot_id, current_bc_time, ms_created_block_id)\n RenewalIdentity(PubKey, NodeId, u64, BlockNumber),\n /// Revert active (pubKey, idty_wot_id, current_bc_time, ms_created_block_id)\n RevertRenewalIdentity(PubKey, NodeId, u64, BlockNumber),\n /// Excluded\n ExcludeIdentity(PubKey, Blockstamp),\n /// Revert exclusion\n RevertExcludeIdentity(PubKey, Blockstamp),\n /// Revoked\n RevokeIdentity(PubKey, Blockstamp, bool),\n /// Revert revocation\n RevertRevokeIdentity(PubKey, Blockstamp, bool),\n /// Certification (source_pubkey, source, target, created_block_id, median_time)\n CreateCert(PubKey, NodeId, NodeId, BlockNumber, u64),\n /// Revert certification (source_pubkey, source, target, created_block_id, median_time)\n RevertCert(CompactCertificationDocumentV10, NodeId, NodeId),\n /// Certification expiry (source, target, created_block_id)\n ExpireCerts(BlockNumber),\n /// Revert certification expiry event (source, target, created_block_id)\n RevertExpireCert(NodeId, NodeId, BlockNumber),\n}\n\nimpl WotsDBsWriteQuery {\n /// Apply WotsDBsWriteQuery\n pub fn apply(\n \u0026self,\n _blockstamp: \u0026Blockstamp,\n currency_params: \u0026CurrencyParameters,\n databases: \u0026WotsV10DBs,\n ) -\u003e Result\u003c(), DALError\u003e {\n match *self {\n WotsDBsWriteQuery::CreateIdentity(\n ref wot_id,\n ref blockstamp,\n ref current_bc_time,\n ref idty_doc,\n ref ms_created_block_id,\n ) =\u003e {\n writers::identity::create_identity(\n currency_params,\n \u0026databases.identities_db,\n \u0026databases.ms_db,\n idty_doc.deref(),\n *ms_created_block_id,\n *wot_id,\n *blockstamp,\n *current_bc_time,\n )?;\n }\n WotsDBsWriteQuery::RevertCreateIdentity(ref pubkey) =\u003e {\n writers::identity::revert_create_identity(\n \u0026databases.identities_db,\n \u0026databases.ms_db,\n pubkey,\n )?;\n }\n WotsDBsWriteQuery::RenewalIdentity(\n ref pubkey,\n ref idty_wot_id,\n ref current_bc_time,\n ms_created_block_id,\n ) =\u003e {\n trace!(\"WotsDBsWriteQuery::RenewalIdentity...\");\n writers::identity::renewal_identity(\n currency_params,\n \u0026databases.identities_db,\n \u0026databases.ms_db,\n pubkey,\n *idty_wot_id,\n *current_bc_time,\n ms_created_block_id,\n false,\n )?;\n trace!(\"DBWrWotsDBsWriteQueryiteRequest::RenewalIdentity...\");\n }\n WotsDBsWriteQuery::RevertRenewalIdentity(\n ref pubkey,\n ref idty_wot_id,\n ref current_bc_time,\n ms_created_block_id,\n ) =\u003e {\n writers::identity::renewal_identity(\n currency_params,\n \u0026databases.identities_db,\n \u0026databases.ms_db,\n pubkey,\n *idty_wot_id,\n *current_bc_time,\n ms_created_block_id,\n true,\n )?;\n }\n WotsDBsWriteQuery::ExcludeIdentity(ref pubkey, ref blockstamp) =\u003e {\n writers::identity::exclude_identity(\n \u0026databases.identities_db,\n pubkey,\n blockstamp,\n false,\n )?;\n }\n WotsDBsWriteQuery::RevertExcludeIdentity(ref pubkey, ref blockstamp) =\u003e {\n writers::identity::exclude_identity(\n \u0026databases.identities_db,\n pubkey,\n blockstamp,\n true,\n )?;\n }\n WotsDBsWriteQuery::RevokeIdentity(ref pubkey, ref blockstamp, ref explicit) =\u003e {\n writers::identity::revoke_identity(\n \u0026databases.identities_db,\n pubkey,\n blockstamp,\n *explicit,\n false,\n )?;\n }\n WotsDBsWriteQuery::RevertRevokeIdentity(ref pubkey, ref blockstamp, ref explicit) =\u003e {\n writers::identity::revoke_identity(\n \u0026databases.identities_db,\n pubkey,\n blockstamp,\n *explicit,\n true,\n )?;\n }\n WotsDBsWriteQuery::CreateCert(\n ref source_pubkey,\n ref source,\n ref target,\n ref created_block_id,\n ref median_time,\n ) =\u003e {\n trace!(\"WotsDBsWriteQuery::CreateCert...\");\n writers::certification::write_certification(\n currency_params,\n \u0026databases.identities_db,\n \u0026databases.certs_db,\n *source_pubkey,\n *source,\n *target,\n *created_block_id,\n *median_time,\n )?;\n trace!(\"WotsDBsWriteQuery::CreateCert...finish\");\n }\n WotsDBsWriteQuery::RevertCert(ref compact_doc, ref source, ref target) =\u003e {\n trace!(\"WotsDBsWriteQuery::CreateCert...\");\n writers::certification::revert_write_cert(\n \u0026databases.identities_db,\n \u0026databases.certs_db,\n *compact_doc,\n *source,\n *target,\n )?;\n trace!(\"WotsDBsWriteQuery::CreateCert...finish\");\n }\n WotsDBsWriteQuery::ExpireCerts(ref created_block_id) =\u003e {\n super::certification::expire_certs(\u0026databases.certs_db, *created_block_id)?;\n }\n WotsDBsWriteQuery::RevertExpireCert(ref source, ref target, ref created_block_id) =\u003e {\n super::certification::revert_expire_cert(\n \u0026databases.certs_db,\n *source,\n *target,\n *created_block_id,\n )?;\n }\n }\n Ok(())\n }\n}\n\n#[derive(Debug, Clone)]\n/// Contain a pending write request for currency databases\npub enum CurrencyDBsWriteQuery {\n /// Write transaction\n WriteTx(Box\u003cTransactionDocument\u003e),\n /// Revert transaction\n RevertTx(Box\u003cDALTxV10\u003e),\n /// Create dividend\n CreateUD(SourceAmount, BlockNumber, Vec\u003cPubKey\u003e),\n /// Revert dividend\n RevertUD(SourceAmount, BlockNumber, Vec\u003cPubKey\u003e),\n}\n\nimpl CurrencyDBsWriteQuery {\n /// Apply CurrencyDBsWriteQuery\n pub fn apply(\n \u0026self,\n blockstamp: \u0026Blockstamp,\n databases: \u0026CurrencyV10DBs,\n ) -\u003e Result\u003c(), DALError\u003e {\n match *self {\n CurrencyDBsWriteQuery::WriteTx(ref tx_doc) =\u003e {\n super::transaction::apply_and_write_tx(blockstamp, \u0026databases, tx_doc.deref())?;\n }\n CurrencyDBsWriteQuery::RevertTx(ref dal_tx) =\u003e {\n super::transaction::revert_tx(blockstamp, \u0026databases, dal_tx.deref())?;\n }\n CurrencyDBsWriteQuery::CreateUD(ref du_amount, ref block_id, ref members) =\u003e {\n super::dividend::create_du(\n \u0026databases.du_db,\n \u0026databases.balances_db,\n du_amount,\n *block_id,\n members,\n false,\n )?;\n }\n CurrencyDBsWriteQuery::RevertUD(ref du_amount, ref block_id, ref members) =\u003e {\n super::dividend::create_du(\n \u0026databases.du_db,\n \u0026databases.balances_db,\n du_amount,\n *block_id,\n members,\n true,\n )?;\n }\n }\n Ok(())\n }\n}\n","traces":[{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":207,"address":null,"length":0,"stats":{"Line":0}},{"line":208,"address":null,"length":0,"stats":{"Line":0}},{"line":209,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":0}},{"line":230,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":0}},{"line":234,"address":null,"length":0,"stats":{"Line":0}},{"line":235,"address":null,"length":0,"stats":{"Line":0}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":0}},{"line":238,"address":null,"length":0,"stats":{"Line":0}},{"line":239,"address":null,"length":0,"stats":{"Line":0}},{"line":240,"address":null,"length":0,"stats":{"Line":0}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":242,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":244,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":0}},{"line":247,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":249,"address":null,"length":0,"stats":{"Line":0}},{"line":251,"address":null,"length":0,"stats":{"Line":0}},{"line":253,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":255,"address":null,"length":0,"stats":{"Line":0}},{"line":256,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":259,"address":null,"length":0,"stats":{"Line":0}},{"line":260,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":0}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":265,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":268,"address":null,"length":0,"stats":{"Line":0}},{"line":269,"address":null,"length":0,"stats":{"Line":0}},{"line":270,"address":null,"length":0,"stats":{"Line":0}},{"line":271,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":295,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":304,"address":null,"length":0,"stats":{"Line":0}},{"line":305,"address":null,"length":0,"stats":{"Line":0}},{"line":307,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":311,"address":null,"length":0,"stats":{"Line":0}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":313,"address":null,"length":0,"stats":{"Line":0}},{"line":314,"address":null,"length":0,"stats":{"Line":0}},{"line":317,"address":null,"length":0,"stats":{"Line":0}},{"line":318,"address":null,"length":0,"stats":{"Line":0}},{"line":319,"address":null,"length":0,"stats":{"Line":0}},{"line":320,"address":null,"length":0,"stats":{"Line":0}},{"line":321,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":0}},{"line":324,"address":null,"length":0,"stats":{"Line":0}},{"line":328,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":162},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","blockchain","blockchain-dal","src","writers","transaction.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dubp_documents::documents::transaction::*;\nuse durs_common_tools::fatal_error;\n\nuse crate::entities::sources::{SourceAmount, SourceIndexV10, UTXOIndexV10, UTXOV10};\nuse crate::*;\n\n#[derive(Debug, Copy, Clone)]\n/// Transaction error\npub enum TxError {\n /// UnkonwError\n UnkonwError(),\n /// DALError\n DALError(DALError),\n}\n\nimpl From\u003cDALError\u003e for TxError {\n fn from(err: DALError) -\u003e TxError {\n TxError::DALError(err)\n }\n}\n\n#[derive(Debug, Clone, Deserialize, Serialize)]\n/// DAL Transaction V10\npub struct DALTxV10 {\n /// Transaction document\n pub tx_doc: TransactionDocument,\n /// Index of sources destroyed by this transaction\n pub sources_destroyed: HashSet\u003cUTXOIndexV10\u003e,\n}\n\n/// Apply transaction backwards\npub fn revert_tx(\n blockstamp: \u0026Blockstamp,\n dbs: \u0026CurrencyV10DBs,\n dal_tx: \u0026DALTxV10,\n) -\u003e Result\u003c(), DALError\u003e {\n let mut tx_doc = dal_tx.tx_doc.clone();\n let tx_hash = tx_doc.get_hash();\n let sources_destroyed = \u0026dal_tx.sources_destroyed;\n\n // Index consumed utxos\n let consumed_utxos: Vec\u003cUTXOV10\u003e = tx_doc\n .get_outputs()\n .iter()\n .enumerate()\n .map(|(tx_index, output)| UTXOV10(UTXOIndexV10(tx_hash, TxIndex(tx_index)), output.clone()))\n .collect();\n // Recalculate balance of consumed adress\n let new_balances_consumed_adress = dbs.balances_db.read(|db| {\n let mut new_balances_consumed_adress: HashMap\u003c\n UTXOConditionsGroup,\n (SourceAmount, HashSet\u003cUTXOIndexV10\u003e),\n \u003e = HashMap::new();\n for source in \u0026consumed_utxos {\n let source_amount = source.get_amount();\n let conditions = source.get_conditions();\n let (balance, new_sources_index) = if let Some((balance, sources_index)) =\n new_balances_consumed_adress.get(\u0026conditions)\n {\n let mut new_sources_index = sources_index.clone();\n new_sources_index.remove(\u0026source.0);\n (*balance, new_sources_index)\n } else if let Some((balance, sources_index)) = db.get(\u0026conditions) {\n let mut new_sources_index = sources_index.clone();\n new_sources_index.remove(\u0026source.0);\n (*balance, new_sources_index)\n } else {\n fatal_error!(\"Fail to revert tx : an output conditions don't exist in BalancesDB.\")\n };\n let new_balance = if balance \u003e= source_amount {\n balance - source_amount\n } else {\n fatal_error!(\"Fail to revert tx : an output revert cause negative balance.\")\n };\n new_balances_consumed_adress.insert(conditions, (new_balance, new_sources_index));\n }\n new_balances_consumed_adress\n })?;\n // Remove consumed UTXOs\n dbs.utxos_db.write(|db| {\n for utxo_v10 in consumed_utxos {\n db.remove(\u0026utxo_v10.0);\n }\n })?;\n // Write new balance of consumed adress\n dbs.balances_db.write(|db| {\n for (conditions, (balance, sources_index)) in new_balances_consumed_adress {\n db.insert(conditions, (balance, sources_index));\n }\n })?;\n // Complete sources_destroyed\n let sources_destroyed: HashMap\u003cUTXOConditionsGroup, Vec\u003c(UTXOIndexV10, SourceAmount)\u003e\u003e =\n if !sources_destroyed.is_empty() {\n dbs.tx_db.read(|db| {\n let mut sources_destroyed_completed = HashMap::new();\n for s_index in sources_destroyed {\n let tx_output = db\n .get(\u0026s_index.0)\n .expect(\"Not find tx\")\n .tx_doc\n .get_outputs()[(s_index.1).0]\n .clone();\n let mut sources_destroyed_for_same_address: Vec\u003c(UTXOIndexV10, SourceAmount)\u003e =\n sources_destroyed_completed\n .get(\u0026tx_output.conditions.conditions)\n .cloned()\n .unwrap_or_default();\n sources_destroyed_for_same_address\n .push((*s_index, SourceAmount(tx_output.amount, tx_output.base)));\n sources_destroyed_completed.insert(\n tx_output.conditions.conditions,\n sources_destroyed_for_same_address,\n );\n }\n sources_destroyed_completed\n })?\n } else {\n HashMap::with_capacity(0)\n };\n // Index recreated sources\n let recreated_sources: HashMap\u003cSourceIndexV10, SourceAmount\u003e = tx_doc\n .get_inputs()\n .iter()\n .map(|input| match *input {\n TransactionInput::D(tx_amout, tx_amout_base, pubkey, block_id) =\u003e (\n SourceIndexV10::UD(pubkey, block_id),\n SourceAmount(tx_amout, tx_amout_base),\n ),\n TransactionInput::T(tx_amout, tx_amout_base, hash, tx_index) =\u003e (\n SourceIndexV10::UTXO(UTXOIndexV10(hash, tx_index)),\n SourceAmount(tx_amout, tx_amout_base),\n ),\n })\n .collect();\n // Find adress of recreated sources\n let recreated_adress: HashMap\u003cUTXOConditionsGroup, (SourceAmount, HashSet\u003cUTXOIndexV10\u003e)\u003e =\n dbs.utxos_db.read(|db| {\n let mut recreated_adress: HashMap\u003c\n UTXOConditionsGroup,\n (SourceAmount, HashSet\u003cUTXOIndexV10\u003e),\n \u003e = HashMap::new();\n for (source_index, source_amount) in \u0026recreated_sources {\n if let SourceIndexV10::UTXO(utxo_index) = source_index {\n // Get utxo\n let utxo = db.get(\u0026utxo_index).unwrap_or_else(|| {\n fatal_error!(\n \"ApplyBLockError {} : unknow UTXO in inputs : {:?} !\",\n blockstamp,\n utxo_index\n )\n });\n // Get utxo conditions(=address)\n let conditions = \u0026utxo.conditions.conditions;\n // Calculate new balances datas for \"conditions\" address\n let (mut balance, mut utxos_index) = recreated_adress\n .get(conditions)\n .cloned()\n .unwrap_or_default();\n balance = balance + *source_amount;\n utxos_index.insert(*utxo_index);\n // Write new balances datas for \"conditions\" address\n recreated_adress.insert(conditions.clone(), (balance, utxos_index));\n } else if let SourceIndexV10::UD(pubkey, _block_id) = source_index {\n let address =\n UTXOConditionsGroup::Single(TransactionOutputCondition::Sig(*pubkey));\n let (mut balance, utxos_index) =\n recreated_adress.get(\u0026address).cloned().unwrap_or_default();\n balance = balance + *source_amount;\n recreated_adress.insert(address, (balance, utxos_index));\n }\n }\n recreated_adress\n })?;\n // Recalculate balance of recreated adress\n let new_balances_recreated_adress = dbs.balances_db.read(|db| {\n let mut new_balances_recreated_adress = Vec::new();\n for (conditions, (amount_recreated, adress_recreated_sources)) in recreated_adress {\n let (mut balance, mut sources_indexs) =\n if let Some((balance, sources_indexs)) = db.get(\u0026conditions) {\n (*balance, sources_indexs.clone())\n } else {\n (SourceAmount::default(), HashSet::new())\n };\n // Apply recreated sources (inputs)\n balance = balance + amount_recreated;\n for s_index in adress_recreated_sources {\n sources_indexs.insert(s_index);\n }\n // Recreate destroy sources\n if let Some(address_sources_destroyed) = sources_destroyed.get(\u0026conditions) {\n for (utxo_index, s_amout) in address_sources_destroyed {\n balance = balance + *s_amout;\n sources_indexs.insert(*utxo_index);\n }\n }\n new_balances_recreated_adress.push((conditions.clone(), (balance, sources_indexs)));\n }\n new_balances_recreated_adress\n })?;\n // Write new balance of recreated adress\n dbs.balances_db.write(|db| {\n for (conditions, (balance, sources_index)) in new_balances_recreated_adress {\n db.insert(conditions, (balance, sources_index));\n }\n })?;\n // Recreate recreated sources\n for s_index in recreated_sources.keys() {\n if let SourceIndexV10::UTXO(utxo_index) = s_index {\n let utxo_content = dbs.tx_db.read(|db| {\n db.get(\u0026utxo_index.0)\n .expect(\"Fatal error : not found Source TX of this utxo !\")\n .tx_doc\n .get_outputs()[(utxo_index.1).0]\n .clone()\n })?;\n dbs.utxos_db.write(|db| {\n db.insert(*utxo_index, utxo_content);\n })?;\n } else if let SourceIndexV10::UD(pubkey, block_id) = s_index {\n let mut pubkey_dus: HashSet\u003cBlockNumber\u003e = dbs\n .du_db\n .read(|db| db.get(\u0026pubkey).cloned().unwrap_or_default())?;\n pubkey_dus.insert(*block_id);\n dbs.du_db.write(|db| {\n db.insert(*pubkey, pubkey_dus);\n })?;\n }\n }\n Ok(())\n}\n\n/// Apply and write transaction in databases\npub fn apply_and_write_tx(\n blockstamp: \u0026Blockstamp,\n dbs: \u0026CurrencyV10DBs,\n tx_doc: \u0026TransactionDocument,\n) -\u003e Result\u003c(), DALError\u003e {\n let mut tx_doc = tx_doc.clone();\n let tx_hash = tx_doc.get_hash();\n let mut sources_destroyed = HashSet::new();\n // Index consumed sources\n let consumed_sources: HashMap\u003cSourceIndexV10, SourceAmount\u003e = tx_doc\n .get_inputs()\n .iter()\n .map(|input| match *input {\n TransactionInput::D(tx_amout, tx_amout_base, pubkey, block_id) =\u003e (\n SourceIndexV10::UD(pubkey, block_id),\n SourceAmount(tx_amout, tx_amout_base),\n ),\n TransactionInput::T(tx_amout, tx_amout_base, hash, tx_index) =\u003e (\n SourceIndexV10::UTXO(UTXOIndexV10(hash, tx_index)),\n SourceAmount(tx_amout, tx_amout_base),\n ),\n })\n .collect();\n // Find adress of consumed sources\n let consumed_adress: HashMap\u003cUTXOConditionsGroup, (SourceAmount, HashSet\u003cUTXOIndexV10\u003e)\u003e =\n dbs.utxos_db.read(|db| {\n let mut consumed_adress: HashMap\u003c\n UTXOConditionsGroup,\n (SourceAmount, HashSet\u003cUTXOIndexV10\u003e),\n \u003e = HashMap::new();\n for (source_index, source_amount) in \u0026consumed_sources {\n if let SourceIndexV10::UTXO(utxo_index) = source_index {\n // Get utxo\n let utxo = db.get(\u0026utxo_index).unwrap_or_else(|| {\n debug!(\"apply_tx=\\\"{:#?}\\\"\", tx_doc);\n fatal_error!(\n \"ApplyBLockError {} : unknow UTXO in inputs : {:?} !\",\n blockstamp,\n utxo_index\n )\n });\n // Get utxo conditions(=address)\n let conditions = \u0026utxo.conditions.conditions;\n // Calculate new balances datas for \"conditions\" address\n let (mut balance, mut utxos_index) =\n consumed_adress.get(conditions).cloned().unwrap_or_default();\n balance = balance + *source_amount;\n utxos_index.insert(*utxo_index);\n // Write new balances datas for \"conditions\" address\n consumed_adress.insert(conditions.clone(), (balance, utxos_index));\n } else if let SourceIndexV10::UD(pubkey, _block_id) = source_index {\n let address =\n UTXOConditionsGroup::Single(TransactionOutputCondition::Sig(*pubkey));\n let (mut balance, utxos_index) =\n consumed_adress.get(\u0026address).cloned().unwrap_or_default();\n balance = balance + *source_amount;\n consumed_adress.insert(address, (balance, utxos_index));\n }\n }\n consumed_adress\n })?;\n // Recalculate balance of consumed adress\n let new_balances_consumed_adress = dbs.balances_db.read(|db| {\n let mut new_balances_consumed_adress = Vec::new();\n for (conditions, (amount_consumed, adress_consumed_sources)) in consumed_adress {\n if let Some((balance, sources)) = db.get(\u0026conditions) {\n let mut new_balance = *balance - amount_consumed;\n if (new_balance.1 == TxBase(0) \u0026\u0026 new_balance.0 \u003c TxAmount(100))\n || (new_balance.1 == TxBase(1) \u0026\u0026 new_balance.0 \u003c TxAmount(10)) {\n sources_destroyed = sources.union(\u0026sources_destroyed).cloned().collect();\n new_balance = SourceAmount(TxAmount(0), new_balance.1);\n }\n let mut new_sources_index = sources.clone();\n for source in adress_consumed_sources {\n new_sources_index.remove(\u0026source);\n }\n new_balances_consumed_adress\n .push((conditions.clone(), (new_balance, new_sources_index)));\n } else {\n fatal_error!(\"Apply Tx : try to consume a source, but the owner address is not found in balances db : {:?}\", conditions)\n }\n }\n new_balances_consumed_adress\n })?;\n // Write new balance of consumed adress\n dbs.balances_db.write(|db| {\n for (conditions, (balance, sources_index)) in new_balances_consumed_adress {\n db.insert(conditions, (balance, sources_index));\n }\n })?;\n // Remove consumed sources\n for source_index in consumed_sources.keys() {\n if let SourceIndexV10::UTXO(utxo_index) = source_index {\n dbs.utxos_db.write(|db| {\n db.remove(utxo_index);\n })?;\n } else if let SourceIndexV10::UD(pubkey, block_id) = source_index {\n let mut pubkey_dus: HashSet\u003cBlockNumber\u003e = dbs\n .du_db\n .read(|db| db.get(\u0026pubkey).cloned().unwrap_or_default())?;\n pubkey_dus.remove(block_id);\n dbs.du_db.write(|db| {\n db.insert(*pubkey, pubkey_dus);\n })?;\n }\n }\n // Index created sources\n /*let mut created_utxos: Vec\u003cUTXOV10\u003e = Vec::new();\n let mut output_index = 0;\n for output in tx_doc.get_outputs() {\n created_utxos.push(UTXOV10(\n UTXOIndexV10(tx_hash, TxIndex(output_index)),\n output.clone(),\n ));\n output_index += 1;\n }*/\n let created_utxos: Vec\u003cUTXOV10\u003e = tx_doc\n .get_outputs()\n .iter()\n .enumerate()\n .map(|(tx_index, output)| UTXOV10(UTXOIndexV10(tx_hash, TxIndex(tx_index)), output.clone()))\n .collect();\n // Recalculate balance of supplied adress\n let new_balances_supplied_adress = dbs.balances_db.read(|db| {\n let mut new_balances_supplied_adress: HashMap\u003c\n UTXOConditionsGroup,\n (SourceAmount, HashSet\u003cUTXOIndexV10\u003e),\n \u003e = HashMap::new();\n for source in \u0026created_utxos {\n let source_amount = source.get_amount();\n let conditions = source.get_conditions();\n let (balance, new_sources_index) = if let Some((balance, sources_index)) =\n new_balances_supplied_adress.get(\u0026conditions)\n {\n let mut new_sources_index = sources_index.clone();\n new_sources_index.insert(source.0);\n (*balance, new_sources_index)\n } else if let Some((balance, sources_index)) = db.get(\u0026conditions) {\n let mut new_sources_index = sources_index.clone();\n new_sources_index.insert(source.0);\n (*balance, new_sources_index)\n } else {\n let mut new_sources_index = HashSet::new();\n new_sources_index.insert(source.0);\n (SourceAmount::default(), new_sources_index)\n };\n new_balances_supplied_adress\n .insert(conditions, (balance + source_amount, new_sources_index));\n }\n new_balances_supplied_adress\n })?;\n // Insert created UTXOs\n dbs.utxos_db.write(|db| {\n for utxo_v10 in created_utxos {\n db.insert(utxo_v10.0, utxo_v10.1);\n }\n })?;\n // Write new balance of supplied adress\n dbs.balances_db.write(|db| {\n for (conditions, (balance, sources_index)) in new_balances_supplied_adress {\n db.insert(conditions, (balance, sources_index));\n }\n })?;\n // Write tx\n tx_doc.reduce();\n dbs.tx_db.write(|db| {\n db.insert(\n tx_hash,\n DALTxV10 {\n tx_doc,\n sources_destroyed,\n },\n );\n })?;\n Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use dubp_documents::{Document, DocumentBuilder};\n use std::str::FromStr;\n use unwrap::unwrap;\n\n fn build_first_tx_of_g1() -\u003e TransactionDocument {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\")\n .unwrap(),\n );\n let sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==\",\n ).unwrap());\n let block = Blockstamp::from_string(\n \"50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7\",\n )\n .unwrap();\n let builder = TransactionDocumentBuilder {\n currency: \"g1\",\n blockstamp: \u0026block,\n locktime: \u00260,\n issuers: \u0026vec![pubkey],\n inputs: \u0026vec![TransactionInput::from_str(\n \"1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1\",\n )\n .expect(\"fail to parse input !\")],\n unlocks: \u0026vec![\n TransactionInputUnlocks::from_str(\"0:SIG(0)\").expect(\"fail to parse unlock !\")\n ],\n outputs: \u0026vec![\n TransactionOutput::from_str(\n \"1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)\",\n )\n .expect(\"fail to parse output !\"),\n TransactionOutput::from_str(\n \"999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)\",\n )\n .expect(\"fail to parse output !\"),\n ],\n comment: \"TEST\",\n hash: None,\n };\n builder.build_with_signature(vec![sig])\n }\n\n #[test]\n fn apply_and_revert_one_tx() {\n // Get document of first g1 transaction\n let tx_doc = build_first_tx_of_g1();\n assert_eq!(tx_doc.verify_signatures(), Ok(()));\n // Get pubkey of receiver\n let tortue_pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm\")\n .unwrap(),\n );\n // Open currencys_db in memory mode\n let currency_dbs = CurrencyV10DBs::open(None);\n // Create first g1 UD for cgeek and tortue\n writers::dividend::create_du(\n \u0026currency_dbs.du_db,\n \u0026currency_dbs.balances_db,\n \u0026SourceAmount(TxAmount(1000), TxBase(0)),\n BlockNumber(1),\n \u0026vec![tx_doc.issuers()[0], tortue_pubkey],\n false,\n )\n .expect(\"Fail to create first g1 UD !\");\n // Check members balance\n let cgeek_new_balance = currency_dbs\n .balances_db\n .read(|db| {\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(tx_doc.issuers()[0]),\n ))\n .cloned()\n })\n .expect(\"Fail to read cgeek new balance\")\n .expect(\"Error : cgeek is not referenced in balances_db !\");\n assert_eq!(cgeek_new_balance.0, SourceAmount(TxAmount(1000), TxBase(0)));\n let tortue_new_balance = currency_dbs\n .balances_db\n .read(|db| {\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(tortue_pubkey),\n ))\n .cloned()\n })\n .expect(\"Fail to read receiver new balance\")\n .expect(\"Error : receiver is not referenced in balances_db !\");\n assert_eq!(\n tortue_new_balance.0,\n SourceAmount(TxAmount(1000), TxBase(0))\n );\n // Apply first g1 transaction\n let blockstamp = unwrap!(Blockstamp::from_string(\n \"52-000057D4B29AF6DADB16F841F19C54C00EB244CECA9C8F2D4839D54E5F91451C\"\n ));\n apply_and_write_tx(\u0026blockstamp, \u0026currency_dbs, \u0026tx_doc).expect(\"Fail to apply first g1 tx\");\n // Check issuer new balance\n let cgeek_new_balance = currency_dbs\n .balances_db\n .read(|db| {\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(tx_doc.issuers()[0]),\n ))\n .cloned()\n })\n .expect(\"Fail to read cgeek new balance\")\n .expect(\"Error : cgeek is not referenced in balances_db !\");\n assert_eq!(cgeek_new_balance.0, SourceAmount(TxAmount(999), TxBase(0)));\n\n // Check receiver new balance\n let receiver_new_balance = currency_dbs\n .balances_db\n .read(|db| {\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(tortue_pubkey),\n ))\n .cloned()\n })\n .expect(\"Fail to read receiver new balance\")\n .expect(\"Error : receiver is not referenced in balances_db !\");\n assert_eq!(\n receiver_new_balance.0,\n SourceAmount(TxAmount(1001), TxBase(0))\n );\n\n // Revert first g1 tx\n let blockstamp = unwrap!(Blockstamp::from_string(\n \"52-000057D4B29AF6DADB16F841F19C54C00EB244CECA9C8F2D4839D54E5F91451C\"\n ));\n revert_tx(\n \u0026blockstamp,\n \u0026currency_dbs,\n \u0026DALTxV10 {\n tx_doc: tx_doc.clone(),\n sources_destroyed: HashSet::with_capacity(0),\n },\n )\n .expect(\"Fail to revert first g1 tx\");\n\n // Check issuer new balance\n let cgeek_new_balance = currency_dbs\n .balances_db\n .read(|db| {\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(tx_doc.issuers()[0]),\n ))\n .cloned()\n })\n .expect(\"Fail to read cgeek new balance\")\n .expect(\"Error : cgeek is not referenced in balances_db !\");\n assert_eq!(cgeek_new_balance.0, SourceAmount(TxAmount(1000), TxBase(0)));\n\n // Check receiver new balance\n let receiver_new_balance = currency_dbs\n .balances_db\n .read(|db| {\n db.get(\u0026UTXOConditionsGroup::Single(\n TransactionOutputCondition::Sig(tortue_pubkey),\n ))\n .cloned()\n })\n .expect(\"Fail to read receiver new balance\")\n .expect(\"Error : receiver is not referenced in balances_db !\");\n assert_eq!(\n receiver_new_balance.0,\n SourceAmount(TxAmount(1000), TxBase(0))\n );\n }\n}\n","traces":[{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":8044320,"length":1,"stats":{"Line":1}},{"line":52,"address":8044357,"length":1,"stats":{"Line":1}},{"line":53,"address":8044446,"length":1,"stats":{"Line":1}},{"line":54,"address":8044473,"length":1,"stats":{"Line":1}},{"line":57,"address":8044495,"length":1,"stats":{"Line":1}},{"line":61,"address":4701360,"length":1,"stats":{"Line":2}},{"line":62,"address":8044702,"length":1,"stats":{"Line":1}},{"line":64,"address":4701632,"length":1,"stats":{"Line":2}},{"line":65,"address":4701658,"length":1,"stats":{"Line":1}},{"line":68,"address":4701674,"length":1,"stats":{"Line":1}},{"line":69,"address":4701724,"length":1,"stats":{"Line":1}},{"line":70,"address":4701957,"length":1,"stats":{"Line":1}},{"line":71,"address":4702072,"length":1,"stats":{"Line":1}},{"line":72,"address":4702162,"length":1,"stats":{"Line":1}},{"line":73,"address":4702107,"length":1,"stats":{"Line":1}},{"line":75,"address":4702226,"length":1,"stats":{"Line":0}},{"line":76,"address":4702253,"length":1,"stats":{"Line":0}},{"line":77,"address":4702287,"length":1,"stats":{"Line":0}},{"line":78,"address":4702462,"length":1,"stats":{"Line":0}},{"line":79,"address":4702600,"length":1,"stats":{"Line":1}},{"line":80,"address":4702627,"length":1,"stats":{"Line":1}},{"line":81,"address":4702661,"length":1,"stats":{"Line":1}},{"line":82,"address":4702836,"length":1,"stats":{"Line":0}},{"line":83,"address":4702855,"length":1,"stats":{"Line":0}},{"line":85,"address":4704101,"length":1,"stats":{"Line":1}},{"line":86,"address":4704150,"length":1,"stats":{"Line":1}},{"line":88,"address":4704531,"length":1,"stats":{"Line":0}},{"line":90,"address":4704241,"length":1,"stats":{"Line":1}},{"line":92,"address":4702000,"length":1,"stats":{"Line":1}},{"line":95,"address":4705792,"length":1,"stats":{"Line":2}},{"line":96,"address":4705804,"length":1,"stats":{"Line":1}},{"line":97,"address":4706227,"length":1,"stats":{"Line":1}},{"line":101,"address":4706592,"length":1,"stats":{"Line":2}},{"line":102,"address":4706604,"length":1,"stats":{"Line":1}},{"line":103,"address":4707076,"length":1,"stats":{"Line":1}},{"line":108,"address":8046166,"length":1,"stats":{"Line":1}},{"line":109,"address":4707744,"length":1,"stats":{"Line":0}},{"line":110,"address":4707764,"length":1,"stats":{"Line":0}},{"line":111,"address":4707821,"length":1,"stats":{"Line":0}},{"line":112,"address":4708028,"length":1,"stats":{"Line":0}},{"line":113,"address":4708033,"length":1,"stats":{"Line":0}},{"line":116,"address":4708162,"length":1,"stats":{"Line":0}},{"line":117,"address":4708234,"length":1,"stats":{"Line":0}},{"line":119,"address":4708250,"length":1,"stats":{"Line":0}},{"line":120,"address":4708242,"length":1,"stats":{"Line":0}},{"line":122,"address":4708322,"length":1,"stats":{"Line":0}},{"line":123,"address":4708485,"length":1,"stats":{"Line":0}},{"line":124,"address":4708330,"length":1,"stats":{"Line":0}},{"line":125,"address":4708608,"length":1,"stats":{"Line":0}},{"line":126,"address":4708512,"length":1,"stats":{"Line":0}},{"line":127,"address":4708568,"length":1,"stats":{"Line":0}},{"line":130,"address":4708069,"length":1,"stats":{"Line":0}},{"line":132,"address":8046295,"length":1,"stats":{"Line":0}},{"line":133,"address":8046699,"length":1,"stats":{"Line":1}},{"line":136,"address":8046725,"length":1,"stats":{"Line":1}},{"line":139,"address":4708912,"length":1,"stats":{"Line":1}},{"line":140,"address":4708932,"length":1,"stats":{"Line":1}},{"line":141,"address":4709083,"length":1,"stats":{"Line":1}},{"line":142,"address":4709195,"length":1,"stats":{"Line":1}},{"line":144,"address":4709281,"length":1,"stats":{"Line":0}},{"line":145,"address":4709385,"length":1,"stats":{"Line":0}},{"line":146,"address":4709581,"length":1,"stats":{"Line":0}},{"line":152,"address":4711344,"length":1,"stats":{"Line":2}},{"line":153,"address":4711378,"length":1,"stats":{"Line":1}},{"line":156,"address":4711402,"length":1,"stats":{"Line":1}},{"line":157,"address":4711449,"length":1,"stats":{"Line":1}},{"line":158,"address":4711731,"length":1,"stats":{"Line":1}},{"line":160,"address":4709696,"length":1,"stats":{"Line":0}},{"line":161,"address":4709726,"length":1,"stats":{"Line":0}},{"line":163,"address":4709908,"length":1,"stats":{"Line":0}},{"line":164,"address":4709916,"length":1,"stats":{"Line":0}},{"line":168,"address":4711939,"length":1,"stats":{"Line":0}},{"line":170,"address":4711967,"length":1,"stats":{"Line":0}},{"line":171,"address":4711959,"length":1,"stats":{"Line":0}},{"line":174,"address":4712146,"length":1,"stats":{"Line":0}},{"line":175,"address":4712243,"length":1,"stats":{"Line":0}},{"line":177,"address":4712317,"length":1,"stats":{"Line":0}},{"line":178,"address":4712198,"length":1,"stats":{"Line":0}},{"line":180,"address":4712651,"length":1,"stats":{"Line":1}},{"line":181,"address":4712897,"length":1,"stats":{"Line":1}},{"line":182,"address":4712814,"length":1,"stats":{"Line":1}},{"line":183,"address":4713001,"length":1,"stats":{"Line":1}},{"line":184,"address":4713098,"length":1,"stats":{"Line":1}},{"line":187,"address":4711768,"length":1,"stats":{"Line":1}},{"line":190,"address":4713600,"length":1,"stats":{"Line":2}},{"line":191,"address":4713618,"length":1,"stats":{"Line":1}},{"line":192,"address":4713728,"length":1,"stats":{"Line":1}},{"line":193,"address":4714619,"length":1,"stats":{"Line":1}},{"line":194,"address":4714262,"length":1,"stats":{"Line":1}},{"line":195,"address":4714354,"length":1,"stats":{"Line":1}},{"line":197,"address":4714482,"length":1,"stats":{"Line":0}},{"line":200,"address":4714723,"length":1,"stats":{"Line":1}},{"line":201,"address":4714821,"length":1,"stats":{"Line":1}},{"line":202,"address":4715236,"length":1,"stats":{"Line":0}},{"line":205,"address":4715347,"length":1,"stats":{"Line":1}},{"line":206,"address":4715436,"length":1,"stats":{"Line":0}},{"line":207,"address":4715650,"length":1,"stats":{"Line":0}},{"line":208,"address":4715730,"length":1,"stats":{"Line":0}},{"line":211,"address":4715825,"length":1,"stats":{"Line":1}},{"line":213,"address":4716243,"length":1,"stats":{"Line":1}},{"line":216,"address":4716912,"length":1,"stats":{"Line":2}},{"line":217,"address":4716924,"length":1,"stats":{"Line":1}},{"line":218,"address":4717403,"length":1,"stats":{"Line":1}},{"line":222,"address":8048385,"length":1,"stats":{"Line":1}},{"line":223,"address":8048621,"length":1,"stats":{"Line":1}},{"line":224,"address":4718064,"length":1,"stats":{"Line":0}},{"line":225,"address":4718081,"length":1,"stats":{"Line":0}},{"line":228,"address":4718177,"length":1,"stats":{"Line":0}},{"line":231,"address":4718288,"length":1,"stats":{"Line":0}},{"line":232,"address":4718300,"length":1,"stats":{"Line":0}},{"line":234,"address":8049194,"length":1,"stats":{"Line":0}},{"line":235,"address":8049654,"length":1,"stats":{"Line":1}},{"line":237,"address":4718448,"length":1,"stats":{"Line":2}},{"line":238,"address":8052018,"length":1,"stats":{"Line":1}},{"line":239,"address":4718544,"length":1,"stats":{"Line":2}},{"line":240,"address":4718556,"length":1,"stats":{"Line":1}},{"line":244,"address":8048650,"length":1,"stats":{"Line":1}},{"line":248,"address":8052608,"length":1,"stats":{"Line":1}},{"line":253,"address":8052645,"length":1,"stats":{"Line":1}},{"line":254,"address":8052747,"length":1,"stats":{"Line":1}},{"line":255,"address":8052774,"length":1,"stats":{"Line":1}},{"line":257,"address":8052793,"length":1,"stats":{"Line":1}},{"line":260,"address":4718704,"length":1,"stats":{"Line":1}},{"line":261,"address":4718724,"length":1,"stats":{"Line":1}},{"line":262,"address":4718875,"length":1,"stats":{"Line":1}},{"line":263,"address":4718987,"length":1,"stats":{"Line":1}},{"line":265,"address":4719073,"length":1,"stats":{"Line":0}},{"line":266,"address":4719177,"length":1,"stats":{"Line":0}},{"line":267,"address":4719373,"length":1,"stats":{"Line":0}},{"line":273,"address":4721536,"length":1,"stats":{"Line":2}},{"line":274,"address":4721554,"length":1,"stats":{"Line":1}},{"line":277,"address":4721578,"length":1,"stats":{"Line":1}},{"line":278,"address":4721638,"length":1,"stats":{"Line":1}},{"line":279,"address":4721915,"length":1,"stats":{"Line":1}},{"line":281,"address":4719488,"length":1,"stats":{"Line":0}},{"line":282,"address":4719502,"length":1,"stats":{"Line":0}},{"line":283,"address":4719928,"length":1,"stats":{"Line":0}},{"line":285,"address":4720118,"length":1,"stats":{"Line":0}},{"line":286,"address":4720122,"length":1,"stats":{"Line":0}},{"line":290,"address":4722131,"length":1,"stats":{"Line":0}},{"line":292,"address":4722234,"length":1,"stats":{"Line":0}},{"line":293,"address":4722151,"length":1,"stats":{"Line":0}},{"line":294,"address":4722338,"length":1,"stats":{"Line":0}},{"line":295,"address":4722435,"length":1,"stats":{"Line":0}},{"line":297,"address":4722509,"length":1,"stats":{"Line":0}},{"line":298,"address":4722390,"length":1,"stats":{"Line":0}},{"line":300,"address":4722843,"length":1,"stats":{"Line":1}},{"line":301,"address":4723089,"length":1,"stats":{"Line":1}},{"line":302,"address":4723006,"length":1,"stats":{"Line":1}},{"line":303,"address":4723193,"length":1,"stats":{"Line":1}},{"line":304,"address":4723290,"length":1,"stats":{"Line":1}},{"line":307,"address":4721952,"length":1,"stats":{"Line":1}},{"line":310,"address":4723792,"length":1,"stats":{"Line":2}},{"line":311,"address":4723810,"length":1,"stats":{"Line":1}},{"line":312,"address":4723920,"length":1,"stats":{"Line":1}},{"line":313,"address":4724454,"length":1,"stats":{"Line":1}},{"line":314,"address":4724546,"length":1,"stats":{"Line":1}},{"line":315,"address":4724644,"length":1,"stats":{"Line":1}},{"line":316,"address":4724675,"length":1,"stats":{"Line":0}},{"line":317,"address":4724899,"length":1,"stats":{"Line":1}},{"line":318,"address":4727967,"length":1,"stats":{"Line":1}},{"line":320,"address":4725018,"length":1,"stats":{"Line":1}},{"line":321,"address":4725053,"length":1,"stats":{"Line":1}},{"line":322,"address":4725477,"length":1,"stats":{"Line":0}},{"line":324,"address":4725854,"length":1,"stats":{"Line":1}},{"line":325,"address":4725551,"length":1,"stats":{"Line":1}},{"line":326,"address":4725881,"length":1,"stats":{"Line":1}},{"line":327,"address":4725919,"length":1,"stats":{"Line":0}},{"line":330,"address":4727391,"length":1,"stats":{"Line":1}},{"line":333,"address":4728272,"length":1,"stats":{"Line":2}},{"line":334,"address":4728284,"length":1,"stats":{"Line":1}},{"line":335,"address":4728763,"length":1,"stats":{"Line":1}},{"line":339,"address":8054469,"length":1,"stats":{"Line":1}},{"line":340,"address":8054705,"length":1,"stats":{"Line":1}},{"line":341,"address":4729424,"length":1,"stats":{"Line":0}},{"line":342,"address":4729437,"length":1,"stats":{"Line":0}},{"line":344,"address":8055250,"length":1,"stats":{"Line":1}},{"line":345,"address":8055318,"length":1,"stats":{"Line":1}},{"line":347,"address":4729488,"length":1,"stats":{"Line":2}},{"line":348,"address":8059249,"length":1,"stats":{"Line":1}},{"line":349,"address":4729584,"length":1,"stats":{"Line":2}},{"line":350,"address":4729596,"length":1,"stats":{"Line":1}},{"line":364,"address":8054734,"length":1,"stats":{"Line":1}},{"line":368,"address":4729744,"length":1,"stats":{"Line":2}},{"line":369,"address":8056514,"length":1,"stats":{"Line":1}},{"line":371,"address":4730016,"length":1,"stats":{"Line":2}},{"line":372,"address":4730042,"length":1,"stats":{"Line":1}},{"line":375,"address":4730066,"length":1,"stats":{"Line":1}},{"line":376,"address":4730116,"length":1,"stats":{"Line":1}},{"line":377,"address":4730349,"length":1,"stats":{"Line":1}},{"line":378,"address":4730464,"length":1,"stats":{"Line":1}},{"line":379,"address":4730554,"length":1,"stats":{"Line":1}},{"line":380,"address":4730499,"length":1,"stats":{"Line":1}},{"line":382,"address":4730618,"length":1,"stats":{"Line":0}},{"line":383,"address":4730645,"length":1,"stats":{"Line":0}},{"line":384,"address":4730722,"length":1,"stats":{"Line":0}},{"line":385,"address":4730891,"length":1,"stats":{"Line":0}},{"line":386,"address":4731023,"length":1,"stats":{"Line":1}},{"line":387,"address":4731050,"length":1,"stats":{"Line":1}},{"line":388,"address":4731124,"length":1,"stats":{"Line":1}},{"line":389,"address":4731269,"length":1,"stats":{"Line":0}},{"line":390,"address":4731288,"length":1,"stats":{"Line":0}},{"line":391,"address":4731307,"length":1,"stats":{"Line":0}},{"line":392,"address":4731381,"length":1,"stats":{"Line":0}},{"line":394,"address":4731932,"length":1,"stats":{"Line":1}},{"line":395,"address":4731634,"length":1,"stats":{"Line":1}},{"line":397,"address":4730392,"length":1,"stats":{"Line":1}},{"line":400,"address":4732288,"length":1,"stats":{"Line":2}},{"line":401,"address":4732300,"length":1,"stats":{"Line":1}},{"line":402,"address":4732723,"length":1,"stats":{"Line":1}},{"line":406,"address":4733184,"length":1,"stats":{"Line":2}},{"line":407,"address":4733196,"length":1,"stats":{"Line":1}},{"line":408,"address":4733668,"length":1,"stats":{"Line":1}},{"line":412,"address":8057894,"length":1,"stats":{"Line":1}},{"line":413,"address":4734336,"length":1,"stats":{"Line":2}},{"line":414,"address":4734348,"length":1,"stats":{"Line":1}},{"line":415,"address":4734353,"length":1,"stats":{"Line":1}},{"line":416,"address":4734511,"length":1,"stats":{"Line":1}},{"line":417,"address":4734403,"length":1,"stats":{"Line":1}},{"line":418,"address":4734465,"length":1,"stats":{"Line":1}},{"line":422,"address":8058457,"length":1,"stats":{"Line":1}},{"line":432,"address":8294224,"length":1,"stats":{"Line":1}},{"line":433,"address":8294321,"length":1,"stats":{"Line":1}},{"line":434,"address":8294241,"length":1,"stats":{"Line":1}},{"line":437,"address":8294378,"length":1,"stats":{"Line":1}},{"line":440,"address":8294477,"length":1,"stats":{"Line":1}},{"line":444,"address":8295390,"length":1,"stats":{"Line":1}},{"line":448,"address":8294522,"length":1,"stats":{"Line":1}},{"line":449,"address":8294647,"length":1,"stats":{"Line":1}},{"line":453,"address":8294839,"length":1,"stats":{"Line":1}},{"line":454,"address":8294860,"length":1,"stats":{"Line":1}},{"line":456,"address":8295013,"length":1,"stats":{"Line":1}},{"line":457,"address":8295026,"length":1,"stats":{"Line":1}},{"line":461,"address":8295110,"length":1,"stats":{"Line":1}},{"line":467,"address":8295382,"length":1,"stats":{"Line":1}},{"line":469,"address":8295595,"length":1,"stats":{"Line":1}},{"line":473,"address":8296096,"length":1,"stats":{"Line":2}},{"line":475,"address":8296109,"length":1,"stats":{"Line":1}},{"line":476,"address":8296139,"length":1,"stats":{"Line":1}},{"line":478,"address":8296717,"length":1,"stats":{"Line":1}},{"line":479,"address":8296663,"length":1,"stats":{"Line":1}},{"line":483,"address":8296757,"length":1,"stats":{"Line":1}},{"line":485,"address":8297207,"length":1,"stats":{"Line":1}},{"line":487,"address":8296800,"length":1,"stats":{"Line":1}},{"line":489,"address":8296808,"length":1,"stats":{"Line":1}},{"line":490,"address":8296837,"length":1,"stats":{"Line":1}},{"line":493,"address":8297189,"length":1,"stats":{"Line":0}},{"line":495,"address":8297320,"length":1,"stats":{"Line":1}},{"line":497,"address":8297336,"length":1,"stats":{"Line":2}},{"line":498,"address":8390740,"length":1,"stats":{"Line":1}},{"line":499,"address":8390745,"length":1,"stats":{"Line":1}},{"line":505,"address":8297445,"length":1,"stats":{"Line":1}},{"line":506,"address":8297904,"length":1,"stats":{"Line":1}},{"line":508,"address":8297920,"length":1,"stats":{"Line":2}},{"line":509,"address":8391076,"length":1,"stats":{"Line":1}},{"line":510,"address":8391081,"length":1,"stats":{"Line":1}},{"line":516,"address":8298029,"length":1,"stats":{"Line":1}},{"line":521,"address":8298488,"length":1,"stats":{"Line":1}},{"line":524,"address":8298630,"length":1,"stats":{"Line":1}},{"line":526,"address":8298673,"length":1,"stats":{"Line":1}},{"line":528,"address":8298689,"length":1,"stats":{"Line":2}},{"line":529,"address":8391348,"length":1,"stats":{"Line":1}},{"line":530,"address":8391353,"length":1,"stats":{"Line":1}},{"line":536,"address":8298798,"length":1,"stats":{"Line":1}},{"line":539,"address":8299257,"length":1,"stats":{"Line":1}},{"line":541,"address":8299273,"length":1,"stats":{"Line":2}},{"line":542,"address":8391684,"length":1,"stats":{"Line":1}},{"line":543,"address":8391689,"length":1,"stats":{"Line":1}},{"line":549,"address":8299382,"length":1,"stats":{"Line":1}},{"line":555,"address":8299841,"length":1,"stats":{"Line":1}},{"line":558,"address":8300138,"length":1,"stats":{"Line":1}},{"line":561,"address":8300001,"length":1,"stats":{"Line":1}},{"line":562,"address":8299975,"length":1,"stats":{"Line":1}},{"line":563,"address":8299994,"length":1,"stats":{"Line":1}},{"line":566,"address":8300170,"length":1,"stats":{"Line":0}},{"line":569,"address":8300232,"length":1,"stats":{"Line":1}},{"line":571,"address":8300248,"length":1,"stats":{"Line":2}},{"line":572,"address":8391956,"length":1,"stats":{"Line":1}},{"line":573,"address":8391961,"length":1,"stats":{"Line":1}},{"line":579,"address":8300357,"length":1,"stats":{"Line":1}},{"line":582,"address":8300786,"length":1,"stats":{"Line":1}},{"line":584,"address":8300802,"length":1,"stats":{"Line":2}},{"line":585,"address":8392292,"length":1,"stats":{"Line":1}},{"line":586,"address":8392297,"length":1,"stats":{"Line":1}},{"line":592,"address":8300911,"length":1,"stats":{"Line":1}}],"covered":206,"coverable":287},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","skeleton","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module template to copy to create a new Dunitrust module.\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde_derive;\n#[macro_use]\nextern crate structopt;\n\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_common_tools::traits::merge::Merge;\nuse durs_conf::DuRsConf;\nuse durs_message::events::*;\nuse durs_message::*;\nuse durs_module::*;\nuse durs_network::events::NetworkEvent;\nuse std::ops::Deref;\nuse std::sync::mpsc;\nuse std::thread;\nuse std::time::{Duration, SystemTime};\n\n/// Name of your module\npub static MODULE_NAME: \u0026'static str = \"skeleton\";\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Skeleton Module Configuration\npub struct SkeletonConf {\n test_fake_conf_field: String,\n}\n\nimpl Default for SkeletonConf {\n fn default() -\u003e Self {\n SkeletonConf {\n test_fake_conf_field: String::from(\"default value\"),\n }\n }\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Skeleton Module Configuration\npub struct SkeletonUserConf {\n test_fake_conf_field: Option\u003cString\u003e,\n}\n\nimpl Merge for SkeletonUserConf {\n fn merge(self, other: Self) -\u003e Self {\n SkeletonUserConf {\n test_fake_conf_field: self.test_fake_conf_field.or(other.test_fake_conf_field),\n }\n }\n}\n\n#[derive(Debug, Copy, Clone)]\n/// Message from others thread of skeleton module\npub enum SkeletonThreadMsg {}\n\n#[derive(Debug, Clone)]\n/// Format of messages received by the skeleton module\npub enum SkeletonMsg {\n /// Message from another module\n DursMsg(Box\u003cDursMsg\u003e),\n /// Message from others thread of skeleton module\n SkeletonThreadMsg(SkeletonThreadMsg),\n}\n\n#[derive(StructOpt, Debug, Clone)]\n#[structopt(\n name = \"skeleton\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// Skeleton subcommand options\npub struct SkeletonOpt {\n /// Change test conf fake field\n pub new_conf_field: String,\n}\n\n#[derive(Debug, Clone)]\n/// Data that the Skeleton module needs to cache\npub struct SkeletonModuleDatas {\n /// Sender of all child threads (except the proxy thread)\n pub child_threads: Vec\u003cmpsc::Sender\u003cSkeletonMsg\u003e\u003e,\n /// Any data\n pub field: usize,\n}\n\n#[derive(Debug, Copy, Clone)]\n/// Skeleton module\npub struct SkeletonModule {}\n\nimpl Default for SkeletonModule {\n fn default() -\u003e SkeletonModule {\n SkeletonModule {}\n }\n}\n\nimpl DursModule\u003cDuRsConf, DursMsg\u003e for SkeletonModule {\n type ModuleUserConf = SkeletonUserConf;\n type ModuleConf = SkeletonConf;\n type ModuleOpt = SkeletonOpt;\n\n fn name() -\u003e ModuleStaticName {\n ModuleStaticName(MODULE_NAME)\n }\n fn priority() -\u003e ModulePriority {\n //ModulePriority::Recommended()\n ModulePriority::Optional()\n }\n fn ask_required_keys() -\u003e RequiredKeys {\n RequiredKeys::None()\n }\n fn have_subcommand() -\u003e bool {\n true\n }\n fn generate_module_conf(\n _currency_name: Option\u003c\u0026CurrencyName\u003e,\n _global_conf: \u0026\u003cDuRsConf as DursConfTrait\u003e::GlobalConf,\n module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n ) -\u003e Result\u003c(Self::ModuleConf, Option\u003cSelf::ModuleUserConf\u003e), ModuleConfError\u003e {\n let mut conf = SkeletonConf::default();\n\n if let Some(ref module_user_conf) = module_user_conf {\n if let Some(ref test_fake_conf_field) = module_user_conf.test_fake_conf_field {\n conf.test_fake_conf_field = test_fake_conf_field.to_owned();\n }\n }\n\n Ok((conf, module_user_conf))\n }\n fn exec_subcommand(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n module_conf: Self::ModuleConf,\n _module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n subcommand_args: Self::ModuleOpt,\n ) -\u003e Option\u003cSelf::ModuleUserConf\u003e {\n let new_skeleton_conf = SkeletonUserConf {\n test_fake_conf_field: Some(subcommand_args.new_conf_field.to_owned()),\n };\n println!(\n \"Succesfully exec skeleton subcommand whit terminal name : {} and conf={:?}!\",\n subcommand_args.new_conf_field, module_conf\n );\n Some(new_skeleton_conf)\n }\n fn start(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n _conf: Self::ModuleConf,\n router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) -\u003e Result\u003c(), failure::Error\u003e {\n let _start_time = SystemTime::now();\n\n // Instanciate Skeleton module datas\n let datas = SkeletonModuleDatas {\n child_threads: Vec::new(),\n field: 3,\n };\n\n // Create skeleton main thread channel\n let (skeleton_sender, skeleton_receiver): (\n mpsc::Sender\u003cSkeletonMsg\u003e,\n mpsc::Receiver\u003cSkeletonMsg\u003e,\n ) = mpsc::channel();\n\n // Create proxy channel\n let (proxy_sender, proxy_receiver): (mpsc::Sender\u003cDursMsg\u003e, mpsc::Receiver\u003cDursMsg\u003e) =\n mpsc::channel();\n\n // Launch a proxy thread that transform DursMsgContent() to SkeleonMsg::DursMsgContent(DursMsgContent())\n let router_sender_clone = router_sender.clone();\n let skeleton_sender_clone = skeleton_sender.clone();\n thread::spawn(move || {\n // Send skeleton module registration to router thread\n router_sender_clone\n .send(RouterThreadMessage::ModuleRegistration {\n static_name: ModuleStaticName(MODULE_NAME),\n sender: proxy_sender, // Messages sent by the router will be received by your proxy thread\n roles: vec![ModuleRole::UserInterface], // Roles assigned to your module\n events_subscription: vec![ModuleEvent::NewValidBlock], // Events to which your module subscribes\n reserved_apis_parts: vec![],\n endpoints: vec![],\n })\n .expect(\"Fatal error : skeleton module fail to register to router !\"); // The registration of your module must be successful, in case of failure the program must be interrupted.\n\n // If we are here it means that your module has successfully registered, we indicate it in the debug level log, it can be helpful.\n debug!(\"Send skeleton module registration to router thread.\");\n\n /*\n * Main loop of your proxy thread\n */\n loop {\n match proxy_receiver.recv() {\n Ok(message) =\u003e {\n let stop = if let DursMsg::Stop = message {\n true\n } else {\n false\n };\n if skeleton_sender_clone\n .send(SkeletonMsg::DursMsg(Box::new(message)))\n .is_err()\n {\n // Log error\n warn!(\n \"Skeleton proxy : fail to relay DursMsg to skeleton main thread !\"\n )\n }\n if stop {\n break;\n }\n }\n Err(e) =\u003e {\n // Log error\n warn!(\"{}\", e);\n break;\n }\n }\n }\n });\n\n /*\n * Main loop of your module\n */\n loop {\n // Get messages\n match skeleton_receiver.recv_timeout(Duration::from_millis(250)) {\n Ok(ref message) =\u003e match *message {\n SkeletonMsg::DursMsg(ref durs_message) =\u003e {\n match durs_message.deref() {\n DursMsg::Stop =\u003e {\n // Relay stop signal to all child threads\n let _result_stop_propagation: Result\u003c\n (),\n mpsc::SendError\u003cSkeletonMsg\u003e,\n \u003e = datas\n .child_threads\n .iter()\n .map(|t| t.send(SkeletonMsg::DursMsg(Box::new(DursMsg::Stop))))\n .collect();\n // Relay stop signal to router\n let _result = router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Stop));\n // Break main loop\n break;\n }\n DursMsg::Event {\n ref event_content, ..\n } =\u003e match *event_content {\n DursEvent::BlockchainEvent(ref blockchain_event) =\u003e {\n match *blockchain_event.deref() {\n BlockchainEvent::StackUpValidBlock(ref _block) =\u003e {\n // Do something when the node has stacked a new block at its local blockchain\n }\n BlockchainEvent::RevertBlocks(ref _blocks) =\u003e {\n // Do something when the node has destacked blocks from its local blockchain (roll back)\n }\n _ =\u003e {} // Do nothing for events that don't concern your module.\n }\n }\n DursEvent::NetworkEvent(ref network_event_box) =\u003e {\n match *network_event_box.deref() {\n NetworkEvent::ReceivePeers(ref _peers) =\u003e {\n // Do something when the node receive peers cards from network\n }\n NetworkEvent::ReceiveDocuments(ref _bc_documents) =\u003e {\n // Do something when the node receive blockchain documents from network\n }\n _ =\u003e {} // Do nothing for events that don't concern your module.\n }\n }\n _ =\u003e {} // Do nothing for DursEvent variants that don't concern your module.\n },\n _ =\u003e {} // Do nothing for DursMsgContent variants that don't concern your module.\n }\n }\n SkeletonMsg::SkeletonThreadMsg(ref _child_thread_msg) =\u003e {\n // Do something when receive a message from child thread.\n }\n },\n Err(e) =\u003e match e {\n mpsc::RecvTimeoutError::Disconnected =\u003e {\n fatal_error!(\"Disconnected skeleton module !\");\n }\n mpsc::RecvTimeoutError::Timeout =\u003e {\n // If you arrive here it's because your main thread did not receive anything at the end of the timeout.\n // This is quite normal and happens regularly when there is little activity, there is nothing particular to do.\n }\n },\n }\n // If you want your module's main thread to do things even when it doesn't receive any messages, this is the place where it can do them.\n // ...\n }\n // If we reach this point it means that the module has stopped correctly, so we return OK.\n Ok(())\n }\n}\n","traces":[{"line":1,"address":4248781,"length":1,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":205,"address":4326768,"length":1,"stats":{"Line":0}},{"line":206,"address":4326805,"length":1,"stats":{"Line":0}},{"line":207,"address":4326933,"length":1,"stats":{"Line":0}},{"line":208,"address":null,"length":0,"stats":{"Line":0}},{"line":209,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":223,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":0}},{"line":232,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":0}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":0}},{"line":240,"address":null,"length":0,"stats":{"Line":0}},{"line":242,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":252,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":255,"address":null,"length":0,"stats":{"Line":0}},{"line":256,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":260,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":0}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":265,"address":null,"length":0,"stats":{"Line":0}},{"line":266,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":269,"address":null,"length":0,"stats":{"Line":0}},{"line":270,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":0}},{"line":274,"address":null,"length":0,"stats":{"Line":0}},{"line":275,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":277,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":279,"address":null,"length":0,"stats":{"Line":0}},{"line":282,"address":null,"length":0,"stats":{"Line":0}},{"line":288,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":null,"length":0,"stats":{"Line":0}},{"line":290,"address":null,"length":0,"stats":{"Line":0}},{"line":293,"address":null,"length":0,"stats":{"Line":0}},{"line":304,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":97},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","tui","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Defined the few global types used by all modules,\n//! as well as the DursModule trait that all modules must implement.\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde_derive;\n#[macro_use]\nextern crate structopt;\n\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_common_tools::traits::merge::Merge;\nuse durs_conf::DuRsConf;\nuse durs_message::events::*;\nuse durs_message::*;\nuse durs_module::*;\nuse durs_network::events::NetworkEvent;\nuse durs_network_documents::network_head::NetworkHead;\nuse durs_network_documents::NodeFullId;\nuse std::collections::HashMap;\nuse std::io::{stdout, Write};\nuse std::ops::Deref;\nuse std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender};\nuse std::thread;\nuse std::time::{Duration, SystemTime};\nuse termion::event::*;\nuse termion::input::{MouseTerminal, TermRead};\nuse termion::raw::{IntoRawMode, RawTerminal};\nuse termion::{clear, color, cursor, style};\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Tui Module Configuration (For future use)\npub struct TuiConf {}\n\nimpl Default for TuiConf {\n fn default() -\u003e Self {\n TuiConf {}\n }\n}\n\nimpl Merge for TuiConf {\n fn merge(self, _: Self) -\u003e Self {\n self\n }\n}\n\n#[derive(Debug, Clone)]\n/// Format of messages received by the tui module\npub enum TuiMess {\n /// Message from another module\n DursMsg(Box\u003cDursMsg\u003e),\n /// Message from stdin (user event)\n TermionEvent(Event),\n}\n\n#[derive(Debug, Copy, Clone)]\n/// Tui module\npub struct TuiModule {}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n#[structopt(\n name = \"tui\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// Tui subcommand options\npub struct TuiOpt {}\n\n#[derive(Debug, Clone)]\n/// Network connexion (data to display)\npub struct Connection {\n /// Connexion status\n status: u32,\n /// Endpoint url\n url: String,\n /// Node uid at the other end of the connection (member nodes only)\n uid: Option\u003cString\u003e,\n}\n\n#[derive(Debug, Clone)]\n/// Data that the Tui module needs to cache\npub struct TuiModuleDatas {\n /// Sender of all other modules\n pub router_sender: Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n /// HEADs cache content\n pub heads_cache: HashMap\u003cNodeFullId, NetworkHead\u003e,\n /// Position of the 1st head displayed on the screen\n pub heads_index: usize,\n /// Connections cache content\n pub connections_status: HashMap\u003cNodeFullId, Connection\u003e,\n /// Number of connections in `Established` status\n pub established_conns_count: usize,\n}\n\nimpl TuiModuleDatas {\n /// Draw terminal\n fn draw_term\u003cW: Write\u003e(\n \u0026self,\n stdout: \u0026mut RawTerminal\u003cW\u003e,\n start_time: SystemTime,\n heads_cache: \u0026HashMap\u003cNodeFullId, NetworkHead\u003e,\n heads_index: usize,\n out_connections_status: \u0026HashMap\u003cNodeFullId, Connection\u003e,\n _in_connections_status: \u0026HashMap\u003cNodeFullId, Connection\u003e,\n ) {\n // Get Terminal size\n let (w, h) = termion::terminal_size().expect(\"Fail to get terminal size !\");\n\n // Prepare connections screen\n let mut out_never_try_conns_count = 0;\n let mut out_unreachable_conns_count = 0;\n let mut out_trying_conns_count = 0;\n let mut out_denial_conns_count = 0;\n let mut out_disconnected_conns_count = 0;\n let mut out_established_conns = Vec::new();\n for (node_full_id, connection) in out_connections_status {\n match connection.status {\n 0 =\u003e out_never_try_conns_count += 1,\n 2 | 4 =\u003e out_unreachable_conns_count += 1,\n 1 | 3 | 5 | 7 | 8 | 9 =\u003e out_trying_conns_count += 1,\n 10 =\u003e out_denial_conns_count += 1,\n 11 =\u003e out_disconnected_conns_count += 1,\n 12 =\u003e out_established_conns.push((\n node_full_id,\n connection.uid.clone(),\n connection.url.clone(),\n )),\n _ =\u003e {}\n }\n }\n\n // Prepare HEADs screen\n let mut heads = heads_cache.values().collect::\u003cVec\u003c\u0026NetworkHead\u003e\u003e();\n heads.sort_unstable_by(|a, b| b.cmp(a));\n let heads_window_size = h as isize - 8 - out_established_conns.len() as isize;\n let heads_index_max = if heads_window_size \u003e 0 \u0026\u0026 heads.len() \u003e heads_window_size as usize {\n heads.len() - heads_window_size as usize\n } else {\n 0\n };\n\n // Clear term and reset background color\n write!(\n stdout,\n \"{}{}{}\",\n color::Bg(color::Black),\n clear::All,\n cursor::Goto(1, 1)\n )\n .unwrap();\n\n // Draw headers\n let mut line = 1;\n write!(\n stdout,\n \"{}{}{} established connections : \",\n cursor::Goto(1, line),\n color::Fg(color::White),\n out_established_conns.len()\n )\n .unwrap();\n line += 1;\n write!(\n stdout,\n \"{}{}{} NodeId-PubKey\",\n cursor::Goto(1, line),\n color::Fg(color::White),\n style::Italic,\n )\n .unwrap();\n\n // Draw inter-nodes established connections\n if out_established_conns.is_empty() {\n line += 1;\n write!(\n stdout,\n \"{}{}{}No established connections !\",\n cursor::Goto(2, line),\n color::Fg(color::Red),\n style::Bold,\n )\n .unwrap();\n } else {\n for (ref node_full_id, ref uid, ref url) in out_established_conns {\n line += 1;\n let mut uid_string = uid\n .clone()\n .unwrap_or_else(|| String::from(\"----------------\"));\n uid_string.truncate(16);\n write!(\n stdout,\n \"{}{} {} {:16} {}\",\n cursor::Goto(2, line),\n color::Fg(color::Green),\n node_full_id.to_human_string(),\n uid_string,\n url,\n )\n .unwrap();\n }\n }\n\n // Draw number of conns per state\n line += 1;\n write!(\n stdout,\n \"{}{}{} know endpoints : {} Never try, {} Unreach, {} on trial, {} Denial, {} Close.\",\n cursor::Goto(2, line),\n color::Fg(color::Rgb(128, 128, 128)),\n out_connections_status.len(),\n out_never_try_conns_count,\n out_unreachable_conns_count,\n out_trying_conns_count,\n out_denial_conns_count,\n out_disconnected_conns_count,\n )\n .unwrap();\n\n // Draw separated line\n line += 1;\n let mut separated_line = String::with_capacity(w as usize);\n for _ in 0..w as usize {\n separated_line.push('-');\n }\n write!(\n stdout,\n \"{}{}{}\",\n cursor::Goto(1, line),\n color::Fg(color::White),\n separated_line,\n )\n .unwrap();\n\n // Draw HEADs\n line += 1;\n write!(\n stdout,\n \"{}{}{} HEADs :\",\n cursor::Goto(1, line),\n color::Fg(color::White),\n heads.len()\n )\n .unwrap();\n line += 1;\n if heads_index \u003e 0 {\n write!(\n stdout,\n \"{}{}/\\\\\",\n cursor::Goto(35, line),\n color::Fg(color::Green),\n )\n .unwrap();\n } else {\n write!(\n stdout,\n \"{}{}/\\\\\",\n cursor::Goto(35, line),\n color::Fg(color::Black),\n )\n .unwrap();\n }\n line += 1;\n write!(\n stdout,\n \"{}{}Step NodeId-Pubkey BlockId-BlockHash Soft:Ver Pre [ Api ] MeR:MiR uid\",\n cursor::Goto(1, line),\n color::Fg(color::White)\n ).unwrap();\n for head in \u0026heads[heads_index..] {\n if line \u003c (h - 2) {\n line += 1;\n if head.step() == 0 {\n write!(\n stdout,\n \"{}{}{}\",\n cursor::Goto(1, line),\n color::Fg(color::Blue),\n head.to_human_string(w as usize),\n )\n .unwrap();\n } else {\n write!(\n stdout,\n \"{}{}{}\",\n cursor::Goto(1, line),\n color::Fg(color::Green),\n head.to_human_string(w as usize),\n )\n .unwrap();\n }\n } else {\n break;\n }\n }\n line += 1;\n if heads_index \u003c heads_index_max {\n write!(\n stdout,\n \"{}{}\\\\/\",\n cursor::Goto(35, line),\n color::Fg(color::Green),\n )\n .unwrap();\n } else {\n write!(\n stdout,\n \"{}{}\\\\/\",\n cursor::Goto(35, line),\n color::Fg(color::Black),\n )\n .unwrap();\n }\n\n // Draw footer\n let mut runtime_in_secs = SystemTime::now()\n .duration_since(start_time)\n .expect(\"Fail to get runtime\")\n .as_secs();\n let runtime_hours = runtime_in_secs / 3600;\n runtime_in_secs -= runtime_hours * 3600;\n let runtime_mins = runtime_in_secs / 60;\n let runtime_secs = runtime_in_secs % 60;\n let runtime_str = format!(\n \"{:02}:{:02}:{:02}\",\n runtime_hours, runtime_mins, runtime_secs\n );\n write!(\n stdout,\n \"{}{}{}runtime : {}\",\n cursor::Goto(1, h),\n color::Bg(color::Blue),\n color::Fg(color::White),\n runtime_str,\n )\n .unwrap();\n write!(\n stdout,\n \"{}{}{}q : quit{}\",\n cursor::Goto(w - 7, h),\n color::Bg(color::Blue),\n color::Fg(color::White),\n cursor::Hide,\n )\n .unwrap();\n\n // Flush stdout (i.e. make the output appear).\n stdout.flush().unwrap();\n }\n /// Restore Terminal\n fn restore_term\u003cW: Write\u003e(stdout: \u0026RawTerminal\u003cW\u003e) {\n let _ = stdout.suspend_raw_mode();\n }\n}\n\nimpl Default for TuiModule {\n fn default() -\u003e TuiModule {\n TuiModule {}\n }\n}\n\nimpl DursModule\u003cDuRsConf, DursMsg\u003e for TuiModule {\n type ModuleUserConf = TuiConf;\n type ModuleConf = TuiConf;\n type ModuleOpt = TuiOpt;\n\n fn name() -\u003e ModuleStaticName {\n ModuleStaticName(\"tui\")\n }\n fn priority() -\u003e ModulePriority {\n ModulePriority::Recommended()\n }\n fn ask_required_keys() -\u003e RequiredKeys {\n RequiredKeys::None()\n }\n fn generate_module_conf(\n _currency_name: Option\u003c\u0026CurrencyName\u003e,\n _global_conf: \u0026\u003cDuRsConf as DursConfTrait\u003e::GlobalConf,\n _module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n ) -\u003e Result\u003c(Self::ModuleConf, Option\u003cSelf::ModuleUserConf\u003e), ModuleConfError\u003e {\n Ok((TuiConf {}, None))\n }\n fn start(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n _conf: Self::ModuleConf,\n router_sender: Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) -\u003e Result\u003c(), failure::Error\u003e {\n let start_time = SystemTime::now(); //: DateTime\u003cUtc\u003e = Utc::now();\n\n // Instanciate Tui module datas\n let mut tui = TuiModuleDatas {\n router_sender: router_sender.clone(),\n heads_cache: HashMap::new(),\n heads_index: 0,\n connections_status: HashMap::new(),\n established_conns_count: 0,\n };\n\n // Create tui main thread channel\n let (tui_sender, tui_receiver): (Sender\u003cTuiMess\u003e, Receiver\u003cTuiMess\u003e) = channel();\n\n // Create proxy channel\n let (proxy_sender, proxy_receiver): (Sender\u003cDursMsg\u003e, Receiver\u003cDursMsg\u003e) = channel();\n\n // Launch a proxy thread that transform DursMsg() to TuiMess::DursMsg(DursMsg())\n let tui_sender_clone = tui_sender.clone();\n thread::spawn(move || {\n // Send proxy sender to main\n router_sender\n .send(RouterThreadMessage::ModuleRegistration {\n static_name: TuiModule::name(),\n sender: proxy_sender,\n roles: vec![ModuleRole::UserInterface],\n events_subscription: vec![\n ModuleEvent::NewValidBlock,\n ModuleEvent::ConnectionsChangeNodeNetwork,\n ModuleEvent::NewValidHeadFromNetwork,\n ModuleEvent::NewValidPeerFromNodeNetwork,\n ],\n reserved_apis_parts: vec![],\n endpoints: vec![],\n })\n .expect(\"Fatal error : tui module fail to send is sender channel !\");\n debug!(\"Send tui sender to main thread.\");\n loop {\n match proxy_receiver.recv() {\n Ok(message) =\u003e {\n let stop = if let DursMsg::Stop = message {\n true\n } else {\n false\n };\n match tui_sender_clone.send(TuiMess::DursMsg(Box::new(message))) {\n Ok(_) =\u003e {\n if stop {\n break;\n };\n }\n Err(_) =\u003e {\n debug!(\"tui proxy : fail to relay DursMsg to tui main thread !\");\n break;\n }\n }\n }\n Err(e) =\u003e {\n warn!(\"Tui: {}\", e);\n break;\n }\n }\n }\n });\n\n // Enter raw mode.\n // Wait a short while before modifying stdout, in case errors should be reported regarding the launch of the other modules.\n thread::sleep(Duration::from_millis(500));\n let mut stdout = MouseTerminal::from(stdout().into_raw_mode().unwrap());\n\n // Initial draw\n let mut last_draw = SystemTime::now();\n tui.draw_term(\n \u0026mut stdout,\n start_time,\n \u0026tui.heads_cache,\n tui.heads_index,\n \u0026tui.connections_status,\n \u0026HashMap::with_capacity(0),\n );\n\n // Launch stdin thread\n let _stdin_thread = thread::spawn(move || {\n // Get the standard input stream.\n let stdin = std::io::stdin();\n // Get stdin events\n for c in stdin.events() {\n tui_sender\n .send(TuiMess::TermionEvent(\n c.expect(\"error to read stdin event !\"),\n ))\n .expect(\"Fatal error : tui stdin thread module fail to send message !\");\n trace!(\"Send stdin event to tui main thread.\");\n }\n });\n\n // ui main loop\n loop {\n let mut user_event = false;\n // Get messages\n match tui_receiver.recv_timeout(Duration::from_millis(250)) {\n Ok(ref message) =\u003e match *message {\n TuiMess::DursMsg(ref durs_message) =\u003e match durs_message.deref() {\n DursMsg::Stop =\u003e {\n let _ = writeln!(\n stdout,\n \"{}{}{}{}{}\",\n color::Fg(color::Reset),\n cursor::Goto(1, 1),\n color::Bg(color::Reset),\n cursor::Show,\n clear::All,\n );\n break;\n }\n DursMsg::Event {\n ref event_content, ..\n } =\u003e match *event_content {\n DursEvent::BlockchainEvent(ref dal_event) =\u003e match *dal_event.deref() {\n BlockchainEvent::StackUpValidBlock(ref _block) =\u003e {}\n BlockchainEvent::RevertBlocks(ref _blocks) =\u003e {}\n _ =\u003e {}\n },\n DursEvent::NetworkEvent(ref network_event_box) =\u003e {\n match *network_event_box.deref() {\n NetworkEvent::ConnectionStateChange(\n ref node_full_id,\n ref status,\n ref uid,\n ref url,\n ) =\u003e {\n if let Some(conn) =\n tui.connections_status.get(\u0026node_full_id)\n {\n if *status == 12 \u0026\u0026 (*conn).status != 12 {\n tui.established_conns_count += 1;\n } else if *status != 12\n \u0026\u0026 (*conn).status == 12\n \u0026\u0026 tui.established_conns_count \u003e 0\n {\n tui.established_conns_count -= 1;\n }\n };\n tui.connections_status.insert(\n *node_full_id,\n Connection {\n status: *status,\n url: url.clone(),\n uid: uid.clone(),\n },\n );\n }\n NetworkEvent::ReceiveHeads(ref heads) =\u003e {\n heads\n .iter()\n .map(|h| {\n tui.heads_cache.insert(h.node_full_id(), h.clone())\n })\n .for_each(drop);\n }\n _ =\u003e {}\n }\n }\n _ =\u003e {}\n },\n _ =\u003e {}\n },\n TuiMess::TermionEvent(ref term_event) =\u003e match *term_event {\n Event::Key(Key::Char('q')) =\u003e {\n // Exit\n let _ = writeln!(\n stdout,\n \"{}{}{}{}{}\",\n color::Fg(color::Reset),\n cursor::Goto(1, 1),\n color::Bg(color::Reset),\n cursor::Show,\n clear::All,\n );\n TuiModuleDatas::restore_term(\u0026stdout);\n if tui\n .router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Stop))\n .is_err()\n {\n warn!(\"Tui: Fail to send STOP message to router !\");\n }\n break;\n }\n Event::Mouse(ref me) =\u003e match *me {\n MouseEvent::Press(ref button, ref _a, ref _b) =\u003e match *button {\n MouseButton::WheelDown =\u003e {\n // Get Terminal size\n let (_w, h) = termion::terminal_size()\n .expect(\"Fail to get terminal size !\");\n // heads_index\n if h \u003e 16 {\n let heads_index_max =\n if tui.heads_cache.len() \u003e (h - 16) as usize {\n tui.heads_cache.len() - (h - 16) as usize\n } else {\n 0\n };\n if tui.heads_index \u003c heads_index_max {\n tui.heads_index += 1;\n user_event = true;\n } else {\n tui.heads_index = heads_index_max;\n }\n }\n }\n MouseButton::WheelUp =\u003e {\n // heads_index\n if tui.heads_index \u003e 0 {\n tui.heads_index -= 1;\n user_event = true;\n }\n }\n _ =\u003e {}\n },\n MouseEvent::Release(ref _a, ref _b)\n | MouseEvent::Hold(ref _a, ref _b) =\u003e {}\n },\n _ =\u003e {}\n },\n },\n Err(e) =\u003e match e {\n RecvTimeoutError::Disconnected =\u003e {\n fatal_error!(\"Disconnected tui module !\");\n }\n RecvTimeoutError::Timeout =\u003e {}\n },\n }\n let now = SystemTime::now();\n if user_event\n || now\n .duration_since(last_draw)\n .expect(\"Tui : Fatal error : fail to get duration since last draw !\")\n .subsec_nanos()\n \u003e 250_000_000\n {\n last_draw = now;\n tui.draw_term(\n \u0026mut stdout,\n start_time,\n \u0026tui.heads_cache,\n tui.heads_index,\n \u0026tui.connections_status,\n \u0026HashMap::with_capacity(0),\n );\n }\n }\n Ok(())\n }\n}\n","traces":[{"line":1,"address":4209499,"length":1,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":231,"address":null,"length":0,"stats":{"Line":0}},{"line":232,"address":null,"length":0,"stats":{"Line":0}},{"line":247,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":249,"address":null,"length":0,"stats":{"Line":0}},{"line":250,"address":null,"length":0,"stats":{"Line":0}},{"line":252,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":0}},{"line":271,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":0}},{"line":273,"address":null,"length":0,"stats":{"Line":0}},{"line":280,"address":null,"length":0,"stats":{"Line":0}},{"line":281,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":null,"length":0,"stats":{"Line":0}},{"line":290,"address":null,"length":0,"stats":{"Line":0}},{"line":296,"address":null,"length":0,"stats":{"Line":0}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":0}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":318,"address":null,"length":0,"stats":{"Line":0}},{"line":319,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":0}},{"line":324,"address":null,"length":0,"stats":{"Line":0}},{"line":331,"address":null,"length":0,"stats":{"Line":0}},{"line":332,"address":null,"length":0,"stats":{"Line":0}},{"line":342,"address":null,"length":0,"stats":{"Line":0}},{"line":343,"address":null,"length":0,"stats":{"Line":0}},{"line":344,"address":null,"length":0,"stats":{"Line":0}},{"line":345,"address":null,"length":0,"stats":{"Line":0}},{"line":346,"address":null,"length":0,"stats":{"Line":0}},{"line":347,"address":null,"length":0,"stats":{"Line":0}},{"line":348,"address":null,"length":0,"stats":{"Line":0}},{"line":349,"address":null,"length":0,"stats":{"Line":0}},{"line":350,"address":null,"length":0,"stats":{"Line":0}},{"line":351,"address":null,"length":0,"stats":{"Line":0}},{"line":352,"address":null,"length":0,"stats":{"Line":0}},{"line":354,"address":null,"length":0,"stats":{"Line":0}},{"line":363,"address":null,"length":0,"stats":{"Line":0}},{"line":374,"address":null,"length":0,"stats":{"Line":0}},{"line":377,"address":null,"length":0,"stats":{"Line":0}},{"line":378,"address":null,"length":0,"stats":{"Line":0}},{"line":383,"address":null,"length":0,"stats":{"Line":0}},{"line":393,"address":null,"length":0,"stats":{"Line":0}},{"line":396,"address":null,"length":0,"stats":{"Line":0}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":402,"address":null,"length":0,"stats":{"Line":0}},{"line":407,"address":null,"length":0,"stats":{"Line":0}},{"line":409,"address":null,"length":0,"stats":{"Line":0}},{"line":415,"address":4285798,"length":1,"stats":{"Line":0}},{"line":418,"address":null,"length":0,"stats":{"Line":0}},{"line":419,"address":null,"length":0,"stats":{"Line":0}},{"line":420,"address":null,"length":0,"stats":{"Line":0}},{"line":421,"address":null,"length":0,"stats":{"Line":0}},{"line":422,"address":null,"length":0,"stats":{"Line":0}},{"line":423,"address":null,"length":0,"stats":{"Line":0}},{"line":427,"address":null,"length":0,"stats":{"Line":0}},{"line":430,"address":null,"length":0,"stats":{"Line":0}},{"line":433,"address":null,"length":0,"stats":{"Line":0}},{"line":434,"address":null,"length":0,"stats":{"Line":0}},{"line":436,"address":null,"length":0,"stats":{"Line":0}},{"line":437,"address":null,"length":0,"stats":{"Line":0}},{"line":438,"address":null,"length":0,"stats":{"Line":0}},{"line":439,"address":null,"length":0,"stats":{"Line":0}},{"line":440,"address":null,"length":0,"stats":{"Line":0}},{"line":441,"address":null,"length":0,"stats":{"Line":0}},{"line":442,"address":null,"length":0,"stats":{"Line":0}},{"line":443,"address":null,"length":0,"stats":{"Line":0}},{"line":444,"address":null,"length":0,"stats":{"Line":0}},{"line":445,"address":null,"length":0,"stats":{"Line":0}},{"line":447,"address":null,"length":0,"stats":{"Line":0}},{"line":448,"address":null,"length":0,"stats":{"Line":0}},{"line":450,"address":null,"length":0,"stats":{"Line":0}},{"line":451,"address":null,"length":0,"stats":{"Line":0}},{"line":452,"address":null,"length":0,"stats":{"Line":0}},{"line":453,"address":null,"length":0,"stats":{"Line":0}},{"line":454,"address":null,"length":0,"stats":{"Line":0}},{"line":455,"address":null,"length":0,"stats":{"Line":0}},{"line":456,"address":null,"length":0,"stats":{"Line":0}},{"line":457,"address":null,"length":0,"stats":{"Line":0}},{"line":458,"address":null,"length":0,"stats":{"Line":0}},{"line":460,"address":null,"length":0,"stats":{"Line":0}},{"line":461,"address":null,"length":0,"stats":{"Line":0}},{"line":462,"address":null,"length":0,"stats":{"Line":0}},{"line":463,"address":null,"length":0,"stats":{"Line":0}},{"line":466,"address":null,"length":0,"stats":{"Line":0}},{"line":467,"address":null,"length":0,"stats":{"Line":0}},{"line":468,"address":null,"length":0,"stats":{"Line":0}},{"line":472,"address":null,"length":0,"stats":{"Line":0}},{"line":473,"address":null,"length":0,"stats":{"Line":0}},{"line":474,"address":null,"length":0,"stats":{"Line":0}},{"line":482,"address":null,"length":0,"stats":{"Line":0}},{"line":483,"address":null,"length":0,"stats":{"Line":0}},{"line":486,"address":null,"length":0,"stats":{"Line":0}},{"line":487,"address":null,"length":0,"stats":{"Line":0}},{"line":488,"address":null,"length":0,"stats":{"Line":0}},{"line":489,"address":null,"length":0,"stats":{"Line":0}},{"line":490,"address":null,"length":0,"stats":{"Line":0}},{"line":491,"address":null,"length":0,"stats":{"Line":0}},{"line":492,"address":null,"length":0,"stats":{"Line":0}},{"line":493,"address":null,"length":0,"stats":{"Line":0}},{"line":497,"address":null,"length":0,"stats":{"Line":0}},{"line":499,"address":null,"length":0,"stats":{"Line":0}},{"line":501,"address":null,"length":0,"stats":{"Line":0}},{"line":502,"address":null,"length":0,"stats":{"Line":0}},{"line":503,"address":null,"length":0,"stats":{"Line":0}},{"line":504,"address":null,"length":0,"stats":{"Line":0}},{"line":506,"address":null,"length":0,"stats":{"Line":0}},{"line":507,"address":null,"length":0,"stats":{"Line":0}},{"line":512,"address":null,"length":0,"stats":{"Line":0}},{"line":513,"address":null,"length":0,"stats":{"Line":0}},{"line":515,"address":null,"length":0,"stats":{"Line":0}},{"line":516,"address":null,"length":0,"stats":{"Line":0}},{"line":517,"address":null,"length":0,"stats":{"Line":0}},{"line":518,"address":null,"length":0,"stats":{"Line":0}},{"line":519,"address":null,"length":0,"stats":{"Line":0}},{"line":520,"address":null,"length":0,"stats":{"Line":0}},{"line":521,"address":null,"length":0,"stats":{"Line":0}},{"line":522,"address":null,"length":0,"stats":{"Line":0}},{"line":523,"address":null,"length":0,"stats":{"Line":0}},{"line":524,"address":null,"length":0,"stats":{"Line":0}},{"line":525,"address":null,"length":0,"stats":{"Line":0}},{"line":526,"address":null,"length":0,"stats":{"Line":0}},{"line":528,"address":null,"length":0,"stats":{"Line":0}},{"line":530,"address":null,"length":0,"stats":{"Line":0}},{"line":531,"address":null,"length":0,"stats":{"Line":0}},{"line":532,"address":null,"length":0,"stats":{"Line":0}},{"line":533,"address":null,"length":0,"stats":{"Line":0}},{"line":534,"address":null,"length":0,"stats":{"Line":0}},{"line":535,"address":null,"length":0,"stats":{"Line":0}},{"line":536,"address":null,"length":0,"stats":{"Line":0}},{"line":538,"address":null,"length":0,"stats":{"Line":0}},{"line":539,"address":null,"length":0,"stats":{"Line":0}},{"line":540,"address":null,"length":0,"stats":{"Line":0}},{"line":541,"address":null,"length":0,"stats":{"Line":0}},{"line":542,"address":null,"length":0,"stats":{"Line":0}},{"line":543,"address":null,"length":0,"stats":{"Line":0}},{"line":544,"address":null,"length":0,"stats":{"Line":0}},{"line":545,"address":null,"length":0,"stats":{"Line":0}},{"line":546,"address":null,"length":0,"stats":{"Line":0}},{"line":547,"address":null,"length":0,"stats":{"Line":0}},{"line":549,"address":null,"length":0,"stats":{"Line":0}},{"line":550,"address":null,"length":0,"stats":{"Line":0}},{"line":551,"address":null,"length":0,"stats":{"Line":0}},{"line":552,"address":null,"length":0,"stats":{"Line":0}},{"line":553,"address":null,"length":0,"stats":{"Line":0}},{"line":555,"address":null,"length":0,"stats":{"Line":0}},{"line":558,"address":null,"length":0,"stats":{"Line":0}},{"line":559,"address":null,"length":0,"stats":{"Line":0}},{"line":560,"address":null,"length":0,"stats":{"Line":0}},{"line":561,"address":null,"length":0,"stats":{"Line":0}},{"line":562,"address":null,"length":0,"stats":{"Line":0}},{"line":563,"address":null,"length":0,"stats":{"Line":0}},{"line":567,"address":null,"length":0,"stats":{"Line":0}},{"line":568,"address":null,"length":0,"stats":{"Line":0}},{"line":573,"address":null,"length":0,"stats":{"Line":0}},{"line":575,"address":null,"length":0,"stats":{"Line":0}},{"line":578,"address":null,"length":0,"stats":{"Line":0}},{"line":580,"address":null,"length":0,"stats":{"Line":0}},{"line":582,"address":null,"length":0,"stats":{"Line":0}},{"line":583,"address":null,"length":0,"stats":{"Line":0}},{"line":585,"address":null,"length":0,"stats":{"Line":0}},{"line":586,"address":null,"length":0,"stats":{"Line":0}},{"line":587,"address":null,"length":0,"stats":{"Line":0}},{"line":588,"address":null,"length":0,"stats":{"Line":0}},{"line":589,"address":null,"length":0,"stats":{"Line":0}},{"line":590,"address":null,"length":0,"stats":{"Line":0}},{"line":591,"address":null,"length":0,"stats":{"Line":0}},{"line":592,"address":null,"length":0,"stats":{"Line":0}},{"line":594,"address":null,"length":0,"stats":{"Line":0}},{"line":595,"address":null,"length":0,"stats":{"Line":0}},{"line":600,"address":null,"length":0,"stats":{"Line":0}},{"line":602,"address":null,"length":0,"stats":{"Line":0}},{"line":604,"address":null,"length":0,"stats":{"Line":0}},{"line":605,"address":null,"length":0,"stats":{"Line":0}},{"line":606,"address":null,"length":0,"stats":{"Line":0}},{"line":608,"address":null,"length":0,"stats":{"Line":0}},{"line":609,"address":null,"length":0,"stats":{"Line":0}},{"line":611,"address":null,"length":0,"stats":{"Line":0}},{"line":612,"address":null,"length":0,"stats":{"Line":0}},{"line":613,"address":null,"length":0,"stats":{"Line":0}},{"line":614,"address":null,"length":0,"stats":{"Line":0}},{"line":615,"address":null,"length":0,"stats":{"Line":0}},{"line":616,"address":null,"length":0,"stats":{"Line":0}},{"line":618,"address":null,"length":0,"stats":{"Line":0}},{"line":619,"address":null,"length":0,"stats":{"Line":0}},{"line":620,"address":null,"length":0,"stats":{"Line":0}},{"line":621,"address":null,"length":0,"stats":{"Line":0}},{"line":622,"address":null,"length":0,"stats":{"Line":0}},{"line":626,"address":null,"length":0,"stats":{"Line":0}},{"line":628,"address":null,"length":0,"stats":{"Line":0}},{"line":629,"address":null,"length":0,"stats":{"Line":0}},{"line":630,"address":null,"length":0,"stats":{"Line":0}},{"line":633,"address":null,"length":0,"stats":{"Line":0}},{"line":635,"address":null,"length":0,"stats":{"Line":0}},{"line":636,"address":null,"length":0,"stats":{"Line":0}},{"line":638,"address":null,"length":0,"stats":{"Line":0}},{"line":641,"address":null,"length":0,"stats":{"Line":0}},{"line":642,"address":null,"length":0,"stats":{"Line":0}},{"line":643,"address":null,"length":0,"stats":{"Line":0}},{"line":645,"address":null,"length":0,"stats":{"Line":0}},{"line":648,"address":null,"length":0,"stats":{"Line":0}},{"line":649,"address":null,"length":0,"stats":{"Line":0}},{"line":650,"address":null,"length":0,"stats":{"Line":0}},{"line":651,"address":null,"length":0,"stats":{"Line":0}},{"line":652,"address":null,"length":0,"stats":{"Line":0}},{"line":653,"address":null,"length":0,"stats":{"Line":0}},{"line":654,"address":null,"length":0,"stats":{"Line":0}},{"line":656,"address":null,"length":0,"stats":{"Line":0}},{"line":657,"address":null,"length":0,"stats":{"Line":0}},{"line":658,"address":null,"length":0,"stats":{"Line":0}},{"line":659,"address":null,"length":0,"stats":{"Line":0}},{"line":660,"address":null,"length":0,"stats":{"Line":0}},{"line":661,"address":null,"length":0,"stats":{"Line":0}},{"line":662,"address":null,"length":0,"stats":{"Line":0}},{"line":663,"address":null,"length":0,"stats":{"Line":0}},{"line":667,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":268},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","controllers","handler.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P connection handler.\n\nuse crate::constants;\nuse crate::controllers::WsSender;\nuse durs_common_tools::fatal_error;\nuse durs_message::DursMsg;\nuse durs_ws2p_protocol::connection_state::WS2PConnectionState;\nuse durs_ws2p_protocol::controller::{WS2PController, WebsocketActionOrder};\nuse durs_ws2p_protocol::websocket::{WebsocketAction, WebsocketIncomingEvent, WebsocketMessage};\nuse std::net::SocketAddr;\n\nuse ws::{util::Token, CloseCode, Handler, Handshake, Message};\n\nconst RECV_SERVICE: Token = Token(3);\n\n/// Our Handler struct.\n/// Here we explicity indicate that the Ws2pConnectionHandler needs a Sender,\n/// whereas a closure captures the Sender for us automatically.\n#[derive(Debug)]\npub struct Ws2pConnectionHandler {\n /// Websocket sender\n pub ws: WsSender,\n /// Remote addr\n pub remote_addr_opt: Option\u003cSocketAddr\u003e,\n /// WS2P Controller\n pub controller: WS2PController\u003cDursMsg\u003e,\n}\n\nimpl Ws2pConnectionHandler {\n fn exec_ws_action(\u0026mut self, ws_action_order: WebsocketActionOrder) -\u003e ws::Result\u003c()\u003e {\n match ws_action_order.ws_action {\n WebsocketAction::ConnectTo { .. } =\u003e {\n fatal_error!(\"Could not generate a new connection in the context of a controller.\")\n }\n WebsocketAction::SendMessage { msg } =\u003e {\n let ws_msg = match msg {\n WebsocketMessage::Bin(bin_msg) =\u003e Message::binary(bin_msg),\n WebsocketMessage::Str(str_msg) =\u003e Message::text(str_msg),\n };\n match self.ws.0.send(ws_msg) {\n Ok(()) =\u003e {\n // Update state\n if let Some(new_state) = ws_action_order.new_state_if_success {\n if let Err(e) = self.controller.update_conn_state(new_state) {\n self.ws\n .0\n .close_with_reason(CloseCode::Error, format!(\"{}\", e))?;\n }\n }\n // Log\n debug!(\n \"Succesfully send message to '{}'\",\n if let Some(remote_addr) = self.remote_addr_opt {\n remote_addr.to_string()\n } else {\n \"unknown addr\".to_string()\n }\n );\n Ok(())\n }\n Err(e) =\u003e {\n let _ = self\n .controller\n .update_conn_state(ws_action_order.new_state_if_fail);\n warn!(\n \"Fail to send message to '{}' : {}\",\n if let Some(remote_addr) = self.remote_addr_opt {\n remote_addr.to_string()\n } else {\n \"unknown addr\".to_string()\n },\n e\n );\n self.ws\n .0\n .close_with_reason(CloseCode::Error, \"Fail to send message !\")\n }\n }\n }\n WebsocketAction::CloseConnection { reason } =\u003e match self.ws.0.close_with_reason(\n CloseCode::Error,\n reason.unwrap_or_else(|| String::from(\"unknown reason\")),\n ) {\n Ok(()) =\u003e {\n if let Some(new_state) = ws_action_order.new_state_if_success {\n let _ = self.controller.update_conn_state(new_state);\n }\n Ok(())\n }\n Err(e) =\u003e {\n let _ = self\n .controller\n .update_conn_state(ws_action_order.new_state_if_fail);\n Err(e)\n }\n },\n }\n }\n}\n\n// We implement the Handler trait for Ws2pConnectionHandler so that we can get more\n// fine-grained control of the connection.\nimpl Handler for Ws2pConnectionHandler {\n // `on_open` will be called only after the WebSocket handshake is successful\n // so at this point we know that the connection is ready to send/receive messages.\n // We ignore the `Handshake` for now, but you could also use this method to setup\n // Handler state or reject the connection based on the details of the Request\n // or Response, such as by checking cookies or Auth headers.\n fn on_open(\u0026mut self, handshake: Handshake) -\u003e ws::Result\u003c()\u003e {\n self.remote_addr_opt = handshake.peer_addr;\n match self.controller.process(WebsocketIncomingEvent::OnOpen {\n remote_addr: self.remote_addr_opt,\n }) {\n Ok(ws_action_order_opt) =\u003e {\n // Start RECV_SERVICE timeout\n self.ws\n .0\n .timeout(*constants::WS2P_RECV_SERVICE_FREQ_IN_MS, RECV_SERVICE)?;\n // Execute websocket action order\n if let Some(ws_action_order) = ws_action_order_opt {\n self.exec_ws_action(ws_action_order)\n } else {\n Ok(())\n }\n }\n Err(e) =\u003e self.exec_ws_action(WebsocketActionOrder {\n ws_action: WebsocketAction::CloseConnection {\n reason: Some(format!(\"{}\", e)),\n },\n new_state_if_success: Some(WS2PConnectionState::Close),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n }),\n }\n }\n\n // `on_message` is roughly equivalent to the Handler closure. It takes a `Message`\n // and returns a `Result\u003c()\u003e`.\n fn on_message(\u0026mut self, msg: Message) -\u003e ws::Result\u003c()\u003e {\n let msg = match msg {\n Message::Binary(bin_msg) =\u003e WebsocketMessage::Bin(bin_msg),\n Message::Text(str_msg) =\u003e WebsocketMessage::Str(str_msg),\n };\n match self\n .controller\n .process(WebsocketIncomingEvent::OnMessage { msg })\n {\n Ok(ws_action_order_opt) =\u003e {\n if let Some(ws_action_order) = ws_action_order_opt {\n self.exec_ws_action(ws_action_order)\n } else {\n Ok(())\n }\n }\n Err(e) =\u003e self.exec_ws_action(WebsocketActionOrder {\n ws_action: WebsocketAction::CloseConnection {\n reason: Some(format!(\"{}\", e)),\n },\n new_state_if_success: Some(WS2PConnectionState::Close),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n }),\n }\n }\n fn on_timeout(\u0026mut self, _event: Token) -\u003e ws::Result\u003c()\u003e {\n self.ws.0.timeout(1_000, RECV_SERVICE)?;\n if let Some(ws_action_order) = self.controller.check_timeouts() {\n self.exec_ws_action(ws_action_order)\n } else {\n Ok(())\n }\n }\n /*fn on_frame(\u0026mut self, frame: Frame) -\u003e ws::Result\u003cOption\u003cFrame\u003e\u003e {\n Ok(Some(frame))\n }*/\n fn on_close(\u0026mut self, _code: CloseCode, _reason: \u0026str) {\n // The WebSocket protocol allows for a utf8 reason for the closing state after the\n // close code. WS-RS will attempt to interpret this data as a utf8 description of the\n // reason for closing the connection. I many cases, `reason` will be an empty string.\n // So, you may not normally want to display `reason` to the user,\n // but let's assume that we know that `reason` is human-readable.\n /*match code {\n CloseCode::Normal =\u003e info!(\"The remote server close the connection.\"),\n CloseCode::Away =\u003e info!(\"The remote server is leaving.\"),\n _ =\u003e warn!(\"The remote server encountered an error: {}\", reason),\n }\n let _result = self\n .conductor_sender\n .send(WS2PThreadSignal::WS2PConnectionMessage(\n WS2PConnectionMessage(\n self.conn_datas.node_full_id(),\n WS2PConnectionMessagePayload::Close,\n ),\n ));*/\n }\n}\n","traces":[{"line":45,"address":null,"length":0,"stats":{"Line":2}},{"line":46,"address":null,"length":0,"stats":{"Line":2}},{"line":47,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":2}},{"line":51,"address":null,"length":0,"stats":{"Line":2}},{"line":52,"address":null,"length":0,"stats":{"Line":1}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":2}},{"line":56,"address":null,"length":0,"stats":{"Line":2}},{"line":58,"address":null,"length":0,"stats":{"Line":2}},{"line":59,"address":null,"length":0,"stats":{"Line":2}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":2}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":2}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":1}},{"line":126,"address":null,"length":0,"stats":{"Line":1}},{"line":127,"address":null,"length":0,"stats":{"Line":1}},{"line":129,"address":null,"length":0,"stats":{"Line":2}},{"line":131,"address":null,"length":0,"stats":{"Line":1}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":2}},{"line":135,"address":null,"length":0,"stats":{"Line":2}},{"line":136,"address":null,"length":0,"stats":{"Line":1}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":2}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":1}}],"covered":38,"coverable":86},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","controllers","incoming_connections.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P incoming connections controllers.\n\nuse crate::controllers::handler::Ws2pConnectionHandler;\nuse crate::controllers::*;\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_message::DursMsg;\nuse durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse durs_ws2p_protocol::controller::meta_datas::WS2PControllerMetaDatas;\nuse durs_ws2p_protocol::controller::{WS2PController, WS2PControllerId};\nuse durs_ws2p_protocol::orchestrator::OrchestratorMsg;\nuse durs_ws2p_protocol::MySelfWs2pNode;\nuse std::fmt::Debug;\nuse std::net::ToSocketAddrs;\nuse std::sync::mpsc;\nuse ws::deflate::DeflateBuilder;\nuse ws::listen;\n\n/// Listen on WSPv2 host:port\npub fn listen_on_ws2p_v2_endpoint\u003cA: ToSocketAddrs + Debug\u003e(\n currency: \u0026CurrencyName,\n orchestrator_sender: \u0026mpsc::Sender\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n self_node: \u0026MySelfWs2pNode,\n addr: A,\n) -\u003e ws::Result\u003c()\u003e {\n // Connect to websocket\n listen(addr, move |ws| {\n match WS2PController::\u003cDursMsg\u003e::try_new(\n WS2PControllerId::Incoming,\n WS2PControllerMetaDatas::new(\n Hash::random(),\n WS2Pv2ConnectType::Incoming,\n currency.clone(),\n self_node.clone(),\n ),\n orchestrator_sender.clone(),\n ) {\n Ok(controller) =\u003e DeflateBuilder::new().build(Ws2pConnectionHandler {\n ws: WsSender(ws),\n remote_addr_opt: None,\n controller,\n }),\n Err(_e) =\u003e fatal_error!(\"WS2P Orchestrator unreachable\"),\n }\n })\n}\n","traces":[{"line":35,"address":4226560,"length":1,"stats":{"Line":1}},{"line":42,"address":4226582,"length":1,"stats":{"Line":2}},{"line":43,"address":4226696,"length":1,"stats":{"Line":1}},{"line":44,"address":4226752,"length":1,"stats":{"Line":1}},{"line":45,"address":4226896,"length":1,"stats":{"Line":1}},{"line":46,"address":4226763,"length":1,"stats":{"Line":1}},{"line":47,"address":4226824,"length":1,"stats":{"Line":1}},{"line":48,"address":4226835,"length":1,"stats":{"Line":1}},{"line":49,"address":4226869,"length":1,"stats":{"Line":1}},{"line":51,"address":4226971,"length":1,"stats":{"Line":1}},{"line":53,"address":4227112,"length":1,"stats":{"Line":1}},{"line":54,"address":4227258,"length":1,"stats":{"Line":1}},{"line":55,"address":4227329,"length":1,"stats":{"Line":1}},{"line":56,"address":4227340,"length":1,"stats":{"Line":1}},{"line":58,"address":4227593,"length":1,"stats":{"Line":0}}],"covered":14,"coverable":15},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","controllers","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P connections controllers.\n\n//use constants::*;\nuse dubp_documents::Blockstamp;\nuse dup_crypto::hashs::Hash;\nuse ws::Sender;\n//use dup_crypto::keys::*;\nuse durs_network_documents::network_peer::PeerCardV11;\nuse durs_ws2p_messages::*;\n//use std::sync::mpsc;\n\npub mod handler;\npub mod incoming_connections;\npub mod outgoing_connections;\n\n/// Order transmitted to the controller\n#[derive(Debug, Clone)]\npub enum Ws2pControllerOrder {\n /// Give a message to be transmitted\n SendMsg(Box\u003cWS2PMessage\u003e),\n /// Close the connection\n Close,\n}\n\n/// Store a websocket sender\npub struct WsSender(pub Sender);\n\nimpl ::std::fmt::Debug for WsSender {\n fn fmt(\u0026self, f: \u0026mut ::std::fmt::Formatter) -\u003e ::std::fmt::Result {\n write!(f, \"WsSender {{ }}\")\n }\n}\n\n#[derive(Debug, Clone)]\n/// WS2P remote node datas\npub struct Ws2pRemoteNodeDatas {\n /// Remote challenge\n pub challenge: Hash,\n /// Remote peer card\n pub peer_card: Option\u003cPeerCardV11\u003e,\n /// Remote current blockstamp\n pub current_blockstamp: Option\u003cBlockstamp\u003e,\n}\n\n#[derive(Debug, Copy, Clone)]\n/// WS2P remote node request\npub enum Ws2pRemoteNodeReq {\n /// Sync\n Sync,\n /// Ask chunk\n AskChunk(Blockstamp),\n}\n","traces":[{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","controllers","outgoing_connections.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P outgoing connections controllers.\n\nuse crate::controllers::handler::Ws2pConnectionHandler;\nuse crate::controllers::*;\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_message::DursMsg;\nuse durs_network_documents::network_endpoint::EndpointEnum;\nuse durs_network_documents::NodeFullId;\nuse durs_ws2p_protocol::controller::meta_datas::WS2PControllerMetaDatas;\nuse durs_ws2p_protocol::controller::{WS2PController, WS2PControllerId};\nuse durs_ws2p_protocol::orchestrator::OrchestratorMsg;\nuse durs_ws2p_protocol::MySelfWs2pNode;\nuse ws::connect;\nuse ws::deflate::DeflateBuilder;\n//use durs_network::*;\nuse durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse std::sync::mpsc;\n\n/// Connect to WSPv2 Endpoint\npub fn connect_to_ws2p_v2_endpoint(\n currency: \u0026CurrencyName,\n orchestrator_sender: \u0026mpsc::Sender\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n self_node: \u0026MySelfWs2pNode,\n expected_remote_full_id: Option\u003cNodeFullId\u003e,\n endpoint: \u0026EndpointEnum,\n) -\u003e ws::Result\u003c()\u003e {\n // Get endpoint url\n let ws_url = endpoint.get_url(true, false).expect(\"Endpoint unreachable\");\n\n // Log\n info!(\"Try connection to {} ...\", ws_url);\n\n // Connect to websocket\n connect(ws_url, move |ws| {\n match WS2PController::\u003cDursMsg\u003e::try_new(\n WS2PControllerId::Outgoing {\n expected_remote_full_id,\n },\n WS2PControllerMetaDatas::new(\n Hash::random(),\n WS2Pv2ConnectType::OutgoingServer,\n currency.clone(),\n self_node.clone(),\n ),\n orchestrator_sender.clone(),\n ) {\n Ok(controller) =\u003e DeflateBuilder::new().build(Ws2pConnectionHandler {\n ws: WsSender(ws),\n remote_addr_opt: None,\n controller,\n }),\n Err(_e) =\u003e fatal_error!(\"WS2P Service unreachable\"),\n }\n })\n}\n","traces":[{"line":36,"address":4575936,"length":1,"stats":{"Line":1}},{"line":44,"address":4575969,"length":1,"stats":{"Line":1}},{"line":47,"address":4576081,"length":1,"stats":{"Line":1}},{"line":50,"address":4573424,"length":1,"stats":{"Line":2}},{"line":51,"address":4573448,"length":1,"stats":{"Line":1}},{"line":52,"address":4573548,"length":1,"stats":{"Line":1}},{"line":53,"address":4573504,"length":1,"stats":{"Line":1}},{"line":55,"address":4573740,"length":1,"stats":{"Line":1}},{"line":56,"address":4573607,"length":1,"stats":{"Line":1}},{"line":57,"address":4573668,"length":1,"stats":{"Line":1}},{"line":58,"address":4573679,"length":1,"stats":{"Line":1}},{"line":59,"address":4573713,"length":1,"stats":{"Line":1}},{"line":61,"address":4573815,"length":1,"stats":{"Line":1}},{"line":63,"address":4573948,"length":1,"stats":{"Line":1}},{"line":64,"address":4574094,"length":1,"stats":{"Line":1}},{"line":65,"address":4574165,"length":1,"stats":{"Line":1}},{"line":66,"address":4574176,"length":1,"stats":{"Line":1}},{"line":68,"address":4574425,"length":1,"stats":{"Line":0}}],"covered":17,"coverable":18},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","generate_peer.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Generate self peer card\n\nuse bincode;\nuse dubp_documents::BlockNumber;\nuse dup_crypto::keys::text_signable::TextSignable;\nuse dup_crypto::keys::{KeyPair, KeyPairEnum, SignError};\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_network_documents::network_endpoint::*;\nuse durs_network_documents::network_peer::*;\nuse durs_network_documents::*;\n\npub fn _self_peer_update_endpoints(\n self_peer: PeerCardV11,\n issuer_keys: KeyPairEnum,\n created_on: BlockNumber,\n new_endpoints: Vec\u003cEndpointEnum\u003e,\n) -\u003e Result\u003cPeerCardV11, SignError\u003e {\n let max_eps = self_peer.endpoints.len() + self_peer.endpoints_str.len() + new_endpoints.len();\n let apis: Vec\u003cApiName\u003e = new_endpoints\n .iter()\n .filter(|ep| {\n if let EndpointEnum::V2(_) = ep {\n true\n } else {\n false\n }\n })\n .map(EndpointEnum::api)\n .collect();\n let mut new_endpoints_bin = Vec::with_capacity(max_eps);\n let mut new_endpoints_str = Vec::with_capacity(max_eps);\n for ep in self_peer.endpoints {\n if !apis.contains(\u0026ep.api) {\n new_endpoints_bin.push(ep);\n }\n }\n for ep in self_peer.endpoints_str {\n let ep_clone = ep.clone();\n let ep_fields: Vec\u003c\u0026str\u003e = ep_clone.split(' ').collect();\n if !apis.contains(\u0026ApiName(ep_fields[0].to_owned())) {\n new_endpoints_str.push(ep);\n }\n }\n for ep in new_endpoints {\n if let EndpointEnum::V2(ep_v2) = ep {\n let bin_len = bincode::serialize(\u0026ep_v2)\n .unwrap_or_else(|_| {\n fatal_error!(\n \"Fail to update self peer : invalid endpoint : {:?} !\",\n ep_v2\n )\n })\n .len();\n let str_ep = ep_v2.to_string();\n if str_ep.len() \u003c bin_len {\n new_endpoints_str.push(str_ep);\n } else {\n new_endpoints_bin.push(ep_v2);\n }\n }\n }\n\n let mut new_self_peer = PeerCardV11 {\n created_on,\n endpoints: new_endpoints_bin,\n endpoints_str: new_endpoints_str,\n sig: None,\n ..self_peer\n };\n\n new_self_peer.sign(issuer_keys.private_key())?;\n\n Ok(new_self_peer)\n}\n\npub fn _generate_self_peer(\n currency_name: CurrencyName,\n issuer_keys: KeyPairEnum,\n node_id: NodeId,\n created_on: BlockNumber,\n endpoints: Vec\u003cEndpointEnum\u003e,\n) -\u003e Result\u003cPeerCardV11, SignError\u003e {\n let mut endpoints_bin = Vec::with_capacity(endpoints.len());\n let mut endpoints_str = Vec::with_capacity(endpoints.len());\n\n for ep in endpoints {\n if let EndpointEnum::V2(ep_v2) = ep {\n let bin_len = bincode::serialize(\u0026ep_v2)\n .unwrap_or_else(|_| {\n fatal_error!(\n \"Fail to generate self peer : invalid endpoint : {:?} !\",\n ep_v2\n )\n })\n .len();\n let str_ep = ep_v2.to_string();\n if str_ep.len() \u003c bin_len {\n endpoints_str.push(str_ep);\n } else {\n endpoints_bin.push(ep_v2);\n }\n }\n }\n\n let mut self_peer = PeerCardV11 {\n currency_name,\n issuer: issuer_keys.public_key(),\n node_id,\n created_on,\n endpoints: endpoints_bin,\n endpoints_str,\n sig: None,\n };\n\n self_peer.sign(issuer_keys.private_key())?;\n\n Ok(self_peer)\n}\n","traces":[{"line":28,"address":4941520,"length":1,"stats":{"Line":0}},{"line":34,"address":4941543,"length":1,"stats":{"Line":0}},{"line":35,"address":4941947,"length":1,"stats":{"Line":0}},{"line":37,"address":4210768,"length":1,"stats":{"Line":0}},{"line":38,"address":4210781,"length":1,"stats":{"Line":0}},{"line":39,"address":4210795,"length":1,"stats":{"Line":0}},{"line":41,"address":4210802,"length":1,"stats":{"Line":0}},{"line":46,"address":4942134,"length":1,"stats":{"Line":0}},{"line":47,"address":4942169,"length":1,"stats":{"Line":0}},{"line":48,"address":4942222,"length":1,"stats":{"Line":0}},{"line":49,"address":4942692,"length":1,"stats":{"Line":0}},{"line":50,"address":4942769,"length":1,"stats":{"Line":0}},{"line":53,"address":4942890,"length":1,"stats":{"Line":0}},{"line":54,"address":4946174,"length":1,"stats":{"Line":0}},{"line":55,"address":4943367,"length":1,"stats":{"Line":0}},{"line":56,"address":4943483,"length":1,"stats":{"Line":0}},{"line":57,"address":4943747,"length":1,"stats":{"Line":0}},{"line":60,"address":4943881,"length":1,"stats":{"Line":0}},{"line":61,"address":4944342,"length":1,"stats":{"Line":0}},{"line":62,"address":4944414,"length":1,"stats":{"Line":0}},{"line":63,"address":4210832,"length":1,"stats":{"Line":0}},{"line":64,"address":4210855,"length":1,"stats":{"Line":0}},{"line":66,"address":4211114,"length":1,"stats":{"Line":0}},{"line":69,"address":4944512,"length":1,"stats":{"Line":0}},{"line":70,"address":4944558,"length":1,"stats":{"Line":0}},{"line":71,"address":4944601,"length":1,"stats":{"Line":0}},{"line":72,"address":4944628,"length":1,"stats":{"Line":0}},{"line":74,"address":4944700,"length":1,"stats":{"Line":0}},{"line":79,"address":4944916,"length":1,"stats":{"Line":0}},{"line":80,"address":4944821,"length":1,"stats":{"Line":0}},{"line":81,"address":4944828,"length":1,"stats":{"Line":0}},{"line":82,"address":4944868,"length":1,"stats":{"Line":0}},{"line":83,"address":4944908,"length":1,"stats":{"Line":0}},{"line":87,"address":4945152,"length":1,"stats":{"Line":0}},{"line":89,"address":4946865,"length":1,"stats":{"Line":0}},{"line":92,"address":4947472,"length":1,"stats":{"Line":0}},{"line":99,"address":4947497,"length":1,"stats":{"Line":0}},{"line":100,"address":4947722,"length":1,"stats":{"Line":0}},{"line":102,"address":4947784,"length":1,"stats":{"Line":0}},{"line":103,"address":4948219,"length":1,"stats":{"Line":0}},{"line":104,"address":4948291,"length":1,"stats":{"Line":0}},{"line":105,"address":4212368,"length":1,"stats":{"Line":0}},{"line":106,"address":4212391,"length":1,"stats":{"Line":0}},{"line":108,"address":4212650,"length":1,"stats":{"Line":0}},{"line":111,"address":4948389,"length":1,"stats":{"Line":0}},{"line":112,"address":4948435,"length":1,"stats":{"Line":0}},{"line":113,"address":4948478,"length":1,"stats":{"Line":0}},{"line":114,"address":4948505,"length":1,"stats":{"Line":0}},{"line":116,"address":4948577,"length":1,"stats":{"Line":0}},{"line":121,"address":4948866,"length":1,"stats":{"Line":0}},{"line":122,"address":4948698,"length":1,"stats":{"Line":0}},{"line":123,"address":4948737,"length":1,"stats":{"Line":0}},{"line":124,"address":4948764,"length":1,"stats":{"Line":0}},{"line":125,"address":4948771,"length":1,"stats":{"Line":0}},{"line":126,"address":4948778,"length":1,"stats":{"Line":0}},{"line":127,"address":4948818,"length":1,"stats":{"Line":0}},{"line":128,"address":4948858,"length":1,"stats":{"Line":0}},{"line":131,"address":4949104,"length":1,"stats":{"Line":0}},{"line":133,"address":4950305,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":59},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WebSocketToPeer API for the Duniter project.\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde_derive;\n#[macro_use]\nextern crate structopt;\n\nmod constants;\npub mod controllers;\nmod errors;\nmod generate_peer;\npub mod services;\n\nuse crate::errors::WS2PError;\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_common_tools::traits::merge::Merge;\nuse durs_conf::DuRsConf;\nuse durs_message::DursMsg;\nuse durs_module::*;\nuse durs_network::cli::sync::SyncOpt;\nuse durs_network::*;\nuse durs_network_documents::network_endpoint::*;\nuse maplit::hashset;\nuse std::sync::mpsc;\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\n/// WS2P Configuration\npub struct WS2PConf {\n /// Limit of outcoming connections\n pub outcoming_quota: usize,\n /// Default WS2P endpoints provides by configuration file\n pub sync_endpoints: Vec\u003cEndpointEnum\u003e,\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// WS2P Configuration\npub struct WS2PUserConf {\n /// Limit of outcoming connections\n pub outcoming_quota: Option\u003cusize\u003e,\n /// Default WS2P endpoints provides by configuration file\n pub sync_endpoints: Option\u003cVec\u003cEndpointEnum\u003e\u003e,\n}\n\nimpl Merge for WS2PUserConf {\n fn merge(self, other: Self) -\u003e Self {\n WS2PUserConf {\n outcoming_quota: self.outcoming_quota.or(other.outcoming_quota),\n sync_endpoints: self.sync_endpoints.or(other.sync_endpoints),\n }\n }\n}\n\nimpl Default for WS2PConf {\n fn default() -\u003e Self {\n WS2PConf {\n outcoming_quota: *constants::WS2P_DEFAULT_OUTCOMING_QUOTA,\n sync_endpoints: vec![\n EndpointV2::parse_from_raw(\"WS2P 2 g1.dunitrust.org 443 ws2p\").unwrap(),\n EndpointV2::parse_from_raw(\"WS2P 2 rs.g1.librelois.fr 443 ws2p\").unwrap(),\n ],\n }\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\n/// WS2Pv2 Module\npub struct WS2PModule {}\n\nimpl Default for WS2PModule {\n fn default() -\u003e WS2PModule {\n WS2PModule {}\n }\n}\n\n#[derive(Debug)]\n/// WS2PFeaturesParseError\npub enum WS2PFeaturesParseError {\n /// UnknowApiFeature\n UnknowApiFeature(String),\n}\n\nimpl ApiModule\u003cDuRsConf, DursMsg\u003e for WS2PModule {\n type ParseErr = WS2PFeaturesParseError;\n /// Parse raw api features\n fn parse_raw_api_features(str_features: \u0026str) -\u003e Result\u003cApiFeatures, Self::ParseErr\u003e {\n let str_features: Vec\u003c\u0026str\u003e = str_features.split(' ').collect();\n let mut api_features = Vec::with_capacity(0);\n for str_feature in str_features {\n match str_feature {\n \"DEF\" =\u003e api_features[0] += 1u8,\n \"LOW\" =\u003e api_features[0] += 2u8,\n \"ABF\" =\u003e api_features[0] += 4u8,\n _ =\u003e {\n debug!(\n \"parse_raw_api_features() = UnknowApiFeature({})\",\n str_feature\n );\n return Err(WS2PFeaturesParseError::UnknowApiFeature(String::from(\n str_feature,\n )));\n }\n }\n }\n Ok(ApiFeatures(api_features))\n }\n}\n\nimpl NetworkModule\u003cDuRsConf, DursMsg\u003e for WS2PModule {\n fn sync(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n _conf: WS2PConf,\n _main_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n _sync_params: SyncOpt,\n ) -\u003e Result\u003c(), SyncError\u003e {\n unimplemented!()\n }\n}\n\n#[derive(StructOpt, Debug, Copy, Clone)]\n#[structopt(\n name = \"ws2p\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// WS2P subcommand options\npub struct WS2POpt {}\n\nimpl DursModule\u003cDuRsConf, DursMsg\u003e for WS2PModule {\n type ModuleUserConf = WS2PUserConf;\n type ModuleConf = WS2PConf;\n type ModuleOpt = WS2POpt;\n\n fn name() -\u003e ModuleStaticName {\n ModuleStaticName(constants::MODULE_NAME)\n }\n fn priority() -\u003e ModulePriority {\n ModulePriority::Essential()\n }\n fn ask_required_keys() -\u003e RequiredKeys {\n RequiredKeys::NetworkKeyPair()\n }\n fn have_subcommand() -\u003e bool {\n true\n }\n fn generate_module_conf(\n _currency_name: Option\u003c\u0026CurrencyName\u003e,\n _global_conf: \u0026\u003cDuRsConf as DursConfTrait\u003e::GlobalConf,\n module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n ) -\u003e Result\u003c(Self::ModuleConf, Option\u003cSelf::ModuleUserConf\u003e), ModuleConfError\u003e {\n let mut conf = WS2PConf::default();\n\n if let Some(module_user_conf) = module_user_conf.clone() {\n if let Some(outcoming_quota) = module_user_conf.outcoming_quota {\n conf.outcoming_quota = outcoming_quota;\n }\n if let Some(sync_endpoints) = module_user_conf.sync_endpoints {\n conf.sync_endpoints = sync_endpoints;\n }\n }\n\n Ok((conf, module_user_conf))\n }\n fn exec_subcommand(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n _module_conf: Self::ModuleConf,\n _module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n _subcommand_args: WS2POpt,\n ) -\u003e Option\u003cSelf::ModuleUserConf\u003e {\n println!(\"Succesfully exec ws2p subcommand !\");\n None\n }\n fn start(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n keys: RequiredKeysContent,\n _conf: WS2PConf,\n router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) -\u003e Result\u003c(), failure::Error\u003e {\n // Get key_pair\n let _key_pair = if let RequiredKeysContent::NetworkKeyPair(key_pair) = keys {\n key_pair\n } else {\n return Err(WS2PError::UnexpectedKeys.into());\n };\n\n // Create module channel\n let (module_sender, module_receiver) = mpsc::channel();\n\n // Registration with the rooter\n if router_sender\n .send(RouterThreadMessage::ModuleRegistration {\n static_name: ModuleStaticName(constants::MODULE_NAME),\n sender: module_sender,\n roles: vec![ModuleRole::InterNodesNetwork],\n events_subscription: vec![\n ModuleEvent::NewValidBlock,\n ModuleEvent::NewWotDocInPool,\n ModuleEvent::NewTxinPool,\n ],\n reserved_apis_parts: vec![ApiPart {\n name: ApiName(constants::API_NAME.to_owned()),\n versions: hashset![ApiVersion(2)],\n }],\n endpoints: vec![],\n })\n .is_err()\n {\n fatal_error!(\"WS2P module fail to send registration to router !\")\n }\n\n while let Ok(msg) = module_receiver.recv() {\n if let DursMsg::Stop = msg {\n break;\n }\n }\n\n Ok(())\n }\n}\n","traces":[{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":209,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":0}},{"line":240,"address":null,"length":0,"stats":{"Line":0}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":242,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":51},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","src","services","outgoing.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P outgoing Services\n\nuse crate::services::WsError;\nuse crate::*;\nuse dup_currency_params::CurrencyName;\nuse durs_network_documents::{NodeFullId, NodeId};\nuse durs_ws2p_protocol::connection_state::WS2PConnectionState;\nuse durs_ws2p_protocol::controller::WebsocketActionOrder;\nuse durs_ws2p_protocol::orchestrator::OrchestratorMsg;\nuse durs_ws2p_protocol::MySelfWs2pNode;\nuse std::collections::HashMap;\nuse std::sync::mpsc;\n\n#[derive(Debug, Clone)]\n/// Data allowing the service to manage an outgoing connection\npub struct OutgoingConnection {\n /// Endpoint\n pub endpoint: EndpointEnum,\n /// Controller channel\n pub controller: mpsc::Sender\u003cWebsocketActionOrder\u003e,\n}\n\n#[derive(Debug, Copy, Clone)]\n/// Endpoind whose last connection attempt failed\npub struct EndpointInError {\n /// Last attemp time\n pub last_attempt_time: u64,\n /// Error status\n pub error: WS2PConnectionState,\n}\n\n#[derive(Debug)]\n/// Outgoing connection management service\npub struct WS2POutgoingOrchestrator {\n /// Currency Name\n pub currency: CurrencyName,\n /// Local node datas\n pub self_node: MySelfWs2pNode,\n /// Outgoing connections quota\n pub quota: usize,\n /// List of established connections\n pub connections: HashMap\u003cNodeFullId, OutgoingConnection\u003e,\n /// List of endpoinds whose last connection attempt failed\n pub endpoints_in_error: HashMap\u003cNodeFullId, EndpointInError\u003e,\n /// List of endpoints that have never been contacted\n pub never_try_endpoints: Vec\u003cEndpointEnum\u003e,\n /// Service receiver\n pub receiver: mpsc::Receiver\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n /// Orchestrator sender\n pub sender: mpsc::Sender\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n}\n\nimpl WS2POutgoingOrchestrator {\n /// Instantiate WS2POutgoingOrchestrator\n pub fn new(\n currency: CurrencyName,\n ws2p_conf: \u0026WS2PConf,\n self_node: MySelfWs2pNode,\n ) -\u003e WS2POutgoingOrchestrator {\n // Create service channel\n let (sender, receiver) = mpsc::channel();\n\n WS2POutgoingOrchestrator {\n currency,\n quota: ws2p_conf.outcoming_quota,\n connections: HashMap::with_capacity(ws2p_conf.outcoming_quota),\n endpoints_in_error: HashMap::new(),\n never_try_endpoints: Vec::new(),\n self_node,\n receiver,\n sender,\n }\n }\n\n /// Connect to WSPv2 Endpoint\n pub fn connect_to_ws2p_v2_endpoint(\n \u0026self,\n endpoint: \u0026EndpointEnum,\n remote_node_id: Option\u003cNodeId\u003e,\n ) -\u003e Result\u003c(), WsError\u003e {\n let expected_remote_full_id = if let Some(remote_node_id) = remote_node_id {\n Some(NodeFullId(remote_node_id, endpoint.pubkey()))\n } else {\n None\n };\n match controllers::outgoing_connections::connect_to_ws2p_v2_endpoint(\n \u0026self.currency,\n \u0026self.sender,\n \u0026self.self_node,\n expected_remote_full_id,\n endpoint,\n ) {\n Ok(_) =\u003e Ok(()),\n Err(_) =\u003e Err(WsError::UnknownError),\n }\n }\n}\n","traces":[{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":19},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p","tests","connection_negociation.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dup_crypto::keys::KeyPair;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\n//use durs_common_tests_tools::logger::init_logger_stdout;\nuse durs_message::DursMsg;\nuse durs_network_documents::network_endpoint::*;\nuse durs_network_documents::*;\nuse durs_ws2p::controllers::incoming_connections::*;\nuse durs_ws2p::controllers::outgoing_connections::*;\nuse durs_ws2p_messages::v2::api_features::*;\nuse durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse durs_ws2p_protocol::connection_state::WS2PConnectionState;\nuse durs_ws2p_protocol::controller::{WS2PControllerEvent, WebsocketActionOrder};\nuse durs_ws2p_protocol::orchestrator::OrchestratorMsg;\nuse durs_ws2p_protocol::MySelfWs2pNode;\nuse std::sync::mpsc;\nuse std::thread;\nuse std::time::Duration;\n\npub static TIMEOUT_IN_MS: \u0026'static u64 = \u002620_000;\npub static PORT: \u0026'static u16 = \u002610899;\n\npub fn currency() -\u003e CurrencyName {\n CurrencyName(String::from(\"g1\"))\n}\n\npub fn keypair1() -\u003e ed25519::KeyPair {\n ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV1\".as_bytes(),\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV1_\".as_bytes(),\n )\n}\n\npub fn keypair2() -\u003e ed25519::KeyPair {\n ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWdLkjrUhHV2\".as_bytes(),\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWdLkjrUhHV2_\".as_bytes(),\n )\n}\n\nfn server_node() -\u003e MySelfWs2pNode {\n MySelfWs2pNode {\n my_node_id: NodeId(0),\n my_key_pair: KeyPairEnum::Ed25519(keypair1()),\n my_features: WS2PFeatures(vec![5u8]),\n }\n}\n\nfn client_node() -\u003e MySelfWs2pNode {\n MySelfWs2pNode {\n my_node_id: NodeId(1),\n my_key_pair: KeyPairEnum::Ed25519(keypair2()),\n my_features: WS2PFeatures(vec![5u8]),\n }\n}\n\n//#[ignore]\n#[test]\n#[cfg(unix)]\nfn test_connection_negociation_denial() {\n //init_logger_stdout();\n\n // ===== initialization =====\n // client and server are initialized and launched in separate threads\n\n let server_node = server_node();\n let client_node = client_node();\n\n // Create server service channel\n let server_service_channel = mpsc::channel();\n\n // Launch server controller\n let server_node_clone = server_node.clone();\n let server_service_sender = server_service_channel.0.clone();\n thread::spawn(move || {\n listen_on_ws2p_v2_endpoint(\n \u0026currency(),\n \u0026server_service_sender,\n \u0026server_node_clone,\n format!(\"localhost:{}\", *PORT + 1),\n )\n });\n\n // Create client service channel\n let client_service_channel = mpsc::channel();\n\n // launch client controller\n let server_node_clone = server_node.clone();\n let client_service_sender = client_service_channel.0.clone();\n thread::spawn(move || {\n connect_to_ws2p_v2_endpoint(\n \u0026currency(),\n \u0026client_service_sender,\n \u0026client_node,\n Some(NodeFullId(\n NodeId(2),\n server_node_clone.my_key_pair.public_key(),\n )),\n \u0026EndpointV2::parse_from_raw(\u0026format!(\"WS2P V2 localhost {}\", *PORT + 1))\n .expect(\"Fail to parse endpoint\"),\n )\n });\n\n // ===== opening connection =====\n // we must get Ws2pServiceSender::ControllerSender from the client and server threads (but we ignore them)\n // we also test that the statuses match expected ones\n\n let client_controller = get_controller(\u0026client_service_channel.1);\n let server_controller = get_controller(\u0026server_service_channel.1);\n\n // TryToSendConnectMsg\n let state = get_state(\u0026server_service_channel.1); // server\n assert_eq!(WS2PConnectionState::TryToSendConnectMsg, state);\n let state = get_state(\u0026client_service_channel.1); // client\n assert_eq!(WS2PConnectionState::TryToSendConnectMsg, state);\n\n // WaitingConnectMsg\n let state = get_state(\u0026server_service_channel.1); // server\n assert_eq!(WS2PConnectionState::WaitingConnectMsg, state);\n let state = get_state(\u0026client_service_channel.1); // client\n assert_eq!(WS2PConnectionState::WaitingConnectMsg, state);\n\n // ConnectMessOk \u0026 Denial\n let state = get_state(\u0026server_service_channel.1); // server\n assert_eq!(WS2PConnectionState::ConnectMessOk, state);\n let state = get_state(\u0026client_service_channel.1); // client\n assert_eq!(WS2PConnectionState::Denial, state);\n\n // Stop\n let _ = client_controller.send(WebsocketActionOrder::close());\n let _ = server_controller.send(WebsocketActionOrder::close());\n}\n\n//#[ignore]\n#[test]\n#[cfg(unix)]\nfn test_connection_negociation_success() {\n //init_logger_stdout();\n\n // ===== initialization =====\n // client and server are initialized and launched in separate threads\n\n let server_node = server_node();\n let client_node = client_node();\n\n // Create server service channel\n let server_service_channel = mpsc::channel();\n\n // Launch server controller\n let server_node_clone = server_node.clone();\n let server_service_sender = server_service_channel.0.clone();\n thread::spawn(move || {\n listen_on_ws2p_v2_endpoint(\n \u0026currency(),\n \u0026server_service_sender,\n \u0026server_node_clone,\n format!(\"localhost:{}\", *PORT),\n )\n });\n\n // Wait server ready...\n //thread::sleep(Duration::from_millis(500));\n\n // Create client service channel\n let client_service_channel = mpsc::channel();\n\n // launch client controller\n let client_node_clone = client_node.clone();\n let server_node_clone = server_node.clone();\n let client_service_sender = client_service_channel.0.clone();\n thread::spawn(move || {\n connect_to_ws2p_v2_endpoint(\n \u0026currency(),\n \u0026client_service_sender,\n \u0026client_node_clone,\n Some(server_node_clone.get_full_id()),\n \u0026EndpointV2::parse_from_raw(\u0026format!(\"WS2P V2 localhost {}\", *PORT))\n .expect(\"Fail to parse endpoint\"),\n )\n });\n\n // ===== opening connection =====\n // we must get Ws2pServiceSender::ControllerSender from the client and server threads (but we ignore them)\n // we also test that the statuses match expected ones\n\n let _client_controller = get_controller(\u0026client_service_channel.1);\n let _server_controller = get_controller(\u0026server_service_channel.1);\n\n // TryToSendConnectMsg\n let state = get_state(\u0026client_service_channel.1); // client\n assert_eq!(WS2PConnectionState::TryToSendConnectMsg, state);\n let state = get_state(\u0026server_service_channel.1); // server\n assert_eq!(WS2PConnectionState::TryToSendConnectMsg, state);\n\n // WaitingConnectMsg\n let state = get_state(\u0026client_service_channel.1); // client\n assert_eq!(WS2PConnectionState::WaitingConnectMsg, state);\n let state = get_state(\u0026server_service_channel.1); // server\n assert_eq!(WS2PConnectionState::WaitingConnectMsg, state);\n\n // ConnectMessOk\n let state = get_state(\u0026client_service_channel.1); // client\n assert_eq!(WS2PConnectionState::ConnectMessOk, state);\n let state = get_state(\u0026server_service_channel.1); // server\n assert_eq!(WS2PConnectionState::ConnectMessOk, state);\n\n // Ack message\n let state_1 = get_state(\u0026client_service_channel.1); // client\n let state_2 = get_state(\u0026server_service_channel.1); // server\n\n println!(\"state_1: {:?}\", \u0026state_1);\n println!(\"state_2: {:?}\", \u0026state_2);\n\n assert!(\n // client faster\n ( state_1 == WS2PConnectionState::OkMsgOkWaitingAckMsg \u0026\u0026\n state_2 == WS2PConnectionState::AckMsgOk ) ||\n // server faster\n ( state_1 == WS2PConnectionState::AckMsgOk \u0026\u0026\n state_2 == WS2PConnectionState::OkMsgOkWaitingAckMsg ) ||\n // ack messages received at the same time\n ( state_1 == WS2PConnectionState::AckMsgOk \u0026\u0026\n state_2 == WS2PConnectionState::AckMsgOk )\n );\n\n // Established for client\n expected_event(\n \u0026client_service_channel.1,\n WS2PControllerEvent::NewConnEstablished {\n conn_type: WS2Pv2ConnectType::OutgoingServer,\n remote_full_id: server_node.get_full_id(),\n },\n );\n // Established for server\n expected_event(\n \u0026server_service_channel.1,\n WS2PControllerEvent::NewConnEstablished {\n conn_type: WS2Pv2ConnectType::OutgoingServer,\n remote_full_id: client_node.get_full_id(),\n },\n );\n}\n\n// === functions used in above test ===\n\n// Get established event in a receiver\nfn expected_event(\n orchestrator_receiver: \u0026mpsc::Receiver\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n expected_event: WS2PControllerEvent,\n) {\n match orchestrator_receiver\n .recv_timeout(Duration::from_millis(*TIMEOUT_IN_MS))\n .expect(\"Receive nothing from controller :\")\n {\n OrchestratorMsg::ControllerEvent { event, .. } =\u003e assert_eq!(expected_event, event),\n other =\u003e panic!(\"Expect signal ControllerEvent, receive '{:?}' !\", other),\n }\n}\n\n// get the state in a receiver\nfn get_state(\n orchestrator_receiver: \u0026mpsc::Receiver\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n) -\u003e WS2PConnectionState {\n match orchestrator_receiver\n .recv_timeout(Duration::from_millis(*TIMEOUT_IN_MS))\n .expect(\"Receive nothing from controller :\")\n {\n OrchestratorMsg::ControllerEvent {\n event: WS2PControllerEvent::StateChange { new_state },\n ..\n } =\u003e new_state,\n other =\u003e panic!(\n \"Expect signal ChangeConnectionState, receive '{:?}' !\",\n other\n ),\n }\n}\n\n// get the controller from the thread\nfn get_controller(\n orchestrator_receiver: \u0026mpsc::Receiver\u003cOrchestratorMsg\u003cDursMsg\u003e\u003e,\n) -\u003e mpsc::Sender\u003cWebsocketActionOrder\u003e {\n // we must receive controller sender\n if let Ok(OrchestratorMsg::ControllerSender(controller_sender)) =\n orchestrator_receiver.recv_timeout(Duration::from_millis(*TIMEOUT_IN_MS))\n {\n return controller_sender;\n } else {\n panic!(\"Not receive client controller sender\");\n }\n}\n","traces":[{"line":1,"address":4227976,"length":1,"stats":{"Line":0}},{"line":38,"address":4329040,"length":1,"stats":{"Line":1}},{"line":39,"address":4329054,"length":1,"stats":{"Line":1}},{"line":42,"address":4329136,"length":1,"stats":{"Line":1}},{"line":43,"address":4329146,"length":1,"stats":{"Line":1}},{"line":49,"address":4329360,"length":1,"stats":{"Line":1}},{"line":50,"address":4329370,"length":1,"stats":{"Line":1}},{"line":56,"address":4329584,"length":1,"stats":{"Line":1}},{"line":58,"address":4329594,"length":1,"stats":{"Line":1}},{"line":59,"address":4329602,"length":1,"stats":{"Line":1}},{"line":60,"address":4329666,"length":1,"stats":{"Line":1}},{"line":64,"address":4329856,"length":1,"stats":{"Line":1}},{"line":66,"address":4329866,"length":1,"stats":{"Line":1}},{"line":67,"address":4329874,"length":1,"stats":{"Line":1}},{"line":68,"address":4329938,"length":1,"stats":{"Line":1}},{"line":75,"address":4265360,"length":1,"stats":{"Line":2}},{"line":81,"address":4332151,"length":1,"stats":{"Line":1}},{"line":82,"address":4332213,"length":1,"stats":{"Line":1}},{"line":85,"address":4332220,"length":1,"stats":{"Line":1}},{"line":88,"address":4332278,"length":1,"stats":{"Line":1}},{"line":89,"address":4332293,"length":1,"stats":{"Line":1}},{"line":90,"address":4265392,"length":1,"stats":{"Line":2}},{"line":91,"address":4265731,"length":1,"stats":{"Line":1}},{"line":92,"address":4265412,"length":1,"stats":{"Line":1}},{"line":94,"address":4265457,"length":1,"stats":{"Line":1}},{"line":95,"address":4265461,"length":1,"stats":{"Line":1}},{"line":100,"address":4332584,"length":1,"stats":{"Line":1}},{"line":103,"address":4332619,"length":1,"stats":{"Line":1}},{"line":104,"address":4332634,"length":1,"stats":{"Line":1}},{"line":105,"address":4265840,"length":1,"stats":{"Line":2}},{"line":106,"address":4266407,"length":1,"stats":{"Line":1}},{"line":107,"address":4265860,"length":1,"stats":{"Line":1}},{"line":109,"address":4265905,"length":1,"stats":{"Line":1}},{"line":110,"address":4265986,"length":1,"stats":{"Line":1}},{"line":111,"address":4265909,"length":1,"stats":{"Line":1}},{"line":112,"address":4265925,"length":1,"stats":{"Line":1}},{"line":114,"address":4266070,"length":1,"stats":{"Line":1}},{"line":123,"address":4333061,"length":1,"stats":{"Line":1}},{"line":124,"address":4333124,"length":1,"stats":{"Line":1}},{"line":127,"address":4333187,"length":1,"stats":{"Line":1}},{"line":128,"address":4333241,"length":1,"stats":{"Line":1}},{"line":129,"address":4333708,"length":1,"stats":{"Line":1}},{"line":130,"address":4333744,"length":1,"stats":{"Line":1}},{"line":133,"address":4334193,"length":1,"stats":{"Line":1}},{"line":134,"address":4334229,"length":1,"stats":{"Line":1}},{"line":135,"address":4334678,"length":1,"stats":{"Line":1}},{"line":136,"address":4334714,"length":1,"stats":{"Line":1}},{"line":139,"address":4335151,"length":1,"stats":{"Line":1}},{"line":140,"address":4335181,"length":1,"stats":{"Line":1}},{"line":141,"address":4335588,"length":1,"stats":{"Line":1}},{"line":142,"address":4335618,"length":1,"stats":{"Line":1}},{"line":145,"address":4336031,"length":1,"stats":{"Line":1}},{"line":146,"address":4336092,"length":1,"stats":{"Line":1}},{"line":152,"address":4266656,"length":1,"stats":{"Line":2}},{"line":158,"address":4336567,"length":1,"stats":{"Line":1}},{"line":159,"address":4336629,"length":1,"stats":{"Line":1}},{"line":162,"address":4336636,"length":1,"stats":{"Line":1}},{"line":165,"address":4336686,"length":1,"stats":{"Line":1}},{"line":166,"address":4336716,"length":1,"stats":{"Line":1}},{"line":167,"address":4266688,"length":1,"stats":{"Line":2}},{"line":168,"address":4266972,"length":1,"stats":{"Line":1}},{"line":169,"address":4266708,"length":1,"stats":{"Line":1}},{"line":171,"address":4266753,"length":1,"stats":{"Line":1}},{"line":172,"address":4266757,"length":1,"stats":{"Line":1}},{"line":180,"address":4337004,"length":1,"stats":{"Line":1}},{"line":183,"address":4337039,"length":1,"stats":{"Line":1}},{"line":184,"address":4337070,"length":1,"stats":{"Line":1}},{"line":185,"address":4337103,"length":1,"stats":{"Line":1}},{"line":186,"address":4267072,"length":1,"stats":{"Line":2}},{"line":187,"address":4267537,"length":1,"stats":{"Line":1}},{"line":188,"address":4267092,"length":1,"stats":{"Line":1}},{"line":190,"address":4267137,"length":1,"stats":{"Line":1}},{"line":191,"address":4267146,"length":1,"stats":{"Line":1}},{"line":192,"address":4267252,"length":1,"stats":{"Line":1}},{"line":201,"address":4337512,"length":1,"stats":{"Line":1}},{"line":202,"address":4337575,"length":1,"stats":{"Line":1}},{"line":205,"address":4337638,"length":1,"stats":{"Line":1}},{"line":206,"address":4337692,"length":1,"stats":{"Line":1}},{"line":207,"address":4338159,"length":1,"stats":{"Line":1}},{"line":208,"address":4338195,"length":1,"stats":{"Line":1}},{"line":211,"address":4338644,"length":1,"stats":{"Line":1}},{"line":212,"address":4338680,"length":1,"stats":{"Line":1}},{"line":213,"address":4339129,"length":1,"stats":{"Line":1}},{"line":214,"address":4339165,"length":1,"stats":{"Line":1}},{"line":217,"address":4339614,"length":1,"stats":{"Line":1}},{"line":218,"address":4339650,"length":1,"stats":{"Line":1}},{"line":219,"address":4340093,"length":1,"stats":{"Line":1}},{"line":220,"address":4340123,"length":1,"stats":{"Line":1}},{"line":223,"address":4340530,"length":1,"stats":{"Line":1}},{"line":224,"address":4340560,"length":1,"stats":{"Line":1}},{"line":226,"address":4340598,"length":1,"stats":{"Line":1}},{"line":227,"address":4340764,"length":1,"stats":{"Line":1}},{"line":229,"address":4340990,"length":1,"stats":{"Line":1}},{"line":231,"address":4340922,"length":1,"stats":{"Line":1}},{"line":232,"address":4341073,"length":1,"stats":{"Line":0}},{"line":234,"address":4341022,"length":1,"stats":{"Line":1}},{"line":235,"address":4341146,"length":1,"stats":{"Line":1}},{"line":237,"address":4340961,"length":1,"stats":{"Line":1}},{"line":238,"address":4341228,"length":1,"stats":{"Line":1}},{"line":242,"address":4341504,"length":1,"stats":{"Line":1}},{"line":243,"address":4341333,"length":1,"stats":{"Line":1}},{"line":244,"address":4341387,"length":1,"stats":{"Line":1}},{"line":245,"address":4341341,"length":1,"stats":{"Line":1}},{"line":246,"address":4341352,"length":1,"stats":{"Line":1}},{"line":250,"address":4341680,"length":1,"stats":{"Line":1}},{"line":251,"address":4341511,"length":1,"stats":{"Line":1}},{"line":252,"address":4341564,"length":1,"stats":{"Line":1}},{"line":253,"address":4341519,"length":1,"stats":{"Line":1}},{"line":254,"address":4341530,"length":1,"stats":{"Line":1}},{"line":262,"address":4330128,"length":1,"stats":{"Line":1}},{"line":266,"address":4330143,"length":1,"stats":{"Line":1}},{"line":267,"address":4330175,"length":1,"stats":{"Line":1}},{"line":268,"address":4330317,"length":1,"stats":{"Line":1}},{"line":270,"address":4330333,"length":1,"stats":{"Line":1}},{"line":271,"address":4330854,"length":1,"stats":{"Line":0}},{"line":276,"address":4331392,"length":1,"stats":{"Line":1}},{"line":279,"address":4331404,"length":1,"stats":{"Line":1}},{"line":280,"address":4331409,"length":1,"stats":{"Line":1}},{"line":283,"address":4331515,"length":1,"stats":{"Line":1}},{"line":284,"address":4331529,"length":1,"stats":{"Line":1}},{"line":286,"address":4331540,"length":1,"stats":{"Line":1}},{"line":287,"address":4331579,"length":1,"stats":{"Line":0}},{"line":295,"address":4331824,"length":1,"stats":{"Line":1}},{"line":299,"address":4331911,"length":1,"stats":{"Line":1}},{"line":300,"address":4331836,"length":1,"stats":{"Line":1}},{"line":302,"address":4331956,"length":1,"stats":{"Line":1}},{"line":304,"address":4332033,"length":1,"stats":{"Line":0}}],"covered":122,"coverable":127},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Handles WebSocketToPeer API Messages.\n\n#![allow(clippy::large_enum_variant)]\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n/*#[cfg(test)]\n#[macro_use]\nextern crate pretty_assertions;*/\n\n#[macro_use]\nextern crate serde_derive;\n#[macro_use]\nextern crate log;\n\n/// WS2Pv2 Messages\npub mod v2;\n\nuse crate::v2::WS2Pv2Message;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::bin_signable::BinSignable;\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\n\n#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2Message\npub enum WS2PMessage {\n /// Old version not used\n _V0,\n /// Old version not used\n _V1,\n /// Version 2\n V2(WS2Pv2Message),\n}\n\n/// Enumerate errors can happen when parsing and checking messages\n#[derive(Debug)]\npub enum WS2PMessageError {\n /// Error at deserialization\n DeserError(bincode::Error),\n /// Invalid hash\n InvalidHash,\n /// Invalid signature\n SigError(SigError),\n}\n\nimpl From\u003cbincode::Error\u003e for WS2PMessageError {\n fn from(e: bincode::Error) -\u003e Self {\n WS2PMessageError::DeserError(e)\n }\n}\n\nimpl WS2PMessage {\n /// Get message hash\n pub fn hash(\u0026self) -\u003e Option\u003cHash\u003e {\n match *self {\n WS2PMessage::V2(ref msg_v2) =\u003e msg_v2.message_hash,\n WS2PMessage::_V0 | WS2PMessage::_V1 =\u003e {\n fatal_error!(\"Dev error: must not use WS2PMessage version \u003c 2 in WS2Pv2+ !\")\n }\n }\n }\n\n /// Parse and check bin message\n pub fn parse_and_check_bin_message(bin_msg: \u0026[u8]) -\u003e Result\u003cWS2PMessage, WS2PMessageError\u003e {\n let msg: WS2PMessage = bincode::deserialize(\u0026bin_msg)?;\n let hash = msg.hash();\n //debug!(\"parse_and_check_bin_message: hash={:?}\", hash);\n // Compute hash len\n let hash_len = 33;\n // Compute signature len\n let sig_len = if let Some(sig) = msg.signature() {\n match sig {\n Sig::Ed25519(_) =\u003e 69,\n Sig::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet implemented !\"),\n }\n } else {\n 1\n };\n\n if hash.is_none()\n || Hash::compute(\u0026bin_msg[0..(bin_msg.len() - hash_len - sig_len)])\n == hash.expect(\"safe unwrap\")\n {\n match msg.verify() {\n Ok(()) =\u003e Ok(msg),\n Err(e) =\u003e Err(WS2PMessageError::SigError(e)),\n }\n } else {\n Err(WS2PMessageError::InvalidHash)\n }\n }\n}\n\nimpl\u003c'de\u003e BinSignable\u003c'de\u003e for WS2PMessage {\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n match *self {\n WS2PMessage::V2(ref msg_v2) =\u003e msg_v2.issuer_pubkey(),\n WS2PMessage::_V0 | WS2PMessage::_V1 =\u003e {\n fatal_error!(\"Dev error: must not use WS2PMessage version \u003c 2 in WS2Pv2+ !\")\n }\n }\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n match *self {\n WS2PMessage::V2(ref msg_v2) =\u003e msg_v2.signature(),\n WS2PMessage::_V0 | WS2PMessage::_V1 =\u003e {\n fatal_error!(\"Dev error: must not use WS2PMessage version \u003c 2 in WS2Pv2+ !\")\n }\n }\n }\n fn set_signature(\u0026mut self, signature: Sig) {\n match *self {\n WS2PMessage::V2(ref mut msg_v2) =\u003e msg_v2.set_signature(signature),\n WS2PMessage::_V0 | WS2PMessage::_V1 =\u003e {\n fatal_error!(\"Dev error: must not use WS2PMessage version \u003c 2 in WS2Pv2+ !\")\n }\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use crate::v2::payload_container::WS2Pv2MessagePayload;\n use crate::v2::WS2Pv2Message;\n use bincode;\n use bincode::{deserialize, serialize};\n use dubp_documents::documents::certification::*;\n use dubp_documents::{BlockNumber, Blockstamp};\n use dup_crypto::keys::bin_signable::BinSignable;\n use dup_crypto::keys::*;\n use dup_currency_params::CurrencyName;\n use durs_network_documents::network_endpoint::*;\n use durs_network_documents::network_peer::*;\n use durs_network_documents::*;\n use std::net::Ipv4Addr;\n use std::str::FromStr;\n\n pub fn keypair1() -\u003e ed25519::KeyPair {\n ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV\".as_bytes(),\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_\".as_bytes(),\n )\n }\n\n pub fn create_endpoint_v11() -\u003e EndpointV2 {\n EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![1u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: None,\n ip_v6: None,\n domain: Some(String::from(\"g1.durs.ifee.fr\")),\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n }\n }\n pub fn create_second_endpoint_v11() -\u003e EndpointV2 {\n EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![1u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: Some(Ipv4Addr::from_str(\"84.16.72.210\").unwrap()),\n ip_v6: None,\n domain: None,\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n }\n }\n\n pub fn create_peer_card_v11() -\u003e PeerCardV11 {\n PeerCardV11 {\n currency_name: CurrencyName(String::from(\"g1\")),\n issuer: PubKey::Ed25519(keypair1().pubkey),\n node_id: NodeId(0),\n created_on: BlockNumber(50),\n endpoints: vec![create_endpoint_v11(), create_second_endpoint_v11()],\n endpoints_str: vec![],\n sig: None,\n }\n }\n\n pub fn test_ws2p_message(payload: WS2Pv2MessagePayload) {\n let keypair1 = keypair1();\n let mut ws2p_message = WS2Pv2Message {\n currency_name: CurrencyName(String::from(\"g1\")),\n issuer_node_id: NodeId(0),\n issuer_pubkey: PubKey::Ed25519(keypair1.public_key()),\n payload,\n message_hash: None,\n signature: None,\n };\n\n let sign_result = ws2p_message.sign(PrivKey::Ed25519(keypair1.private_key()));\n if let Ok(bin_msg) = sign_result {\n // Test binarization\n assert_eq!(\n serialize(\u0026ws2p_message).expect(\"Fail to serialize WS2Pv2Message !\"),\n bin_msg\n );\n // Test sign\n ws2p_message\n .verify()\n .expect(\"WS2Pv2Message : Invalid signature !\");\n // Test debinarization\n let debinarization_result: Result\u003cWS2Pv2Message, bincode::Error\u003e =\n deserialize(\u0026bin_msg);\n if let Ok(ws2p_message2) = debinarization_result {\n assert_eq!(ws2p_message, ws2p_message2);\n } else {\n panic!(\n \"Fail to debinarize ws2p_message : {:?}\",\n debinarization_result.err().unwrap()\n );\n }\n } else {\n panic!(\n \"Fail to sign ws2p_message : {:?}\",\n sign_result.err().unwrap()\n );\n }\n }\n\n pub fn create_cert_doc() -\u003e CompactCertificationDocumentV10 {\n let sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"qfR6zqT1oJbqIsppOi64gC9yTtxb6g6XA9RYpulkq9ehMvqg2VYVigCbR0yVpqKFsnYiQTrnjgFuFRSJCJDfCw==\",\n ).unwrap());\n\n let target = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let blockstamp = Blockstamp::from_string(\n \"36-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B865\",\n )\n .unwrap();\n\n CompactCertificationDocumentV10 {\n issuer: PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR\")\n .unwrap(),\n ),\n target,\n block_number: blockstamp.id,\n signature: sig,\n }\n }\n}\n","traces":[{"line":1,"address":5084106,"length":1,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":1}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":109,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":2}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":2}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":2}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":2}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":2}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":2}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":4688224,"length":1,"stats":{"Line":1}},{"line":163,"address":4688234,"length":1,"stats":{"Line":1}},{"line":169,"address":4688448,"length":1,"stats":{"Line":1}},{"line":171,"address":4688465,"length":1,"stats":{"Line":1}},{"line":173,"address":4688538,"length":1,"stats":{"Line":1}},{"line":174,"address":4688631,"length":1,"stats":{"Line":1}},{"line":177,"address":4688751,"length":1,"stats":{"Line":1}},{"line":179,"address":4688825,"length":1,"stats":{"Line":1}},{"line":182,"address":4689264,"length":1,"stats":{"Line":1}},{"line":184,"address":4689281,"length":1,"stats":{"Line":1}},{"line":186,"address":4689354,"length":1,"stats":{"Line":1}},{"line":187,"address":4689456,"length":1,"stats":{"Line":1}},{"line":188,"address":4689556,"length":1,"stats":{"Line":1}},{"line":192,"address":4689720,"length":1,"stats":{"Line":1}},{"line":196,"address":4690160,"length":1,"stats":{"Line":1}},{"line":198,"address":4690177,"length":1,"stats":{"Line":1}},{"line":199,"address":4690253,"length":1,"stats":{"Line":1}},{"line":200,"address":4690338,"length":1,"stats":{"Line":1}},{"line":201,"address":4690349,"length":1,"stats":{"Line":1}},{"line":202,"address":4690370,"length":1,"stats":{"Line":1}},{"line":203,"address":4690561,"length":1,"stats":{"Line":1}},{"line":208,"address":4690928,"length":1,"stats":{"Line":1}},{"line":209,"address":4690935,"length":1,"stats":{"Line":1}},{"line":210,"address":4691276,"length":1,"stats":{"Line":1}},{"line":211,"address":4691040,"length":1,"stats":{"Line":1}},{"line":212,"address":4691099,"length":1,"stats":{"Line":1}},{"line":213,"address":4691110,"length":1,"stats":{"Line":1}},{"line":214,"address":4691195,"length":1,"stats":{"Line":1}},{"line":215,"address":4691260,"length":1,"stats":{"Line":1}},{"line":216,"address":4691268,"length":1,"stats":{"Line":1}},{"line":219,"address":4691542,"length":1,"stats":{"Line":1}},{"line":220,"address":4691714,"length":1,"stats":{"Line":1}},{"line":222,"address":4691854,"length":1,"stats":{"Line":1}},{"line":223,"address":4691785,"length":1,"stats":{"Line":1}},{"line":227,"address":4692349,"length":1,"stats":{"Line":1}},{"line":232,"address":4692383,"length":1,"stats":{"Line":1}},{"line":233,"address":4692465,"length":1,"stats":{"Line":1}},{"line":234,"address":4692537,"length":1,"stats":{"Line":1}},{"line":236,"address":4693089,"length":1,"stats":{"Line":0}},{"line":238,"address":4692996,"length":1,"stats":{"Line":0}},{"line":241,"address":4692649,"length":1,"stats":{"Line":0}},{"line":242,"address":4693422,"length":1,"stats":{"Line":0}},{"line":244,"address":4693312,"length":1,"stats":{"Line":0}},{"line":249,"address":4694240,"length":1,"stats":{"Line":1}},{"line":250,"address":4694257,"length":1,"stats":{"Line":1}},{"line":254,"address":4694415,"length":1,"stats":{"Line":1}},{"line":255,"address":4694363,"length":1,"stats":{"Line":1}},{"line":259,"address":4694487,"length":1,"stats":{"Line":1}},{"line":265,"address":4694580,"length":1,"stats":{"Line":1}},{"line":270,"address":4694700,"length":1,"stats":{"Line":1}}],"covered":64,"coverable":91},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","api_features.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2PFeatures\npub struct WS2PFeatures(pub Vec\u003cu8\u003e);\n\nimpl WS2PFeatures {\n /// Return true if all flags are disabled (or if it's really empty).\n pub fn is_empty(\u0026self) -\u003e bool {\n for byte in \u0026self.0 {\n if *byte \u003e 0u8 {\n return false;\n }\n }\n true\n }\n /// Check flag DEF\n pub fn def(\u0026self) -\u003e bool {\n self.0[0] | 0b1111_1110 == 255u8\n }\n /// Check flag LOW\n pub fn low(\u0026self) -\u003e bool {\n self.0[0] | 0b1111_1101 == 255u8\n }\n /// Check flag ABF\n pub fn abf(\u0026self) -\u003e bool {\n self.0[0] | 0b1111_1011 == 255u8\n }\n /// Check features compatibility\n pub fn check_features_compatibility(\n \u0026self,\n remote_features: \u0026WS2PFeatures,\n ) -\u003e Result\u003cWS2PFeatures, ()\u003e {\n let mut merged_features = self.clone();\n // Remove features unsuported by remote node\n if self.def() \u0026\u0026 !remote_features.def() {\n merged_features.0[0] \u0026= 0b1111_1110;\n }\n if self.low() \u0026\u0026 !remote_features.low() {\n merged_features.0[0] \u0026= 0b1111_1101;\n }\n if self.abf() \u0026\u0026 !remote_features.abf() {\n merged_features.0[0] \u0026= 0b1111_1011;\n }\n // Check incompatiblities\n if remote_features.low() \u0026\u0026 !self.low() {\n Err(())\n } else {\n Ok(merged_features)\n }\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":28,"address":null,"length":0,"stats":{"Line":0}},{"line":31,"address":null,"length":0,"stats":{"Line":1}},{"line":32,"address":null,"length":0,"stats":{"Line":1}},{"line":35,"address":null,"length":0,"stats":{"Line":1}},{"line":36,"address":null,"length":0,"stats":{"Line":1}},{"line":39,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":1}},{"line":43,"address":null,"length":0,"stats":{"Line":1}},{"line":47,"address":null,"length":0,"stats":{"Line":1}},{"line":49,"address":null,"length":0,"stats":{"Line":1}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":1}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":1}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":1}}],"covered":13,"coverable":23},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","connect.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};\nuse super::api_features::WS2PFeatures;\nuse dubp_documents::Blockstamp;\nuse dup_crypto::hashs::Hash;\nuse durs_network_documents::network_peer::PeerCardV11;\n\n/// WS2P v2 connect message min size\npub static CONNECT_MSG_MIN_SIZE: \u0026'static usize = \u002636;\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2PConnectFlags\npub struct WS2PConnectFlags(Vec\u003cu8\u003e);\n\nimpl WS2PConnectFlags {\n /// Return true if all flags are disabled (or if it's really empty).\n pub fn is_empty(\u0026self) -\u003e bool {\n for byte in \u0026self.0 {\n if *byte \u003e 0u8 {\n return false;\n }\n }\n true\n }\n /// Check flag SYNC\n pub fn sync(\u0026self) -\u003e bool {\n 0b1111_1110 | self.0[0] == 255u8\n }\n /// Check flag ASK_SYNC_CHUNK\n pub fn ask_sync_chunk(\u0026self) -\u003e bool {\n 0b1111_1101 | self.0[0] == 255u8\n }\n /// Check flag RES_SYNC_CHUNK\n pub fn res_sync_chunk(\u0026self) -\u003e bool {\n 0b1111_1011 | self.0[0] == 255u8\n }\n /// Check flag CLIENT\n pub fn client(\u0026self) -\u003e bool {\n 0b1111_0111 | self.0[0] == 255u8\n }\n}\n\nimpl From\u003cWS2Pv2ConnectType\u003e for WS2PConnectFlags {\n fn from(connect_type: WS2Pv2ConnectType) -\u003e Self {\n match connect_type {\n WS2Pv2ConnectType::Incoming | WS2Pv2ConnectType::OutgoingServer =\u003e {\n WS2PConnectFlags(vec![])\n }\n WS2Pv2ConnectType::OutgoingClient =\u003e WS2PConnectFlags(vec![8u8]),\n WS2Pv2ConnectType::Sync(_) =\u003e WS2PConnectFlags(vec![1u8]),\n WS2Pv2ConnectType::SyncAskChunk(_) =\u003e WS2PConnectFlags(vec![3u8]),\n WS2Pv2ConnectType::SyncSendChunks =\u003e WS2PConnectFlags(vec![5u8]),\n }\n }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2ConnectType\npub enum WS2Pv2ConnectType {\n /// Incoming connection\n Incoming,\n /// Client outgoing connection\n OutgoingClient,\n /// Server outgoing connection\n OutgoingServer,\n /// Sync outgoing connection (from blockstamp, or from genesis block if blockstamp is none)\n Sync(Option\u003cBlockstamp\u003e),\n /// Sync outgoing connection to request chunk\n SyncAskChunk(Blockstamp),\n /// Sync outgoing connection to send chunk\n SyncSendChunks,\n}\n\nimpl WS2Pv2ConnectType {\n /// Create WS2Pv2ConnectType from WS2PConnectFlags\n pub fn from_flags(\n flags: \u0026WS2PConnectFlags,\n blockstamp: Option\u003cBlockstamp\u003e,\n ) -\u003e WS2Pv2ConnectType {\n if !flags.is_empty() \u0026\u0026 flags.sync() {\n if flags.ask_sync_chunk() \u0026\u0026 blockstamp.is_some() {\n WS2Pv2ConnectType::SyncAskChunk(blockstamp.expect(\"safe unwrap\"))\n } else if flags.res_sync_chunk() {\n WS2Pv2ConnectType::SyncSendChunks\n } else {\n WS2Pv2ConnectType::Sync(blockstamp)\n }\n } else {\n WS2Pv2ConnectType::OutgoingServer\n }\n }\n}\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2OkWS2Pv2ConnectMsgMsg\npub struct WS2Pv2ConnectMsg {\n /// random hash generated by the sending node of the CONNECT message,\n /// the receiving node will then have to sign this challenge and then\n /// send this signature in its ACK message to prove that it has the corresponding private key to the public key it indicates.\n pub challenge: Hash,\n /// This is exactly the same type as the field of the same name in the endpoints. But private WS2P nodes do not declare endpoints,\n /// so they must be able to indicate in the CONNECT message which features they support. Public WS2P nodes also fill this field,\n /// so any changes in the configuration of a public node will be applied on the 1st new connection. (If this was not the case,\n /// we would have to wait for the update of the peer record).\n pub api_features: WS2PFeatures,\n /// WS2PConnectFlags\n pub flags_queries: WS2PConnectFlags,\n /// Issuer PeerCard\n pub peer_card: Option\u003cPeerCardV11\u003e,\n /// Blockstamp of the last block of the chunk\n pub chunkstamp: Option\u003cBlockstamp\u003e,\n}\n\nimpl Default for WS2Pv2ConnectMsg {\n fn default() -\u003e Self {\n WS2Pv2ConnectMsg {\n challenge: Hash::random(),\n api_features: WS2PFeatures(vec![]),\n flags_queries: WS2PConnectFlags(vec![]),\n peer_card: None,\n chunkstamp: None,\n }\n }\n}\n\n/// Generate connect message\npub fn generate_connect_message(\n connect_type: WS2Pv2ConnectType,\n api_features: WS2PFeatures,\n challenge: Hash,\n peer_card: Option\u003cPeerCardV11\u003e,\n) -\u003e WS2Pv2ConnectMsg {\n let chunkstamp = if let WS2Pv2ConnectType::SyncAskChunk(chunkstamp) = connect_type {\n Some(chunkstamp)\n } else {\n None\n };\n WS2Pv2ConnectMsg {\n challenge,\n api_features,\n flags_queries: WS2PConnectFlags::from(connect_type),\n peer_card,\n chunkstamp,\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::super::*;\n use super::*;\n use crate::tests::*;\n use dubp_documents::Blockstamp;\n use dup_crypto::keys::text_signable::TextSignable;\n\n #[test]\n fn test_ws2p_connect_flags() {\n // test sync()\n assert!(WS2PConnectFlags(vec![1u8]).sync());\n assert!(WS2PConnectFlags(vec![3u8]).sync());\n assert!(WS2PConnectFlags(vec![5u8]).sync());\n\n // test ask_sync_chunk()\n assert_eq!(WS2PConnectFlags(vec![1u8]).ask_sync_chunk(), false);\n assert!(WS2PConnectFlags(vec![3u8]).ask_sync_chunk());\n assert_eq!(WS2PConnectFlags(vec![5u8]).ask_sync_chunk(), false);\n\n // test res_sync_chunk()\n assert_eq!(WS2PConnectFlags(vec![1u8]).res_sync_chunk(), false);\n assert_eq!(WS2PConnectFlags(vec![3u8]).res_sync_chunk(), false);\n assert!(WS2PConnectFlags(vec![5u8]).res_sync_chunk());\n }\n\n #[test]\n fn test_ws2p_message_connect() {\n let keypair1 = keypair1();\n let mut peer = create_peer_card_v11();\n peer.sign(PrivKey::Ed25519(keypair1.private_key()))\n .expect(\"Fail to sign peer card !\");\n let connect_msg = WS2Pv2ConnectMsg {\n challenge: Hash::from_hex(\n \"000007722B243094269E548F600BD34D73449F7578C05BD370A6D301D20B5F10\",\n )\n .unwrap(),\n api_features: WS2PFeatures(vec![7u8]),\n flags_queries: WS2PConnectFlags(vec![]),\n peer_card: Some(peer),\n chunkstamp: Some(\n Blockstamp::from_string(\n \"499-000011BABEEE1020B1F6B2627E2BC1C35BCD24375E114349634404D2C266D84F\",\n )\n .unwrap(),\n ),\n };\n test_ws2p_message(WS2Pv2MessagePayload::Connect(Box::new(connect_msg)));\n }\n}\n","traces":[{"line":31,"address":null,"length":0,"stats":{"Line":1}},{"line":32,"address":null,"length":0,"stats":{"Line":1}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":34,"address":null,"length":0,"stats":{"Line":0}},{"line":37,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":1}},{"line":41,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":1}},{"line":49,"address":null,"length":0,"stats":{"Line":1}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":2}},{"line":59,"address":null,"length":0,"stats":{"Line":2}},{"line":60,"address":null,"length":0,"stats":{"Line":2}},{"line":61,"address":null,"length":0,"stats":{"Line":2}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":5899296,"length":1,"stats":{"Line":2}},{"line":147,"address":5899306,"length":1,"stats":{"Line":1}},{"line":148,"address":5899397,"length":1,"stats":{"Line":0}},{"line":150,"address":5899474,"length":1,"stats":{"Line":2}},{"line":155,"address":5899543,"length":1,"stats":{"Line":2}},{"line":170,"address":6533120,"length":1,"stats":{"Line":2}},{"line":172,"address":6533127,"length":1,"stats":{"Line":1}},{"line":173,"address":6533326,"length":1,"stats":{"Line":1}},{"line":174,"address":6533511,"length":1,"stats":{"Line":1}},{"line":177,"address":6533696,"length":1,"stats":{"Line":1}},{"line":178,"address":6534279,"length":1,"stats":{"Line":1}},{"line":179,"address":6534464,"length":1,"stats":{"Line":1}},{"line":182,"address":6535047,"length":1,"stats":{"Line":1}},{"line":183,"address":6535588,"length":1,"stats":{"Line":1}},{"line":184,"address":6536129,"length":1,"stats":{"Line":1}},{"line":188,"address":6536512,"length":1,"stats":{"Line":2}},{"line":189,"address":6536519,"length":1,"stats":{"Line":1}},{"line":190,"address":6536557,"length":1,"stats":{"Line":1}},{"line":191,"address":6536578,"length":1,"stats":{"Line":1}},{"line":192,"address":6536755,"length":1,"stats":{"Line":1}},{"line":193,"address":6537253,"length":1,"stats":{"Line":1}},{"line":194,"address":6536762,"length":1,"stats":{"Line":1}},{"line":198,"address":6536821,"length":1,"stats":{"Line":1}},{"line":199,"address":6536918,"length":1,"stats":{"Line":1}},{"line":200,"address":6537018,"length":1,"stats":{"Line":1}},{"line":201,"address":6537196,"length":1,"stats":{"Line":1}},{"line":202,"address":6537104,"length":1,"stats":{"Line":1}},{"line":208,"address":6537453,"length":1,"stats":{"Line":1}}],"covered":43,"coverable":63},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n/// WS2P Features\npub mod api_features;\n/// WS2P v2 CONNECT Message\npub mod connect;\n/// WS2P v2 OK Message\npub mod ok;\n/// Message Payload container\npub mod payload_container;\n/// WS2Pv2 requests responses messages\npub mod req_responses;\n/// WS2Pv2 requests messages\npub mod requests;\n/// WS2P v2 SECRET_FLAGS Message\npub mod secret_flags;\n\nuse crate::v2::payload_container::*;\nuse crate::WS2PMessage;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::bin_signable::BinSignable;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\nuse durs_network_documents::NodeId;\n\n/// WS2P v2 message metadata size\npub static WS2P_V2_MESSAGE_METADATA_SIZE: \u0026'static usize = \u0026144;\n\n/// WS2Pv2Message\n#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]\npub struct WS2Pv2Message {\n /// Currency name\n pub currency_name: CurrencyName,\n /// Issuer NodeId\n pub issuer_node_id: NodeId,\n /// Issuer plublic key\n pub issuer_pubkey: PubKey,\n /// Message payload\n pub payload: WS2Pv2MessagePayload,\n /// Message hash\n pub message_hash: Option\u003cHash\u003e,\n /// Signature\n pub signature: Option\u003cSig\u003e,\n}\n\nimpl WS2Pv2Message {\n /// WS2P Version number\n pub const WS2P_VERSION: u16 = 0;\n\n /// Encapsulate message payload\n pub fn encapsulate_payload(\n currency_name: CurrencyName,\n issuer_node_id: NodeId,\n issuer_keypair: KeyPairEnum,\n payload: WS2Pv2MessagePayload,\n ) -\u003e Result\u003c(WS2PMessage, Vec\u003cu8\u003e), SignError\u003e {\n let mut msg = WS2PMessage::V2(WS2Pv2Message {\n currency_name,\n issuer_node_id,\n issuer_pubkey: issuer_keypair.public_key(),\n payload,\n message_hash: None,\n signature: None,\n });\n match msg.sign(issuer_keypair.private_key()) {\n Ok(bin_msg) =\u003e Ok((msg, bin_msg)),\n Err(e) =\u003e Err(e),\n }\n }\n}\n\nimpl\u003c'de\u003e BinSignable\u003c'de\u003e for WS2Pv2Message {\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n self.issuer_pubkey\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n self.signature\n }\n fn set_signature(\u0026mut self, signature: Sig) {\n self.signature = Some(signature)\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::tests::*;\n use dup_crypto::keys::text_signable::TextSignable;\n\n #[test]\n fn test_ws2p_message_ack() {\n test_ws2p_message(WS2Pv2MessagePayload::Ack {\n challenge: Hash::random(),\n });\n }\n\n #[test]\n fn test_ws2p_message_peers() {\n let keypair1 = keypair1();\n let mut peer = create_peer_card_v11();\n peer.sign(PrivKey::Ed25519(keypair1.private_key()))\n .expect(\"Fail to sign peer card !\");\n test_ws2p_message(WS2Pv2MessagePayload::Peers(vec![peer]));\n }\n}\n","traces":[{"line":64,"address":null,"length":0,"stats":{"Line":2}},{"line":70,"address":null,"length":0,"stats":{"Line":2}},{"line":71,"address":null,"length":0,"stats":{"Line":2}},{"line":72,"address":null,"length":0,"stats":{"Line":2}},{"line":73,"address":null,"length":0,"stats":{"Line":2}},{"line":74,"address":null,"length":0,"stats":{"Line":2}},{"line":75,"address":null,"length":0,"stats":{"Line":2}},{"line":76,"address":null,"length":0,"stats":{"Line":2}},{"line":78,"address":null,"length":0,"stats":{"Line":2}},{"line":79,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":3}},{"line":87,"address":null,"length":0,"stats":{"Line":3}},{"line":89,"address":null,"length":0,"stats":{"Line":3}},{"line":90,"address":null,"length":0,"stats":{"Line":3}},{"line":92,"address":null,"length":0,"stats":{"Line":3}},{"line":93,"address":null,"length":0,"stats":{"Line":3}},{"line":104,"address":6188064,"length":1,"stats":{"Line":2}},{"line":105,"address":6560885,"length":1,"stats":{"Line":1}},{"line":106,"address":6560871,"length":1,"stats":{"Line":1}},{"line":111,"address":6188096,"length":1,"stats":{"Line":2}},{"line":112,"address":6560967,"length":1,"stats":{"Line":1}},{"line":113,"address":6561002,"length":1,"stats":{"Line":1}},{"line":114,"address":6561023,"length":1,"stats":{"Line":1}},{"line":115,"address":6561197,"length":1,"stats":{"Line":1}},{"line":116,"address":6561214,"length":1,"stats":{"Line":1}}],"covered":25,"coverable":26},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","ok.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dubp_documents::Blockstamp;\nuse dup_crypto::hashs::Hash;\nuse std::num::NonZeroU16;\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2OkMsg\npub struct WS2Pv2OkMsg {\n /// If this field is zero, it means that the remote node does not want to reveal its prefix (the prefix being necessarily greater than or equal to 1).\n pub prefix: Option\u003cNonZeroU16\u003e,\n /// WS2Pv2SyncTarget\n pub sync_target: Option\u003cWS2Pv2SyncTarget\u003e,\n}\n\nimpl Default for WS2Pv2OkMsg {\n fn default() -\u003e Self {\n WS2Pv2OkMsg {\n prefix: None,\n sync_target: None,\n }\n }\n}\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2SyncTarget\npub struct WS2Pv2SyncTarget {\n /// Indicates the current blockstamp of the message sender node. This blockstamp will be the target to reach for the node being synchronized.\n pub target_blockstamp: Blockstamp,\n /// Hash table of the last block of each chunk. We do not need the block numbers, we know them. Here the remote node sends the hashs of all these chunk, which correspond to the current hashs of all the blocks having a number in 250 module 249, in ascending order.\n pub chunks_hash: Vec\u003cHash\u003e,\n}\n\n#[cfg(test)]\nmod tests {\n use super::super::*;\n use super::*;\n use crate::tests::*;\n use dubp_documents::Blockstamp;\n use std::num::NonZeroU16;\n\n #[test]\n fn test_ws2p_message_ok() {\n let ok_msg = WS2Pv2OkMsg {\n prefix: NonZeroU16::new(1),\n sync_target: Some(WS2Pv2SyncTarget {\n target_blockstamp: Blockstamp::from_string(\n \"500-000011BABEEE1020B1F6B2627E2BC1C35BCD24375E114349634404D2C266D84F\",\n )\n .unwrap(),\n chunks_hash: vec![\n Hash::from_hex(\n \"000007722B243094269E548F600BD34D73449F7578C05BD370A6D301D20B5F10\",\n )\n .unwrap(),\n Hash::from_hex(\n \"0000095FD4C8EA96DE2844E3A4B62FD18761E9B4C13A74FAB716A4C81F438D91\",\n )\n .unwrap(),\n ],\n }),\n };\n test_ws2p_message(WS2Pv2MessagePayload::Ok(ok_msg));\n }\n}\n","traces":[{"line":30,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":6190704,"length":1,"stats":{"Line":2}},{"line":57,"address":7122309,"length":1,"stats":{"Line":1}},{"line":58,"address":7121863,"length":1,"stats":{"Line":1}},{"line":59,"address":7122166,"length":1,"stats":{"Line":1}},{"line":60,"address":7121885,"length":1,"stats":{"Line":1}},{"line":64,"address":7121928,"length":1,"stats":{"Line":1}},{"line":65,"address":7121950,"length":1,"stats":{"Line":1}},{"line":69,"address":7122002,"length":1,"stats":{"Line":1}},{"line":76,"address":7122352,"length":1,"stats":{"Line":1}}],"covered":10,"coverable":10},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","req_responses.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dubp_documents::documents::block::BlockDocument;\nuse dubp_documents::documents::certification::CompactCertificationDocumentV10;\nuse dubp_documents::documents::identity::v10::CompactIdentityDocumentV10;\nuse dubp_documents::documents::membership::v10::CompactPoolMembershipDoc;\nuse dubp_documents::Blockstamp;\nuse dup_crypto::hashs::Hash;\nuse std::str;\n\n/// WS2Pv2 request response\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct WS2Pv2ReqRes {\n /// request unique identifier\n pub id: u32,\n /// request body\n pub body: WS2Pv2ReqResBody,\n}\n\n/// WS2Pv2 request response body\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub enum WS2Pv2ReqResBody {\n /// Empty response\n None,\n /// BadRequest (reason)\n BadRequest(String),\n /// Current blockstamp\n Current(Blockstamp),\n /// Blocks hashs\n BlocksHashs(Vec\u003cHash\u003e),\n /// Chunk of blocks.\n Chunk(Vec\u003cBlockDocument\u003e),\n /// Wot pool datas\n WotPool(Vec\u003cCompactCertificationDocumentV10\u003e, Vec\u003cWotPoolFolder\u003e),\n}\n\n///WotPoolFolder\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct WotPoolFolder {\n /// Pending identity\n pub idty: CompactIdentityDocumentV10,\n /// Pending first membership\n pub membership: CompactPoolMembershipDoc,\n /// Pending certs\n pub certs: Vec\u003cCompactCertificationDocumentV10\u003e,\n}\n\n#[cfg(test)]\nmod tests {\n use super::super::*;\n use super::*;\n use crate::tests::*;\n use dubp_documents::Blockstamp;\n\n #[test]\n fn test_ws2p_message_req_res_none() {\n let response = WS2Pv2ReqRes {\n id: 27,\n body: WS2Pv2ReqResBody::None,\n };\n test_ws2p_message(WS2Pv2MessagePayload::ReqRes(response));\n }\n\n #[test]\n fn test_ws2p_message_req_res_bad_request() {\n let reason = String::from(\"bla bla bla\");\n let response = WS2Pv2ReqRes {\n id: 28,\n body: WS2Pv2ReqResBody::BadRequest(reason),\n };\n test_ws2p_message(WS2Pv2MessagePayload::ReqRes(response));\n }\n\n #[test]\n fn test_ws2p_message_req_res_current() {\n let blockstamp = Blockstamp::from_string(\n \"499-000011BABEEE1020B1F6B2627E2BC1C35BCD24375E114349634404D2C266D84F\",\n )\n .unwrap();\n let response = WS2Pv2ReqRes {\n id: 28,\n body: WS2Pv2ReqResBody::Current(blockstamp),\n };\n test_ws2p_message(WS2Pv2MessagePayload::ReqRes(response));\n }\n\n #[test]\n fn test_ws2p_message_req_res_blocks_hashs() {\n let hashs = vec![\n Hash::from_hex(\"000011BABEEE1020B1F6B2627E2BC1C35BCD24375E114349634404D2C266D84F\")\n .unwrap(),\n Hash::from_hex(\"0000007F8D3CCAF77CB77C5C025C4AED8A82BA2DBD2156FD92C9634DAB59BD7E\")\n .unwrap(),\n ];\n let response = WS2Pv2ReqRes {\n id: 29,\n body: WS2Pv2ReqResBody::BlocksHashs(hashs),\n };\n test_ws2p_message(WS2Pv2MessagePayload::ReqRes(response));\n }\n\n #[test]\n fn test_ws2p_message_req_res_wot_pool() {\n let cert_doc = create_cert_doc();\n let response = WS2Pv2ReqRes {\n id: 29,\n body: WS2Pv2ReqResBody::WotPool(vec![cert_doc.clone(), cert_doc.clone()], vec![]),\n };\n test_ws2p_message(WS2Pv2MessagePayload::ReqRes(response));\n }\n}\n","traces":[{"line":69,"address":5698848,"length":1,"stats":{"Line":2}},{"line":70,"address":7208415,"length":1,"stats":{"Line":1}},{"line":72,"address":7208407,"length":1,"stats":{"Line":1}},{"line":74,"address":7208457,"length":1,"stats":{"Line":1}},{"line":78,"address":5698880,"length":1,"stats":{"Line":2}},{"line":79,"address":7208590,"length":1,"stats":{"Line":1}},{"line":80,"address":7208706,"length":1,"stats":{"Line":1}},{"line":82,"address":7208608,"length":1,"stats":{"Line":1}},{"line":84,"address":7208751,"length":1,"stats":{"Line":1}},{"line":88,"address":5698912,"length":1,"stats":{"Line":2}},{"line":89,"address":7208878,"length":1,"stats":{"Line":1}},{"line":93,"address":7209009,"length":1,"stats":{"Line":1}},{"line":95,"address":7208912,"length":1,"stats":{"Line":1}},{"line":97,"address":7209057,"length":1,"stats":{"Line":1}},{"line":101,"address":5698944,"length":1,"stats":{"Line":2}},{"line":102,"address":7209191,"length":1,"stats":{"Line":1}},{"line":103,"address":7209213,"length":1,"stats":{"Line":1}},{"line":105,"address":7209256,"length":1,"stats":{"Line":1}},{"line":108,"address":7209503,"length":1,"stats":{"Line":1}},{"line":110,"address":7209405,"length":1,"stats":{"Line":1}},{"line":112,"address":7209554,"length":1,"stats":{"Line":1}},{"line":116,"address":5698976,"length":1,"stats":{"Line":2}},{"line":117,"address":7209671,"length":1,"stats":{"Line":1}},{"line":118,"address":7210022,"length":1,"stats":{"Line":1}},{"line":120,"address":7209698,"length":1,"stats":{"Line":1}},{"line":122,"address":7210073,"length":1,"stats":{"Line":1}}],"covered":26,"coverable":26},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","requests.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dubp_documents::{BlockNumber, Blockstamp};\n\n/// WS2Pv2Request\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]\npub struct WS2Pv2Request {\n /// request unique identifier\n pub id: u32,\n /// request body\n pub body: WS2Pv2RequestBody,\n}\n\nimpl WS2Pv2Request {\n /// Request size in binary format\n pub fn size_in_bytes(\u0026self) -\u003e usize {\n 4 + self.body.size_in_bytes()\n }\n}\n\n/// WS2Pv2RequestBody\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]\npub enum WS2Pv2RequestBody {\n /// Empty request\n None,\n /// Request current blockstamp\n Current,\n /// BLOCKS_HASHS : In case of fork, to quickly find the fork point, the node will request the hashes of the ForkWindowsSize of the local blockchains of the other nodes.\n /// It would be counterproductive to ask directly for the entire blocks, when you will only need them if you actually decide to stack the corresponding branch.\n /// param1: begin_block_id (u32)\n /// param2: blocks_count (u16)\n BlocksHashs(BlockNumber, u16),\n /// CHUNK: Request chunk of blocks.\n /// param1: begin_block_id (u32)\n /// param2: blocks_count (u16)\n Chunk(BlockNumber, u16),\n /// CHUNK_BY_HASH : During synchronization, chunk is requested by Chunkstamp (= Blockstamp of the last block of the chunk).\n ChunkByHash(Blockstamp),\n /// WOT_POOL : For network performance reasons, a Durs* node never shares its entire wot pool at once.\n /// It randomly selects folders_count folders among those having received at least min_cert certifications.\n /// It's the requesting node that sets the values of min_cert and folders_count according to its connection rate,\n /// its configuration and the rate of new folders it has obtained in these previous requests.\n /// param1: folders_count (u16)\n /// param2: min_cert (u8)\n WotPool(u16, u8),\n}\n\nimpl WS2Pv2RequestBody {\n /// Request size in binary format\n pub fn size_in_bytes(\u0026self) -\u003e usize {\n match *self {\n WS2Pv2RequestBody::None | WS2Pv2RequestBody::Current =\u003e 1,\n WS2Pv2RequestBody::BlocksHashs(_, _) | WS2Pv2RequestBody::Chunk(_, _) =\u003e 7,\n WS2Pv2RequestBody::ChunkByHash(_) =\u003e 37,\n WS2Pv2RequestBody::WotPool(_, _) =\u003e 4,\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::super::*;\n use super::*;\n use crate::tests::*;\n use dubp_documents::Blockstamp;\n\n #[test]\n fn test_ws2p_message_request() {\n let chunkstamp = Blockstamp::from_string(\n \"499-000011BABEEE1020B1F6B2627E2BC1C35BCD24375E114349634404D2C266D84F\",\n )\n .unwrap();\n let request = WS2Pv2Request {\n id: 27,\n body: WS2Pv2RequestBody::ChunkByHash(chunkstamp),\n };\n test_ws2p_message(WS2Pv2MessagePayload::Request(request));\n }\n}\n","traces":[{"line":29,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":5029408,"length":1,"stats":{"Line":2}},{"line":82,"address":5767550,"length":1,"stats":{"Line":1}},{"line":86,"address":5767678,"length":1,"stats":{"Line":1}},{"line":88,"address":5767584,"length":1,"stats":{"Line":1}},{"line":90,"address":5767730,"length":1,"stats":{"Line":1}}],"covered":5,"coverable":13},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-messages","v2","secret_flags.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse dup_crypto::keys::*;\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2SecretFlags\npub struct WS2Pv2SecretFlags(Vec\u003cu8\u003e);\n\nimpl WS2Pv2SecretFlags {\n /// Return true if all flags are disabled (or if it's really empty).\n pub fn is_empty(\u0026self) -\u003e bool {\n for byte in \u0026self.0 {\n if *byte \u003e 0u8 {\n return false;\n }\n }\n true\n }\n /// Check flag LOW_FLOW_DEMAND\n pub fn _low_flow_demand(\u0026self) -\u003e bool {\n self.0[0] | 0b1111_1110 == 255u8\n }\n}\n\n#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// Member proof\npub struct MemberProof {\n /// Member pubkey\n pub pubkey: PubKey,\n /// Proof that the sender node is a member (Signature of the challenge send by other node in their CONNECT message.)\n pub sig: Sig,\n}\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]\n/// WS2Pv2SecretFlagsMsg\npub struct WS2Pv2SecretFlagsMsg {\n /// Secret flags\n pub secret_flags: WS2Pv2SecretFlags,\n /// Member proof\n pub member_proof: Option\u003cMemberProof\u003e,\n}\n\nimpl Default for WS2Pv2SecretFlagsMsg {\n fn default() -\u003e Self {\n WS2Pv2SecretFlagsMsg {\n secret_flags: WS2Pv2SecretFlags(vec![]),\n member_proof: None,\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::super::*;\n use super::*;\n use crate::tests::*;\n\n #[test]\n fn test_ws2p_message_secret_flags() {\n let keypair1 = keypair1();\n let challenge = Hash::random();\n let msg = WS2Pv2SecretFlagsMsg {\n secret_flags: WS2Pv2SecretFlags(vec![]),\n member_proof: Some(MemberProof {\n pubkey: PubKey::Ed25519(keypair1.public_key()),\n sig: Sig::Ed25519(keypair1.private_key().sign(\u0026challenge.0)),\n }),\n };\n test_ws2p_message(WS2Pv2MessagePayload::SecretFlags(msg));\n }\n}\n","traces":[{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":26,"address":null,"length":0,"stats":{"Line":0}},{"line":27,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":34,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":5074912,"length":1,"stats":{"Line":2}},{"line":73,"address":6554135,"length":1,"stats":{"Line":1}},{"line":74,"address":6554162,"length":1,"stats":{"Line":1}},{"line":75,"address":6554556,"length":1,"stats":{"Line":1}},{"line":76,"address":6554180,"length":1,"stats":{"Line":1}},{"line":77,"address":6554441,"length":1,"stats":{"Line":1}},{"line":78,"address":6554243,"length":1,"stats":{"Line":1}},{"line":79,"address":6554325,"length":1,"stats":{"Line":1}},{"line":82,"address":6554645,"length":1,"stats":{"Line":1}}],"covered":9,"coverable":18},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","meta_datas.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub module define WS2P controller meta datas\n\nuse crate::connection_state::WS2PConnectionState;\nuse crate::MySelfWs2pNode;\nuse dubp_documents::Blockstamp;\nuse dup_crypto::hashs::Hash;\nuse dup_currency_params::CurrencyName;\nuse durs_network_documents::network_peer::PeerCardV11;\nuse durs_network_documents::NodeFullId;\nuse durs_ws2p_messages::v2::api_features::WS2PFeatures;\nuse durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse std::time::SystemTime;\n\n#[derive(Debug, Clone)]\n/// WS2p Connection meta datas\npub struct WS2PControllerMetaDatas {\n /// Local challenge\n pub challenge: Hash,\n /// connect type\n pub connect_type: WS2Pv2ConnectType,\n /// Count invalid messages\n pub count_invalid_msgs: usize,\n /// Currency name\n pub currency: CurrencyName,\n /// Controller creation time\n pub creation_time: SystemTime,\n /// Connection features\n pub features: Option\u003cWS2PFeatures\u003e,\n /// Timestamp of last received message\n pub last_mess_time: SystemTime,\n /// Local node properties\n pub local_node: MySelfWs2pNode,\n /// Remote connect type\n pub remote_connect_type: Option\u003cWS2Pv2ConnectType\u003e,\n /// Remote node datas\n pub remote_node: Option\u003cWs2pRemoteNodeDatas\u003e,\n /// Indicator required for the anti-spam mechanism\n pub spam_interval: bool,\n /// Indicator required for the anti-spam mechanism\n pub spam_counter: usize,\n /// Connection state\n pub state: WS2PConnectionState,\n}\n\nimpl WS2PControllerMetaDatas {\n /// Instanciate new WS2PControllerMetaDatas\n pub fn new(\n challenge: Hash,\n connect_type: WS2Pv2ConnectType,\n currency: CurrencyName,\n local_node: MySelfWs2pNode,\n ) -\u003e Self {\n WS2PControllerMetaDatas {\n challenge,\n connect_type,\n count_invalid_msgs: 0,\n currency,\n creation_time: SystemTime::now(),\n features: None,\n last_mess_time: SystemTime::now(),\n local_node,\n remote_connect_type: None,\n remote_node: None,\n spam_interval: false,\n spam_counter: 0,\n state: WS2PConnectionState::TryToOpenWS,\n }\n }\n}\n\n#[derive(Debug, Clone)]\n/// WS2P remote node datas\npub struct Ws2pRemoteNodeDatas {\n /// Remote challenge\n pub challenge: Hash,\n /// Remote current blockstamp\n pub current_blockstamp: Option\u003cBlockstamp\u003e,\n /// Remote peer card\n pub peer_card: Option\u003cPeerCardV11\u003e,\n /// Remote full id\n pub remote_full_id: NodeFullId,\n}\n","traces":[{"line":62,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":1}}],"covered":3,"coverable":3},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","on_message","ack_msg.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module process reception of ACK message\n\nuse crate::connection_state::WS2PConnectionState;\nuse crate::controller::{WS2PController, WS2PControllerProcessError, WebsocketActionOrder};\nuse crate::websocket::{WebsocketAction, WebsocketMessage};\nuse dup_crypto::hashs::Hash;\nuse durs_common_tools::fatal_error;\nuse durs_module::ModuleMessage;\nuse durs_ws2p_messages::v2::ok::WS2Pv2OkMsg;\nuse durs_ws2p_messages::v2::payload_container::WS2Pv2MessagePayload;\nuse durs_ws2p_messages::v2::WS2Pv2Message;\nuse log::error;\n\n/// Process WS2P v2+ ACK Message\npub fn process_ws2p_v2p_ack_msg\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e, // controller contains original challenge\n ack_msg_challenge: Hash,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n log::debug!(\"Receive ACK message !\");\n\n match controller.meta_datas.state {\n WS2PConnectionState::OkMsgOkWaitingAckMsg =\u003e {\n // already sent ack message and received ok response\n process(\n controller,\n ack_msg_challenge,\n WS2PConnectionState::Established,\n )\n }\n WS2PConnectionState::ConnectMessOk =\u003e {\n // ack message not yet sent\n process(controller, ack_msg_challenge, WS2PConnectionState::AckMsgOk)\n }\n _ =\u003e Ok(super::close_with_reason(\n \"Unexpected ACK message !\",\n WS2PConnectionState::Denial,\n )),\n }\n}\n\n#[inline]\n// process and apply given status in case of success\nfn process\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n ack_msg_challenge: Hash,\n success_status: WS2PConnectionState,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n if controller.meta_datas.challenge != ack_msg_challenge {\n controller\n .update_conn_state(WS2PConnectionState::Denial)\n .map(|_| None)\n } else {\n Ok(Some(send_ok_msg(controller, success_status)))\n }\n}\n\n// send ok message\nfn send_ok_msg\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n success_status: WS2PConnectionState,\n) -\u003e WebsocketActionOrder {\n // generate empty Ok message\n let ok_msg = WS2Pv2OkMsg::default();\n\n // Encapsulate and binarize OK message\n if let Ok((_, bin_ok_msg)) = WS2Pv2Message::encapsulate_payload(\n controller.meta_datas.currency.clone(),\n controller.meta_datas.local_node.my_node_id,\n controller.meta_datas.local_node.my_key_pair,\n WS2Pv2MessagePayload::Ok(ok_msg),\n ) {\n // Order the sending of a OK message\n WebsocketActionOrder {\n ws_action: WebsocketAction::SendMessage {\n msg: WebsocketMessage::Bin(bin_ok_msg),\n },\n new_state_if_success: Some(success_status),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n }\n } else {\n fatal_error!(\"Dev error: Fail to sign own ok message !\");\n }\n}\n","traces":[{"line":30,"address":4564512,"length":1,"stats":{"Line":1}},{"line":36,"address":4564900,"length":1,"stats":{"Line":0}},{"line":37,"address":4564753,"length":1,"stats":{"Line":1}},{"line":40,"address":4564809,"length":1,"stats":{"Line":0}},{"line":41,"address":4564819,"length":1,"stats":{"Line":0}},{"line":42,"address":4564866,"length":1,"stats":{"Line":0}},{"line":47,"address":4564905,"length":1,"stats":{"Line":1}},{"line":49,"address":4565013,"length":1,"stats":{"Line":1}},{"line":51,"address":4565005,"length":1,"stats":{"Line":1}},{"line":58,"address":4565088,"length":1,"stats":{"Line":1}},{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":2}},{"line":73,"address":4562496,"length":1,"stats":{"Line":1}},{"line":78,"address":4562523,"length":1,"stats":{"Line":1}},{"line":81,"address":4562912,"length":1,"stats":{"Line":1}},{"line":82,"address":4562589,"length":1,"stats":{"Line":2}},{"line":83,"address":4562619,"length":1,"stats":{"Line":1}},{"line":84,"address":4562633,"length":1,"stats":{"Line":2}},{"line":85,"address":4562736,"length":1,"stats":{"Line":2}},{"line":89,"address":4563126,"length":1,"stats":{"Line":1}},{"line":92,"address":4563202,"length":1,"stats":{"Line":1}}],"covered":17,"coverable":24},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","on_message","connect_msg.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module process reception of CONNECT message\n\nuse crate::connection_state::WS2PConnectionState;\nuse crate::controller::meta_datas::Ws2pRemoteNodeDatas;\nuse crate::controller::{WS2PController, WS2PControllerProcessError, WebsocketActionOrder};\nuse crate::websocket::{WebsocketAction, WebsocketMessage};\nuse durs_common_tools::fatal_error;\nuse durs_module::ModuleMessage;\nuse durs_network_documents::NodeFullId;\nuse durs_ws2p_messages::v2::connect::{WS2Pv2ConnectMsg, WS2Pv2ConnectType};\nuse durs_ws2p_messages::v2::payload_container::WS2Pv2MessagePayload;\nuse durs_ws2p_messages::v2::WS2Pv2Message;\nuse log::error;\nuse unwrap::unwrap;\n\n/// Process WS2P v2+ CONNECT Message\npub fn process_ws2p_v2p_connect_msg\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n remote_full_id: NodeFullId,\n connect_msg: \u0026WS2Pv2ConnectMsg,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n log::debug!(\"Receive CONNECT message !\");\n\n // Get remote node datas\n let remote_challenge = connect_msg.challenge;\n let remote_node_datas = Ws2pRemoteNodeDatas {\n challenge: connect_msg.challenge,\n current_blockstamp: None,\n peer_card: None,\n remote_full_id,\n };\n\n if let WS2PConnectionState::WaitingConnectMsg = controller.meta_datas.state {\n // Check remote node datas\n if let WS2Pv2ConnectType::Incoming = controller.meta_datas.connect_type {\n controller.meta_datas.remote_node = Some(remote_node_datas);\n // Get remote_connect_type\n controller.meta_datas.remote_connect_type = Some(WS2Pv2ConnectType::from_flags(\n \u0026connect_msg.flags_queries,\n connect_msg.chunkstamp,\n ));\n } else {\n let expected_full_id = unwrap!(controller.id.expected_remote_full_id());\n if remote_full_id == expected_full_id {\n controller.meta_datas.remote_node = Some(remote_node_datas);\n } else {\n return Ok(super::close_with_reason(\n \"Unexpected PUBKEY or NODE_ID !\",\n WS2PConnectionState::Denial,\n ));\n }\n // Flags not allowed from incoming node\n if !connect_msg.flags_queries.is_empty() {\n super::close_with_reason(\n \"Unexpected CONNECT FLAGS from incoming node. !\",\n WS2PConnectionState::Denial,\n );\n }\n // Get remote_connect_type\n controller.meta_datas.remote_connect_type = Some(WS2Pv2ConnectType::Incoming);\n }\n } else {\n super::close_with_reason(\"Unexpected CONNECT message !\", WS2PConnectionState::Denial);\n }\n\n // Check features compatibility\n match controller\n .meta_datas\n .local_node\n .my_features\n .check_features_compatibility(\u0026connect_msg.api_features)\n {\n Ok(merged_features) =\u003e controller.meta_datas.features = Some(merged_features),\n Err(_) =\u003e {\n super::close_with_reason(\"Unsupported features !\", WS2PConnectionState::Denial);\n }\n }\n\n // Encapsulate and binarize ACK message\n if let Ok((_, bin_ack_msg)) = WS2Pv2Message::encapsulate_payload(\n controller.meta_datas.currency.clone(),\n controller.meta_datas.local_node.my_node_id,\n controller.meta_datas.local_node.my_key_pair,\n WS2Pv2MessagePayload::Ack {\n challenge: remote_challenge,\n },\n ) {\n // Order the sending of a OK message\n Ok(Some(WebsocketActionOrder {\n ws_action: WebsocketAction::SendMessage {\n msg: WebsocketMessage::Bin(bin_ack_msg),\n },\n new_state_if_success: Some(WS2PConnectionState::ConnectMessOk),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n }))\n } else {\n fatal_error!(\"Dev error: Fail to sign own ack message !\")\n }\n}\n","traces":[{"line":32,"address":4809792,"length":1,"stats":{"Line":1}},{"line":40,"address":4810146,"length":1,"stats":{"Line":1}},{"line":41,"address":4810355,"length":1,"stats":{"Line":1}},{"line":42,"address":4810214,"length":1,"stats":{"Line":1}},{"line":43,"address":4810282,"length":1,"stats":{"Line":1}},{"line":44,"address":4810293,"length":1,"stats":{"Line":1}},{"line":45,"address":4810301,"length":1,"stats":{"Line":1}},{"line":48,"address":4810553,"length":1,"stats":{"Line":1}},{"line":50,"address":4810582,"length":1,"stats":{"Line":1}},{"line":51,"address":4810608,"length":1,"stats":{"Line":1}},{"line":53,"address":4810770,"length":1,"stats":{"Line":1}},{"line":54,"address":4813790,"length":1,"stats":{"Line":1}},{"line":55,"address":4813805,"length":1,"stats":{"Line":1}},{"line":58,"address":4810817,"length":1,"stats":{"Line":1}},{"line":59,"address":4810956,"length":1,"stats":{"Line":1}},{"line":60,"address":4810986,"length":1,"stats":{"Line":1}},{"line":62,"address":4811137,"length":1,"stats":{"Line":1}},{"line":64,"address":4811129,"length":1,"stats":{"Line":1}},{"line":68,"address":4811264,"length":1,"stats":{"Line":1}},{"line":71,"address":4811288,"length":1,"stats":{"Line":0}},{"line":75,"address":4811352,"length":1,"stats":{"Line":1}},{"line":78,"address":4811407,"length":1,"stats":{"Line":0}},{"line":82,"address":4811471,"length":1,"stats":{"Line":1}},{"line":86,"address":4811483,"length":1,"stats":{"Line":1}},{"line":88,"address":4811521,"length":1,"stats":{"Line":1}},{"line":90,"address":4811748,"length":1,"stats":{"Line":0}},{"line":95,"address":4812038,"length":1,"stats":{"Line":1}},{"line":96,"address":4814192,"length":1,"stats":{"Line":1}},{"line":97,"address":4811849,"length":1,"stats":{"Line":1}},{"line":98,"address":4811863,"length":1,"stats":{"Line":1}},{"line":99,"address":4811998,"length":1,"stats":{"Line":1}},{"line":100,"address":4811966,"length":1,"stats":{"Line":1}},{"line":104,"address":4812395,"length":1,"stats":{"Line":1}},{"line":105,"address":4812289,"length":1,"stats":{"Line":1}},{"line":106,"address":4812181,"length":1,"stats":{"Line":1}},{"line":108,"address":4812365,"length":1,"stats":{"Line":1}},{"line":109,"address":4812387,"length":1,"stats":{"Line":1}}],"covered":34,"coverable":37},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","on_message","ok_msg.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module process reception of OK message\n\nuse crate::connection_state::WS2PConnectionState;\nuse crate::controller::{\n WS2PController, WS2PControllerEvent, WS2PControllerProcessError, WebsocketActionOrder,\n};\nuse durs_common_tools::fatal_error;\nuse durs_module::ModuleMessage;\nuse durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse log::error;\nuse unwrap::unwrap;\n\n/// Process WS2P v2+ OK Message\npub fn process_ws2p_v2p_ok_msg\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n log::debug!(\"Receive OK message !\");\n\n match controller.meta_datas.state {\n WS2PConnectionState::ConnectMessOk | WS2PConnectionState::SecretFlagsOkWaitingAckMsg =\u003e {\n controller.update_conn_state(WS2PConnectionState::OkMsgOkWaitingAckMsg)?;\n Ok(None)\n }\n WS2PConnectionState::AckMsgOk | WS2PConnectionState::SecretFlagsOk =\u003e {\n controller.meta_datas.state = WS2PConnectionState::Established;\n controller.send_event(WS2PControllerEvent::NewConnEstablished {\n conn_type: if controller.meta_datas.connect_type != WS2Pv2ConnectType::Incoming {\n controller.meta_datas.connect_type\n } else {\n unwrap!(controller.meta_datas.remote_connect_type)\n },\n remote_full_id: if let Some(ref remote_node) = controller.meta_datas.remote_node {\n remote_node.remote_full_id\n } else {\n fatal_error!(\"remote_node must be valued in process_ws2p_v2p_ok_msg() !\")\n },\n })?;\n Ok(None)\n }\n _ =\u003e Ok(super::close_with_reason(\n \"Unexpected OK message !\",\n WS2PConnectionState::Denial,\n )),\n }\n}\n","traces":[{"line":29,"address":4443856,"length":1,"stats":{"Line":1}},{"line":34,"address":4444427,"length":1,"stats":{"Line":0}},{"line":35,"address":4444152,"length":1,"stats":{"Line":2}},{"line":36,"address":4444226,"length":1,"stats":{"Line":0}},{"line":37,"address":4444383,"length":1,"stats":{"Line":0}},{"line":40,"address":4444436,"length":1,"stats":{"Line":2}},{"line":41,"address":4444451,"length":1,"stats":{"Line":2}},{"line":42,"address":4444459,"length":1,"stats":{"Line":2}},{"line":43,"address":4444517,"length":1,"stats":{"Line":1}},{"line":45,"address":4444578,"length":1,"stats":{"Line":1}},{"line":47,"address":4444738,"length":1,"stats":{"Line":2}},{"line":48,"address":4444798,"length":1,"stats":{"Line":2}},{"line":53,"address":4446004,"length":1,"stats":{"Line":2}},{"line":55,"address":4446068,"length":1,"stats":{"Line":0}},{"line":57,"address":4446060,"length":1,"stats":{"Line":0}}],"covered":10,"coverable":15},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","on_message","secret_flags.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module process reception of SECRET_FLAGS message\n\nuse crate::connection_state::WS2PConnectionState;\nuse crate::controller::{WS2PController, WS2PControllerProcessError, WebsocketActionOrder};\n//use durs_common_tools::fatal_error;\nuse durs_module::ModuleMessage;\n//use durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse durs_ws2p_messages::v2::secret_flags::WS2Pv2SecretFlagsMsg;\n//use log::error;\n//use unwrap::unwrap;\n\npub fn process_ws2p_v2p_secret_flags_msg\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n secret_flags: \u0026WS2Pv2SecretFlagsMsg,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n // SECRET_FLAGS informations must never be logged in prod\n #[cfg(test)]\n log::debug!(\"Receive SECRET_FLAGS message !\");\n\n match controller.meta_datas.state {\n WS2PConnectionState::ConnectMessOk =\u003e process(\n controller,\n secret_flags,\n WS2PConnectionState::SecretFlagsOkWaitingAckMsg,\n )\n .map(|_| None),\n WS2PConnectionState::AckMsgOk =\u003e {\n process(controller, secret_flags, WS2PConnectionState::SecretFlagsOk).map(|_| None)\n }\n _ =\u003e Ok(super::close_with_reason(\n \"Unexpected SECRET_FLAGS message !\",\n WS2PConnectionState::Denial,\n )),\n }\n}\n\nfn process\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n _secret_flags: \u0026WS2Pv2SecretFlagsMsg,\n success_state: WS2PConnectionState,\n) -\u003e Result\u003c(), WS2PControllerProcessError\u003e {\n // TODO .. traitement des secrets flags\n controller.update_conn_state(success_state)\n}\n","traces":[{"line":27,"address":4565344,"length":1,"stats":{"Line":0}},{"line":35,"address":4565478,"length":1,"stats":{"Line":0}},{"line":36,"address":4565364,"length":1,"stats":{"Line":0}},{"line":41,"address":4565616,"length":1,"stats":{"Line":0}},{"line":43,"address":4565480,"length":1,"stats":{"Line":0}},{"line":45,"address":4565548,"length":1,"stats":{"Line":0}},{"line":47,"address":4565540,"length":1,"stats":{"Line":0}},{"line":52,"address":4565680,"length":1,"stats":{"Line":0}},{"line":58,"address":4565700,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":9},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","on_message.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Controller process event ws message received\n\nmod ack_msg;\nmod connect_msg;\nmod ok_msg;\nmod secret_flags;\n\nuse super::{WS2PController, WS2PControllerProcessError, WebsocketActionOrder};\nuse crate::connection_state::WS2PConnectionState;\nuse crate::constants;\nuse crate::controller::WS2PControllerEvent;\nuse crate::websocket::{WebsocketAction, WebsocketMessage};\nuse durs_common_tools::fatal_error;\nuse durs_module::ModuleMessage;\nuse durs_network_documents::NodeFullId;\nuse durs_ws2p_messages::v2::payload_container::WS2Pv2MessagePayload;\nuse durs_ws2p_messages::WS2PMessage;\nuse log::error;\nuse std::ops::Deref;\nuse std::thread;\nuse std::time::{Duration, SystemTime};\n\npub fn process\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n msg: WebsocketMessage,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n // Update last_mess_time\n controller.meta_datas.last_mess_time = SystemTime::now();\n\n // Spam ?\n if SystemTime::now()\n .duration_since(controller.meta_datas.last_mess_time)\n .unwrap()\n \u003e Duration::new(*constants::WS2P_SPAM_INTERVAL_IN_MILLI_SECS, 0)\n {\n if controller.meta_datas.spam_interval {\n controller.meta_datas.spam_counter += 1;\n } else {\n controller.meta_datas.spam_interval = true;\n controller.meta_datas.spam_counter = 2;\n }\n } else {\n controller.meta_datas.spam_interval = false;\n controller.meta_datas.spam_counter = 0;\n }\n // Spam ?\n if controller.meta_datas.spam_counter \u003e= *constants::WS2P_SPAM_LIMIT {\n thread::sleep(Duration::from_millis(\n *constants::WS2P_SPAM_SLEEP_TIME_IN_SEC,\n ));\n controller.meta_datas.last_mess_time = SystemTime::now();\n return Ok(None);\n }\n\n if let WebsocketMessage::Bin(bin_msg) = msg {\n log::debug!(\"Receive new bin message there is not a spam !\");\n match WS2PMessage::parse_and_check_bin_message(\u0026bin_msg) {\n Ok(valid_msg) =\u003e match valid_msg {\n WS2PMessage::V2(ref msg_v2) =\u003e {\n match msg_v2.payload {\n WS2Pv2MessagePayload::Connect(ref box_connect_msg) =\u003e {\n let connect_msg = box_connect_msg.deref();\n // Get remote node id\n let remote_full_id =\n NodeFullId(msg_v2.issuer_node_id, msg_v2.issuer_pubkey);\n // Process connect message\n connect_msg::process_ws2p_v2p_connect_msg(\n controller,\n remote_full_id,\n connect_msg,\n )\n }\n WS2Pv2MessagePayload::Ack {\n challenge: ack_msg_challenge,\n } =\u003e {\n // Process ack message\n ack_msg::process_ws2p_v2p_ack_msg(controller, ack_msg_challenge)\n }\n WS2Pv2MessagePayload::SecretFlags(ref secret_flags) =\u003e {\n secret_flags::process_ws2p_v2p_secret_flags_msg(\n controller,\n secret_flags,\n )\n }\n WS2Pv2MessagePayload::Ok(_) =\u003e {\n // Process ok message\n ok_msg::process_ws2p_v2p_ok_msg(controller)\n }\n WS2Pv2MessagePayload::Ko(_) =\u003e Ok(close_with_reason(\n \"Receive Ko message !\",\n WS2PConnectionState::Denial,\n )),\n _ =\u003e {\n if let WS2PConnectionState::Established = controller.meta_datas.state {\n controller\n .send_event(WS2PControllerEvent::RecvValidMsg {\n ws2p_msg: valid_msg,\n })\n .map(|_| None)\n } else {\n Ok(close_with_reason(\n \"Receive datas message on negociation !\",\n WS2PConnectionState::Denial,\n ))\n }\n }\n }\n }\n WS2PMessage::_V0 | WS2PMessage::_V1 =\u003e {\n fatal_error!(\"Dev error: must not use WS2PMessage version \u003c 2 in WS2Pv2+ !\")\n }\n },\n Err(ws2p_msg_err) =\u003e {\n log::warn!(\"Message is invalid : {:?}\", ws2p_msg_err);\n controller.meta_datas.count_invalid_msgs += 1;\n if controller.meta_datas.count_invalid_msgs \u003e= *constants::WS2P_INVALID_MSGS_LIMIT {\n Ok(close_with_reason(\n \"Receive several invalid messages !\",\n WS2PConnectionState::Denial,\n ))\n } else {\n Ok(None)\n }\n }\n }\n } else {\n Ok(close_with_reason(\n \"Receive str message !\",\n WS2PConnectionState::Denial,\n ))\n }\n}\n\nfn close_with_reason(reason: \u0026str, new_state: WS2PConnectionState) -\u003e Option\u003cWebsocketActionOrder\u003e {\n Some(WebsocketActionOrder {\n ws_action: WebsocketAction::CloseConnection {\n reason: Some(reason.to_owned()),\n },\n new_state_if_success: Some(new_state),\n new_state_if_fail: new_state,\n })\n}\n","traces":[{"line":38,"address":4321680,"length":1,"stats":{"Line":1}},{"line":43,"address":4321698,"length":1,"stats":{"Line":1}},{"line":46,"address":4321852,"length":1,"stats":{"Line":1}},{"line":47,"address":4321911,"length":1,"stats":{"Line":1}},{"line":49,"address":4322057,"length":1,"stats":{"Line":1}},{"line":51,"address":4322167,"length":1,"stats":{"Line":0}},{"line":52,"address":4322184,"length":1,"stats":{"Line":0}},{"line":54,"address":4322248,"length":1,"stats":{"Line":0}},{"line":55,"address":4322263,"length":1,"stats":{"Line":0}},{"line":58,"address":4322284,"length":1,"stats":{"Line":1}},{"line":59,"address":4322299,"length":1,"stats":{"Line":1}},{"line":62,"address":4322325,"length":1,"stats":{"Line":1}},{"line":63,"address":4322365,"length":1,"stats":{"Line":0}},{"line":64,"address":4322352,"length":1,"stats":{"Line":0}},{"line":66,"address":4322413,"length":1,"stats":{"Line":0}},{"line":67,"address":4322472,"length":1,"stats":{"Line":0}},{"line":70,"address":4322561,"length":1,"stats":{"Line":1}},{"line":72,"address":4322909,"length":1,"stats":{"Line":1}},{"line":73,"address":4322995,"length":1,"stats":{"Line":1}},{"line":74,"address":4323094,"length":1,"stats":{"Line":1}},{"line":75,"address":4323565,"length":1,"stats":{"Line":1}},{"line":76,"address":4323218,"length":1,"stats":{"Line":1}},{"line":77,"address":4323305,"length":1,"stats":{"Line":1}},{"line":80,"address":4323344,"length":1,"stats":{"Line":1}},{"line":83,"address":4323458,"length":1,"stats":{"Line":1}},{"line":84,"address":4323466,"length":1,"stats":{"Line":1}},{"line":85,"address":4323514,"length":1,"stats":{"Line":1}},{"line":89,"address":4323570,"length":1,"stats":{"Line":1}},{"line":92,"address":4323602,"length":1,"stats":{"Line":1}},{"line":94,"address":4323670,"length":1,"stats":{"Line":0}},{"line":96,"address":4323690,"length":1,"stats":{"Line":0}},{"line":97,"address":4323698,"length":1,"stats":{"Line":0}},{"line":102,"address":4323726,"length":1,"stats":{"Line":1}},{"line":104,"address":4323762,"length":1,"stats":{"Line":0}},{"line":106,"address":4323754,"length":1,"stats":{"Line":0}},{"line":109,"address":4323838,"length":1,"stats":{"Line":0}},{"line":110,"address":4323865,"length":1,"stats":{"Line":0}},{"line":114,"address":4326720,"length":1,"stats":{"Line":0}},{"line":116,"address":4324074,"length":1,"stats":{"Line":0}},{"line":118,"address":4324066,"length":1,"stats":{"Line":0}},{"line":128,"address":4325240,"length":1,"stats":{"Line":0}},{"line":129,"address":4325529,"length":1,"stats":{"Line":0}},{"line":130,"address":4325692,"length":1,"stats":{"Line":0}},{"line":131,"address":4325749,"length":1,"stats":{"Line":0}},{"line":132,"address":4325777,"length":1,"stats":{"Line":0}},{"line":134,"address":4325769,"length":1,"stats":{"Line":0}},{"line":137,"address":4325850,"length":1,"stats":{"Line":0}},{"line":141,"address":4323545,"length":1,"stats":{"Line":0}},{"line":142,"address":4325957,"length":1,"stats":{"Line":0}},{"line":144,"address":4325949,"length":1,"stats":{"Line":0}},{"line":149,"address":4224592,"length":1,"stats":{"Line":1}},{"line":150,"address":4224768,"length":1,"stats":{"Line":1}},{"line":151,"address":4224702,"length":1,"stats":{"Line":1}},{"line":152,"address":4224618,"length":1,"stats":{"Line":1}},{"line":154,"address":4224753,"length":1,"stats":{"Line":1}},{"line":155,"address":4224764,"length":1,"stats":{"Line":1}}],"covered":28,"coverable":56},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller","on_open.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Controller process event ws connection opened\n\nuse super::{\n WS2PConnectionState, WS2PController, WS2PControllerProcessError, WebsocketActionOrder,\n};\nuse crate::websocket::{WebsocketAction, WebsocketMessage};\nuse durs_common_tools::fatal_error;\nuse durs_module::ModuleMessage;\nuse durs_ws2p_messages::v2::connect::generate_connect_message;\nuse durs_ws2p_messages::v2::payload_container::WS2Pv2MessagePayload;\nuse durs_ws2p_messages::v2::WS2Pv2Message;\nuse log::error;\nuse std::net::SocketAddr;\n\npub fn process\u003cM: ModuleMessage\u003e(\n controller: \u0026mut WS2PController\u003cM\u003e,\n remote_addr_opt: Option\u003cSocketAddr\u003e,\n) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n log::debug!(\"open websocket from {}\", print_opt_addr(remote_addr_opt));\n\n // Update connection state\n controller.update_conn_state(WS2PConnectionState::TryToSendConnectMsg)?;\n\n // Generate connect message\n let connect_msg = generate_connect_message(\n controller.meta_datas.connect_type,\n controller.meta_datas.local_node.my_features.clone(),\n controller.meta_datas.challenge,\n None,\n );\n\n // Encapsulate and binarize connect message\n if let Ok((_ws2p_full_msg, bin_connect_msg)) = WS2Pv2Message::encapsulate_payload(\n controller.meta_datas.currency.clone(),\n controller.meta_datas.local_node.my_node_id,\n controller.meta_datas.local_node.my_key_pair,\n WS2Pv2MessagePayload::Connect(Box::new(connect_msg)),\n ) {\n // Order the sending of a CONNECT message\n Ok(Some(WebsocketActionOrder {\n ws_action: WebsocketAction::SendMessage {\n msg: WebsocketMessage::Bin(bin_connect_msg),\n },\n new_state_if_success: Some(WS2PConnectionState::WaitingConnectMsg),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n }))\n } else {\n fatal_error!(\"Dev error: Fail to sign own connect message !\")\n }\n}\n\nfn print_opt_addr(addr: Option\u003cSocketAddr\u003e) -\u003e String {\n match addr {\n Some(addr) =\u003e format!(\"{}\", addr),\n None =\u003e String::from(\"\"),\n }\n}\n","traces":[{"line":30,"address":4309568,"length":1,"stats":{"Line":2}},{"line":34,"address":4309850,"length":1,"stats":{"Line":0}},{"line":37,"address":4310143,"length":1,"stats":{"Line":2}},{"line":40,"address":4310447,"length":1,"stats":{"Line":2}},{"line":41,"address":4310297,"length":1,"stats":{"Line":2}},{"line":42,"address":4310339,"length":1,"stats":{"Line":2}},{"line":43,"address":4310371,"length":1,"stats":{"Line":2}},{"line":44,"address":4310439,"length":1,"stats":{"Line":2}},{"line":48,"address":4310839,"length":1,"stats":{"Line":2}},{"line":49,"address":4310501,"length":1,"stats":{"Line":2}},{"line":50,"address":4310539,"length":1,"stats":{"Line":2}},{"line":51,"address":4310553,"length":1,"stats":{"Line":2}},{"line":52,"address":4310656,"length":1,"stats":{"Line":2}},{"line":55,"address":4311238,"length":1,"stats":{"Line":2}},{"line":56,"address":4311162,"length":1,"stats":{"Line":1}},{"line":57,"address":4311086,"length":1,"stats":{"Line":1}},{"line":59,"address":4311206,"length":1,"stats":{"Line":2}},{"line":60,"address":4311230,"length":1,"stats":{"Line":1}},{"line":67,"address":4221056,"length":1,"stats":{"Line":0}},{"line":68,"address":4221321,"length":1,"stats":{"Line":0}},{"line":69,"address":4221066,"length":1,"stats":{"Line":0}}],"covered":17,"coverable":21},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","controller.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WebSocketToPeer V2+ API Protocol.\n//! Controller manage one WS2P connection.\n\npub mod meta_datas;\nmod on_message;\nmod on_open;\n\nuse self::meta_datas::WS2PControllerMetaDatas;\nuse crate::connection_state::WS2PConnectionState;\nuse crate::constants;\nuse crate::orchestrator::OrchestratorMsg;\nuse crate::websocket::{WebsocketAction, WebsocketIncomingEvent};\nuse durs_module::ModuleMessage;\nuse durs_network_documents::NodeFullId;\nuse durs_ws2p_messages::v2::connect::WS2Pv2ConnectType;\nuse durs_ws2p_messages::WS2PMessage;\nuse failure::Fail;\nuse std::sync::mpsc::{Receiver, SendError, Sender};\nuse std::time::SystemTime;\nuse unwrap::unwrap;\n\n#[derive(Copy, Clone, Debug, Hash)]\n/// WS2P Controller unique identitier\npub enum WS2PControllerId {\n /// Client controller\n Client {\n /// Expected remote node full id\n expected_remote_full_id: Option\u003cNodeFullId\u003e,\n },\n /// Server Incoming controller\n Incoming,\n /// Server outgoing controller\n Outgoing {\n /// Expected remote node full id\n expected_remote_full_id: Option\u003cNodeFullId\u003e,\n },\n}\n\nimpl WS2PControllerId {\n /// Get expected remote node full id\n pub fn expected_remote_full_id(\u0026self) -\u003e Option\u003cNodeFullId\u003e {\n match self {\n WS2PControllerId::Client {\n expected_remote_full_id,\n }\n | WS2PControllerId::Outgoing {\n expected_remote_full_id,\n } =\u003e *expected_remote_full_id,\n WS2PControllerId::Incoming =\u003e None,\n }\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n/// Event transmitted to the orchestrator\npub enum WS2PControllerEvent {\n /// New connection established\n NewConnEstablished {\n /// Connection type\n conn_type: WS2Pv2ConnectType,\n /// Remote node full id\n remote_full_id: NodeFullId,\n },\n /// Connection state change\n StateChange {\n /// New connection state\n new_state: WS2PConnectionState,\n },\n /// The controller only reports a message if it cannot process it entirely on its own.\n /// For example, connection negotiation messages are not sent back.\n RecvValidMsg {\n /// WS2P Message\n ws2p_msg: WS2PMessage,\n },\n}\n\n#[derive(Debug)]\n/// WS2P Controller\npub struct WS2PController\u003cM: ModuleMessage\u003e {\n /// Controller id\n pub id: WS2PControllerId,\n /// Orchestrator sender\n pub orchestrator_sender: Sender\u003cOrchestratorMsg\u003cM\u003e\u003e,\n /// Controller meta datas\n pub meta_datas: WS2PControllerMetaDatas,\n /// Controller receiver\n pub receiver: Receiver\u003cWebsocketActionOrder\u003e,\n}\n\n#[derive(Copy, Clone, Debug, Fail)]\n/// WS2P Controller process error\npub enum WS2PControllerProcessError {\n /// Orchestrator unreacheable\n #[fail(display = \"WS2P Orchestrator unreachable\")]\n OrchestratorUnreacheable,\n}\n\n/// Websocket action order\n#[derive(Clone, Debug)]\npub struct WebsocketActionOrder {\n /// Websocket actio,\n pub ws_action: WebsocketAction,\n /// New state if action success\n pub new_state_if_success: Option\u003cWS2PConnectionState\u003e,\n /// New state if action fail\n pub new_state_if_fail: WS2PConnectionState,\n}\n\nimpl WebsocketActionOrder {\n /// Close connection\n #[inline]\n pub fn close() -\u003e Self {\n WebsocketActionOrder::close_with_reason(None)\n }\n /// Close connection with reason\n #[inline]\n pub fn close_with_reason(reason: Option\u003cString\u003e) -\u003e Self {\n WebsocketActionOrder {\n ws_action: WebsocketAction::CloseConnection { reason },\n new_state_if_success: Some(WS2PConnectionState::Close),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n }\n }\n}\n\nimpl\u003cM: ModuleMessage\u003e WS2PController\u003cM\u003e {\n /// Check timeouts\n pub fn check_timeouts(\u0026mut self) -\u003e Option\u003cWebsocketActionOrder\u003e {\n let now = SystemTime::now();\n\n if self.meta_datas.state == WS2PConnectionState::Established {\n if unwrap!(now.duration_since(self.meta_datas.last_mess_time)).as_secs()\n \u003e *constants::WS2P_EXPIRE_TIMEOUT_IN_SECS\n {\n Some(WebsocketActionOrder {\n ws_action: WebsocketAction::CloseConnection {\n reason: Some(\"Closing due to inactivity.\".to_owned()),\n },\n new_state_if_success: Some(WS2PConnectionState::Close),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n })\n } else {\n None\n }\n } else if unwrap!(now.duration_since(self.meta_datas.creation_time)).as_secs()\n \u003e *constants::WS2P_NEGOTIATION_TIMEOUT_IN_SECS\n {\n Some(WebsocketActionOrder {\n ws_action: WebsocketAction::CloseConnection {\n reason: Some(\"Negociation timeout.\".to_owned()),\n },\n new_state_if_success: Some(WS2PConnectionState::Close),\n new_state_if_fail: WS2PConnectionState::Unreachable,\n })\n } else {\n None\n }\n }\n\n /// Try to instanciate new controller\n pub fn try_new(\n id: WS2PControllerId,\n meta_datas: WS2PControllerMetaDatas,\n orchestrator_sender: Sender\u003cOrchestratorMsg\u003cM\u003e\u003e,\n ) -\u003e Result\u003cWS2PController\u003cM\u003e, SendError\u003cOrchestratorMsg\u003cM\u003e\u003e\u003e {\n let (sender, receiver) = std::sync::mpsc::channel();\n\n orchestrator_sender.send(OrchestratorMsg::ControllerSender(sender))?;\n\n Ok(WS2PController {\n id,\n meta_datas,\n orchestrator_sender,\n receiver,\n })\n }\n\n /// Get orchestrator sender\n pub fn get_pending_ws_actions(\u0026self) -\u003e Vec\u003cWebsocketActionOrder\u003e {\n let mut ws_actions = Vec::new();\n\n while let Ok(ws_action) = self.receiver.recv() {\n ws_actions.push(ws_action);\n }\n\n ws_actions\n }\n\n /// Process a websocket incoming event\n pub fn process(\n \u0026mut self,\n event: WebsocketIncomingEvent,\n ) -\u003e Result\u003cOption\u003cWebsocketActionOrder\u003e, WS2PControllerProcessError\u003e {\n match event {\n WebsocketIncomingEvent::OnOpen { remote_addr } =\u003e on_open::process(self, remote_addr),\n WebsocketIncomingEvent::OnMessage { msg } =\u003e on_message::process(self, msg),\n WebsocketIncomingEvent::OnClose { close_code, reason } =\u003e {\n let remote_str = if let Some(remote_node) = \u0026self.meta_datas.remote_node {\n remote_node.remote_full_id.to_string()\n } else {\n \"unknow\".to_owned()\n };\n log::warn!(\n \"Connection with remote '{}' closed (close_code={}, reason={}).\",\n remote_str,\n close_code,\n reason.unwrap_or_else(|| \"\".to_owned())\n );\n self.update_conn_state(WS2PConnectionState::Close)?;\n Ok(None)\n }\n }\n }\n\n fn send_event(\u0026mut self, event: WS2PControllerEvent) -\u003e Result\u003c(), WS2PControllerProcessError\u003e {\n if self\n .orchestrator_sender\n .send(OrchestratorMsg::ControllerEvent {\n controller_id: self.id,\n event,\n })\n .is_err()\n \u0026\u0026 self.meta_datas.state != WS2PConnectionState::Close\n {\n Err(WS2PControllerProcessError::OrchestratorUnreacheable)\n } else {\n Ok(())\n }\n }\n\n #[inline]\n /// Update connection state\n pub fn update_conn_state(\n \u0026mut self,\n new_state: WS2PConnectionState,\n ) -\u003e Result\u003c(), WS2PControllerProcessError\u003e {\n self.meta_datas.state = new_state;\n self.send_event(WS2PControllerEvent::StateChange { new_state })\n }\n}\n","traces":[{"line":56,"address":null,"length":0,"stats":{"Line":1}},{"line":57,"address":null,"length":0,"stats":{"Line":1}},{"line":58,"address":null,"length":0,"stats":{"Line":1}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":1}},{"line":62,"address":null,"length":0,"stats":{"Line":1}},{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":1}},{"line":132,"address":null,"length":0,"stats":{"Line":1}},{"line":134,"address":null,"length":0,"stats":{"Line":1}},{"line":135,"address":null,"length":0,"stats":{"Line":1}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":188,"address":null,"length":0,"stats":{"Line":1}},{"line":189,"address":null,"length":0,"stats":{"Line":1}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":197,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":205,"address":null,"length":0,"stats":{"Line":2}},{"line":209,"address":null,"length":0,"stats":{"Line":1}},{"line":210,"address":null,"length":0,"stats":{"Line":2}},{"line":211,"address":null,"length":0,"stats":{"Line":1}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":230,"address":null,"length":0,"stats":{"Line":2}},{"line":231,"address":null,"length":0,"stats":{"Line":2}},{"line":232,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":2}},{"line":234,"address":null,"length":0,"stats":{"Line":2}},{"line":235,"address":null,"length":0,"stats":{"Line":2}},{"line":237,"address":null,"length":0,"stats":{"Line":0}},{"line":238,"address":null,"length":0,"stats":{"Line":1}},{"line":240,"address":null,"length":0,"stats":{"Line":1}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":242,"address":null,"length":0,"stats":{"Line":2}},{"line":248,"address":null,"length":0,"stats":{"Line":2}},{"line":252,"address":null,"length":0,"stats":{"Line":2}},{"line":253,"address":null,"length":0,"stats":{"Line":2}}],"covered":34,"coverable":76},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p","ws2p-protocol","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WebSocketToPeer V2+ API Protocol.\n\n#![allow(clippy::large_enum_variant)]\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\npub mod connection_state;\npub mod constants;\npub mod controller;\npub mod orchestrator;\npub mod websocket;\n\nuse dup_crypto::keys::{KeyPair, KeyPairEnum};\nuse durs_network_documents::{NodeFullId, NodeId};\nuse durs_ws2p_messages::v2::api_features::WS2PFeatures;\n\n/// Store self WS2P properties\n#[derive(Debug, Clone, PartialEq)]\npub struct MySelfWs2pNode {\n /// Local node id\n pub my_node_id: NodeId,\n /// Local network keypair\n pub my_key_pair: KeyPairEnum,\n /// Local node WWS2PFeatures\n pub my_features: WS2PFeatures,\n}\n\nimpl MySelfWs2pNode {\n /// Get self node full id\n pub fn get_full_id(\u0026self) -\u003e NodeFullId {\n NodeFullId(self.my_node_id, self.my_key_pair.public_key())\n }\n}\n","traces":[{"line":54,"address":null,"length":0,"stats":{"Line":1}},{"line":55,"address":null,"length":0,"stats":{"Line":1}}],"covered":2,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ack_message.rs"],"content":"use crate::*;\nuse dup_crypto::keys::*;\nuse serde::ser::{Serialize, SerializeStruct, Serializer};\n\n#[derive(Debug, Clone)]\npub struct WS2PAckMessageV1 {\n pub currency: String,\n pub pubkey: PubKey,\n pub challenge: String,\n pub signature: Option\u003cSig\u003e,\n}\n\nimpl WS2PMessage for WS2PAckMessageV1 {\n fn parse(v: \u0026serde_json::Value, currency: String) -\u003e Result\u003cSelf, WS2PMsgParseErr\u003e {\n let pubkey = match v.get(\"pub\") {\n Some(pubkey) =\u003e pubkey.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),\n None =\u003e return Err(WS2PMsgParseErr {}),\n };\n let signature = match v.get(\"sig\") {\n Some(signature) =\u003e signature.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),\n None =\u003e return Err(WS2PMsgParseErr {}),\n };\n let pubkey = PubKey::Ed25519(ed25519::PublicKey::from_base58(\u0026pubkey)?);\n let signature: Option\u003cSig\u003e =\n Some(Sig::Ed25519(ed25519::Signature::from_base64(\u0026signature)?));\n Ok(WS2PAckMessageV1 {\n currency,\n pubkey,\n challenge: \"\".to_string(),\n signature,\n })\n }\n fn to_raw(\u0026self) -\u003e String {\n format!(\n \"WS2P:ACK:{}:{}:{}\",\n self.currency, self.pubkey, self.challenge\n )\n }\n fn verify(\u0026self) -\u003e bool {\n if let Some(sig) = self.signature {\n self.pubkey.verify(self.to_raw().as_bytes(), \u0026sig).is_ok()\n } else {\n false\n }\n }\n}\n\nimpl Serialize for WS2PAckMessageV1 {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n let mut connect_message_in_json = serializer.serialize_struct(\"message\", 3)?;\n connect_message_in_json.serialize_field(\"auth\", \"ACK\")?;\n connect_message_in_json.serialize_field(\"pub\", \u0026self.pubkey.to_string())?;\n connect_message_in_json.serialize_field(\n \"sig\",\n \u0026self\n .signature\n .expect(\"Fail to serialize ACK message : the signature field is set to None !\")\n .to_string(),\n )?;\n connect_message_in_json.end()\n }\n}\n","traces":[{"line":14,"address":null,"length":0,"stats":{"Line":0}},{"line":15,"address":null,"length":0,"stats":{"Line":0}},{"line":16,"address":null,"length":0,"stats":{"Line":0}},{"line":17,"address":null,"length":0,"stats":{"Line":0}},{"line":19,"address":null,"length":0,"stats":{"Line":0}},{"line":20,"address":null,"length":0,"stats":{"Line":0}},{"line":21,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":26,"address":null,"length":0,"stats":{"Line":0}},{"line":27,"address":null,"length":0,"stats":{"Line":0}},{"line":28,"address":null,"length":0,"stats":{"Line":0}},{"line":29,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":34,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":5937152,"length":1,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":34},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","connect_message.rs"],"content":"use crate::*;\nuse dup_crypto::keys::*;\nuse serde::ser::{Serialize, SerializeStruct, Serializer};\n\n#[derive(Debug, Clone)]\npub struct WS2PConnectMessageV1 {\n pub currency: String,\n pub pubkey: PubKey,\n pub challenge: String,\n pub signature: Option\u003cSig\u003e,\n}\n\nimpl WS2PMessage for WS2PConnectMessageV1 {\n fn parse(v: \u0026serde_json::Value, currency: String) -\u003e Result\u003cSelf, WS2PMsgParseErr\u003e {\n let pubkey = match v.get(\"pub\") {\n Some(pubkey) =\u003e pubkey.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),\n None =\u003e return Err(WS2PMsgParseErr {}),\n };\n let challenge = match v.get(\"challenge\") {\n Some(challenge) =\u003e challenge.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),\n None =\u003e return Err(WS2PMsgParseErr {}),\n };\n let signature = match v.get(\"sig\") {\n Some(signature) =\u003e signature.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),\n None =\u003e return Err(WS2PMsgParseErr {}),\n };\n let pubkey = PubKey::Ed25519(ed25519::PublicKey::from_base58(\u0026pubkey)?);\n let signature = Some(Sig::Ed25519(ed25519::Signature::from_base64(\u0026signature)?));\n Ok(WS2PConnectMessageV1 {\n currency,\n pubkey,\n challenge,\n signature,\n })\n }\n fn to_raw(\u0026self) -\u003e String {\n format!(\n \"WS2P:CONNECT:{}:{}:{}\",\n self.currency, self.pubkey, self.challenge\n )\n }\n fn verify(\u0026self) -\u003e bool {\n if let Some(sig) = self.signature {\n self.pubkey.verify(self.to_raw().as_bytes(), \u0026sig).is_ok()\n } else {\n false\n }\n }\n}\n\nimpl Serialize for WS2PConnectMessageV1 {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n let mut connect_message_in_json = serializer.serialize_struct(\"message\", 4)?;\n connect_message_in_json.serialize_field(\"auth\", \"CONNECT\")?;\n connect_message_in_json.serialize_field(\"pub\", \u0026self.pubkey.to_string())?;\n connect_message_in_json.serialize_field(\"challenge\", \u0026self.challenge)?;\n connect_message_in_json.serialize_field(\n \"sig\",\n \u0026self\n .signature\n .expect(\"Fail to serialize CONNECT message : the signature field is set to None !\")\n .to_string(),\n )?;\n connect_message_in_json.end()\n }\n}\n","traces":[{"line":14,"address":null,"length":0,"stats":{"Line":0}},{"line":15,"address":null,"length":0,"stats":{"Line":0}},{"line":16,"address":null,"length":0,"stats":{"Line":0}},{"line":17,"address":null,"length":0,"stats":{"Line":0}},{"line":19,"address":null,"length":0,"stats":{"Line":0}},{"line":20,"address":null,"length":0,"stats":{"Line":0}},{"line":21,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":27,"address":null,"length":0,"stats":{"Line":0}},{"line":28,"address":null,"length":0,"stats":{"Line":0}},{"line":29,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":31,"address":null,"length":0,"stats":{"Line":0}},{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":0}},{"line":37,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":4521424,"length":1,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":37},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","events","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing events received from other durs modules\n\nuse crate::serializers::IntoWS2Pv1Json;\nuse crate::*;\nuse dubp_documents::Document;\nuse durs_message::events::DursEvent;\nuse durs_module::*;\nuse std::ops::Deref;\n\npub fn receive_event(\n ws2p_module: \u0026mut WS2Pv1Module,\n _event_type: ModuleEvent,\n event_content: \u0026DursEvent,\n) {\n if let DursEvent::BlockchainEvent(ref bc_event) = *event_content {\n match *bc_event.deref() {\n BlockchainEvent::StackUpValidBlock(ref block) =\u003e {\n ws2p_module.current_blockstamp = block.deref().blockstamp();\n debug!(\n \"WS2Pv1Module : current_blockstamp = {}\",\n ws2p_module.current_blockstamp\n );\n ws2p_module.my_head = Some(heads::generate_my_head(\n \u0026ws2p_module.key_pair,\n ws2p_module.node_id,\n ws2p_module.soft_name,\n ws2p_module.soft_version,\n \u0026ws2p_module.current_blockstamp,\n None,\n ));\n super::sent::send_network_event(\n ws2p_module,\n NetworkEvent::ReceiveHeads(vec![unwrap!(ws2p_module.my_head.clone())]),\n );\n // Send my head to all connections\n let my_json_head = unwrap!(ws2p_module.my_head.clone()).into_ws2p_v1_json();\n trace!(\"Send my HEAD: {:#?}\", my_json_head);\n let _results: Result\u003c(), ws::Error\u003e = ws2p_module\n .websockets\n .iter_mut()\n .map(|ws| {\n (ws.1).0.send(Message::text(\n json!({\n \"name\": \"HEAD\",\n \"body\": {\n \"heads\": [my_json_head]\n }\n })\n .to_string(),\n ))\n })\n .collect();\n }\n BlockchainEvent::RevertBlocks(ref _blocks) =\u003e {}\n _ =\u003e {}\n }\n }\n}\n","traces":[{"line":25,"address":6050048,"length":1,"stats":{"Line":0}},{"line":30,"address":6050081,"length":1,"stats":{"Line":0}},{"line":31,"address":6050135,"length":1,"stats":{"Line":0}},{"line":32,"address":6050164,"length":1,"stats":{"Line":0}},{"line":33,"address":6050246,"length":1,"stats":{"Line":0}},{"line":34,"address":6050343,"length":1,"stats":{"Line":0}},{"line":36,"address":6050530,"length":1,"stats":{"Line":0}},{"line":38,"address":6050829,"length":1,"stats":{"Line":0}},{"line":39,"address":6050722,"length":1,"stats":{"Line":0}},{"line":40,"address":6050736,"length":1,"stats":{"Line":0}},{"line":41,"address":6050750,"length":1,"stats":{"Line":0}},{"line":42,"address":6050772,"length":1,"stats":{"Line":0}},{"line":43,"address":6050794,"length":1,"stats":{"Line":0}},{"line":44,"address":6050809,"length":1,"stats":{"Line":0}},{"line":47,"address":6052010,"length":1,"stats":{"Line":0}},{"line":48,"address":6050946,"length":1,"stats":{"Line":0}},{"line":51,"address":6051213,"length":1,"stats":{"Line":0}},{"line":52,"address":6051370,"length":1,"stats":{"Line":0}},{"line":53,"address":6051767,"length":1,"stats":{"Line":0}},{"line":56,"address":4287456,"length":1,"stats":{"Line":0}},{"line":57,"address":4287481,"length":1,"stats":{"Line":0}},{"line":58,"address":4287510,"length":1,"stats":{"Line":0}},{"line":69,"address":6051907,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":23},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","events","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the events emitted by the blockchain module.\n\nuse crate::constants;\nuse crate::WS2Pv1Module;\nuse dubp_documents::documents::UserDocumentDUBP;\nuse durs_message::events::DursEvent;\nuse durs_message::*;\nuse durs_module::{ModuleEvent, ModuleStaticName, RouterThreadMessage};\nuse durs_network::events::NetworkEvent;\n\npub fn send_network_events(ws2p_module: \u0026mut WS2Pv1Module, events: Vec\u003cNetworkEvent\u003e) {\n for event in events {\n send_network_event(ws2p_module, event);\n }\n}\n\npub fn send_network_event(ws2p_module: \u0026mut WS2Pv1Module, event: NetworkEvent) {\n let module_event = match event {\n NetworkEvent::ConnectionStateChange(_, _, _, _) =\u003e {\n ModuleEvent::ConnectionsChangeNodeNetwork\n }\n NetworkEvent::NewSelfPeer(_) =\u003e ModuleEvent::NewSelfPeer,\n NetworkEvent::ReceiveBlocks(_) =\u003e ModuleEvent::NewBlockFromNetwork,\n NetworkEvent::ReceiveDocuments(ref network_docs) =\u003e {\n if !network_docs.is_empty() {\n match network_docs[0] {\n UserDocumentDUBP::Transaction(_) =\u003e ModuleEvent::NewTxFromNetwork,\n _ =\u003e ModuleEvent::NewWotDocFromNetwork,\n }\n } else {\n return;\n }\n }\n NetworkEvent::ReceiveHeads(_) =\u003e ModuleEvent::NewValidHeadFromNetwork,\n NetworkEvent::ReceivePeers(_) =\u003e ModuleEvent::NewValidPeerFromNodeNetwork,\n NetworkEvent::SyncEvent(_) =\u003e ModuleEvent::SyncEvent,\n };\n ws2p_module\n .router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Event {\n event_from: ModuleStaticName(constants::MODULE_NAME),\n event_type: module_event,\n event_content: DursEvent::NetworkEvent(event),\n }))\n .expect(\"Fail to send network event to router !\");\n}\n","traces":[{"line":26,"address":4327552,"length":1,"stats":{"Line":0}},{"line":27,"address":4327564,"length":1,"stats":{"Line":0}},{"line":28,"address":4328107,"length":1,"stats":{"Line":0}},{"line":32,"address":4328224,"length":1,"stats":{"Line":0}},{"line":33,"address":4328236,"length":1,"stats":{"Line":0}},{"line":34,"address":4328252,"length":1,"stats":{"Line":0}},{"line":35,"address":4328318,"length":1,"stats":{"Line":0}},{"line":37,"address":4328331,"length":1,"stats":{"Line":0}},{"line":38,"address":4328344,"length":1,"stats":{"Line":0}},{"line":39,"address":4328362,"length":1,"stats":{"Line":0}},{"line":40,"address":4328374,"length":1,"stats":{"Line":0}},{"line":41,"address":4328405,"length":1,"stats":{"Line":0}},{"line":42,"address":4328434,"length":1,"stats":{"Line":0}},{"line":43,"address":4328450,"length":1,"stats":{"Line":0}},{"line":49,"address":4328486,"length":1,"stats":{"Line":0}},{"line":50,"address":4328496,"length":1,"stats":{"Line":0}},{"line":51,"address":4328506,"length":1,"stats":{"Line":0}},{"line":53,"address":4328514,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":18},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","heads.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::*;\nuse dubp_documents::Blockstamp;\nuse durs_network_documents::network_head_v2::*;\n\npub fn generate_my_head(\n network_keypair: \u0026KeyPairEnum,\n node_id: NodeId,\n soft_name: \u0026str,\n soft_version: \u0026str,\n my_current_blockstamp: \u0026Blockstamp,\n my_uid: Option\u003cString\u003e,\n) -\u003e NetworkHead {\n let message = NetworkHeadMessage::V2(NetworkHeadMessageV2 {\n api: String::from(\"WS2POCA\"),\n version: 1,\n pubkey: network_keypair.public_key(),\n blockstamp: *my_current_blockstamp,\n node_uuid: node_id,\n software: String::from(soft_name),\n soft_version: String::from(soft_version),\n prefix: 1,\n free_member_room: None,\n free_mirror_room: None,\n });\n let message_v2 = NetworkHeadMessage::V2(NetworkHeadMessageV2 {\n api: String::from(\"WS2POCA\"),\n version: 2,\n pubkey: network_keypair.public_key(),\n blockstamp: *my_current_blockstamp,\n node_uuid: node_id,\n software: String::from(soft_name),\n soft_version: String::from(soft_version),\n prefix: 1,\n free_member_room: Some(0),\n free_mirror_room: Some(0),\n });\n NetworkHead::V2(Box::new(NetworkHeadV2 {\n message: message.clone(),\n sig: network_keypair\n .private_key()\n .sign(message.to_string().as_bytes()),\n message_v2: message_v2.clone(),\n sig_v2: network_keypair\n .private_key()\n .sign(message_v2.to_string().as_bytes()),\n step: 0,\n uid: my_uid,\n }))\n}\n","traces":[{"line":20,"address":4376816,"length":1,"stats":{"Line":0}},{"line":28,"address":4376886,"length":1,"stats":{"Line":0}},{"line":29,"address":4376902,"length":1,"stats":{"Line":0}},{"line":31,"address":4376957,"length":1,"stats":{"Line":0}},{"line":32,"address":4376984,"length":1,"stats":{"Line":0}},{"line":33,"address":4377025,"length":1,"stats":{"Line":0}},{"line":34,"address":4377032,"length":1,"stats":{"Line":0}},{"line":35,"address":4377085,"length":1,"stats":{"Line":0}},{"line":37,"address":4377116,"length":1,"stats":{"Line":0}},{"line":38,"address":4377128,"length":1,"stats":{"Line":0}},{"line":40,"address":4377733,"length":1,"stats":{"Line":0}},{"line":41,"address":4377461,"length":1,"stats":{"Line":0}},{"line":43,"address":4377511,"length":1,"stats":{"Line":0}},{"line":44,"address":4377556,"length":1,"stats":{"Line":0}},{"line":45,"address":4377597,"length":1,"stats":{"Line":0}},{"line":46,"address":4377604,"length":1,"stats":{"Line":0}},{"line":47,"address":4377654,"length":1,"stats":{"Line":0}},{"line":49,"address":4377685,"length":1,"stats":{"Line":0}},{"line":50,"address":4377709,"length":1,"stats":{"Line":0}},{"line":52,"address":4378583,"length":1,"stats":{"Line":0}},{"line":53,"address":4378075,"length":1,"stats":{"Line":0}},{"line":54,"address":4378100,"length":1,"stats":{"Line":0}},{"line":56,"address":4378145,"length":1,"stats":{"Line":0}},{"line":57,"address":4378328,"length":1,"stats":{"Line":0}},{"line":58,"address":4378335,"length":1,"stats":{"Line":0}},{"line":60,"address":4378362,"length":1,"stats":{"Line":0}},{"line":62,"address":4378544,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":27},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WebSocketToPeer API for the Dunitrust project.\n\n#![allow(clippy::large_enum_variant, clippy::identity_conversion)]\n#![deny(\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n#![recursion_limit = \"256\"]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate serde_json;\n#[macro_use]\nextern crate structopt;\n\nmod ack_message;\nmod connect_message;\npub mod constants;\nmod events;\nmod heads;\nmod ok_message;\nmod requests;\nmod responses;\npub mod serializers;\nmod subcommands;\npub mod ws2p_db;\npub mod ws_connections;\n\nuse crate::ack_message::WS2PAckMessageV1;\nuse crate::connect_message::WS2PConnectMessageV1;\nuse crate::constants::*;\nuse crate::ok_message::WS2POkMessageV1;\nuse crate::requests::sent::send_dal_request;\nuse crate::subcommands::WS2PSubCommands;\nuse crate::ws2p_db::DbEndpoint;\nuse crate::ws_connections::messages::WS2Pv1Msg;\nuse crate::ws_connections::requests::{WS2Pv1ReqBody, WS2Pv1ReqFullId, WS2Pv1ReqId, WS2Pv1Request};\nuse crate::ws_connections::states::WS2PConnectionState;\nuse crate::ws_connections::*;\nuse dubp_documents::documents::block::BlockDocument;\nuse dubp_documents::documents::UserDocumentDUBP;\nuse dubp_documents::Blockstamp;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse durs_common_tools::traits::merge::Merge;\nuse durs_conf::DuRsConf;\nuse durs_message::events::*;\nuse durs_message::requests::*;\nuse durs_message::responses::*;\nuse durs_message::*;\nuse durs_module::*;\nuse durs_network::cli::sync::SyncOpt;\nuse durs_network::events::*;\nuse durs_network::requests::*;\nuse durs_network::*;\nuse durs_network_documents::network_endpoint::*;\nuse durs_network_documents::network_head::*;\nuse durs_network_documents::*;\nuse failure::Fail;\nuse maplit::hashset;\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse std::fs;\nuse std::ops::Deref;\nuse std::path::PathBuf;\nuse std::str::FromStr;\nuse std::sync::mpsc;\nuse std::thread;\nuse std::time::{Duration, SystemTime, UNIX_EPOCH};\nuse unwrap::unwrap;\nuse ws::{CloseCode, Message};\n\n#[inline]\n#[cfg(not(feature = \"ssl\"))]\npub fn ssl() -\u003e bool {\n false\n}\n#[inline]\n#[cfg(feature = \"ssl\")]\npub fn ssl() -\u003e bool {\n true\n}\n\n#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]\n/// WS2P Configuration\npub struct WS2PUserConf {\n /// Limit of outcoming connections\n pub outcoming_quota: Option\u003cusize\u003e,\n /// List of prefered public keys\n pub prefered_pubkeys: Option\u003cHashSet\u003cString\u003e\u003e,\n /// Default WS2P endpoints provides by configuration file\n pub sync_endpoints: Option\u003cVec\u003cEndpointV1\u003e\u003e,\n}\n\nimpl Merge for WS2PUserConf {\n fn merge(self, other: Self) -\u003e Self {\n WS2PUserConf {\n outcoming_quota: self.outcoming_quota.or(other.outcoming_quota),\n prefered_pubkeys: self.prefered_pubkeys.or(other.prefered_pubkeys),\n sync_endpoints: self.sync_endpoints.or(other.sync_endpoints),\n }\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq)]\n/// WS2P Configuration\npub struct WS2PConf {\n /// Currency name\n pub currency: Option\u003cCurrencyName\u003e,\n /// Limit of outcoming connections\n pub outcoming_quota: usize,\n /// List of prefered public keys\n pub prefered_pubkeys: HashSet\u003cPubKey\u003e,\n /// Default WS2P endpoints provides by configuration file\n pub sync_endpoints: Vec\u003cEndpointV1\u003e,\n}\n\nimpl Default for WS2PConf {\n fn default() -\u003e Self {\n WS2PConf {\n currency: None,\n outcoming_quota: *WS2P_DEFAULT_OUTCOMING_QUOTA,\n prefered_pubkeys: HashSet::new(),\n sync_endpoints: vec![\n unwrap!(EndpointV1::parse_from_raw(\n \"WS2P c1c39a0a ts.g1.librelois.fr 443 /ws2p\",\n PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(\n \"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\",\n )),),\n 0,\n 0,\n )),\n unwrap!(EndpointV1::parse_from_raw(\n \"WS2P fb17fcd4 g1.duniter.fr 443 /ws2p\",\n PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(\n \"38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE\",\n ))),\n 0,\n 0,\n )),\n unwrap!(EndpointV1::parse_from_raw(\n \"WS2P 7b33becd g1.nordstrom.duniter.org 443 /ws2p\",\n PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(\n \"DWoSCRLQyQ48dLxUGr1MDKg4NFcbPbC56LN2hJjCCPpZ\",\n ))),\n 0,\n 0,\n )),\n unwrap!(EndpointV1::parse_from_raw(\n \"WS2P dff60418 duniter.normandie-libre.fr 443 /ws2p\",\n PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(\n \"8t6Di3pLxxoTEfjXHjF49pNpjSTXuGEQ6BpkT75CkNb2\",\n ))),\n 0,\n 0,\n )),\n ],\n }\n }\n}\n\n#[derive(Debug)]\n/// Store a Signal receive from network (after message treatment)\npub enum WS2PSignal {\n Blocks(NodeFullId, Vec\u003cBlockDocument\u003e),\n /// A new connection is successfully established with `NodeFullId`.\n ConnectionEstablished(NodeFullId),\n Empty,\n Heads(NodeFullId, Vec\u003cNetworkHead\u003e),\n NegociationTimeout(NodeFullId),\n NoConnection,\n PeerCard(NodeFullId, serde_json::Value, Vec\u003cEndpointV1\u003e),\n ReqResponse(\n ModuleReqFullId,\n WS2Pv1ReqBody,\n NodeFullId,\n serde_json::Value,\n ),\n Request {\n from: NodeFullId,\n req_id: WS2Pv1ReqId,\n body: WS2Pv1ReqBody,\n },\n Timeout(NodeFullId),\n UserDocuments(NodeFullId, Vec\u003cUserDocumentDUBP\u003e),\n /// Receive a websocket error from a connextion. `NodeFullId` store the identifier of connection.\n WSError(NodeFullId),\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub enum NetworkConsensusError {\n InsufficientData(usize),\n Fork,\n}\n\n#[derive(Debug)]\npub enum SendRequestError {\n RequestTypeMustNotBeTransmitted(),\n WSError(usize, Vec\u003cws::Error\u003e),\n}\n\n#[derive(Debug)]\npub struct WS2Pv1Module {\n pub conf: WS2PConf,\n pub count_dal_requests: u32,\n pub current_blockstamp: Blockstamp,\n pub ep_file_path: PathBuf,\n pub heads_cache: HashMap\u003cNodeFullId, NetworkHead\u003e,\n pub key_pair: KeyPairEnum,\n pub main_thread_channel: (\n mpsc::Sender\u003cWS2PThreadSignal\u003e,\n mpsc::Receiver\u003cWS2PThreadSignal\u003e,\n ),\n pub my_head: Option\u003cNetworkHead\u003e,\n pub next_receiver: usize,\n pub node_id: NodeId,\n pub pending_received_requests: HashMap\u003cModuleReqId, WS2Pv1ReqFullId\u003e,\n pub requests_awaiting_response: HashMap\u003cWS2Pv1ReqId, WS2Pv1PendingReqInfos\u003e,\n pub router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n pub soft_name: \u0026'static str,\n pub soft_version: \u0026'static str,\n pub ssl: bool,\n pub websockets: HashMap\u003cNodeFullId, WsSender\u003e,\n pub ws2p_endpoints: HashMap\u003cNodeFullId, DbEndpoint\u003e,\n pub uids_cache: HashMap\u003cPubKey, String\u003e,\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct WS2Pv1PendingReqInfos {\n requester_module: ModuleReqFullId,\n req_body: WS2Pv1ReqBody,\n recipient_node: NodeFullId,\n timestamp: SystemTime,\n}\n\nimpl WS2Pv1Module {\n pub fn new(\n soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n conf: WS2PConf,\n ep_file_path: PathBuf,\n key_pair: KeyPairEnum,\n router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) -\u003e WS2Pv1Module {\n WS2Pv1Module {\n router_sender,\n key_pair,\n current_blockstamp: Blockstamp::default(),\n conf,\n ep_file_path,\n soft_name: soft_meta_datas.soft_name,\n soft_version: soft_meta_datas.soft_version,\n ssl: ssl(),\n node_id: NodeId(soft_meta_datas.conf.my_node_id()),\n main_thread_channel: mpsc::channel(),\n next_receiver: 0,\n pending_received_requests: HashMap::new(),\n ws2p_endpoints: HashMap::new(),\n websockets: HashMap::new(),\n requests_awaiting_response: HashMap::new(),\n heads_cache: HashMap::new(),\n my_head: None,\n uids_cache: HashMap::new(),\n count_dal_requests: 0,\n }\n }\n}\n\n#[derive(Debug)]\npub enum WS2PThreadSignal {\n DursMsg(Box\u003cDursMsg\u003e),\n WS2Pv1Msg(WS2Pv1Msg),\n}\n\n#[derive(Copy, Clone, Debug)]\n/// Error when parsing WS2P message\npub struct WS2PMsgParseErr {}\n\nimpl From\u003cdup_crypto::bases::BaseConvertionError\u003e for WS2PMsgParseErr {\n fn from(_: dup_crypto::bases::BaseConvertionError) -\u003e Self {\n WS2PMsgParseErr {}\n }\n}\n\npub trait WS2PMessage: Sized {\n fn parse(v: \u0026serde_json::Value, currency: String) -\u003e Result\u003cSelf, WS2PMsgParseErr\u003e;\n fn to_raw(\u0026self) -\u003e String;\n fn sign(\u0026self, key_pair: KeyPairEnum) -\u003e Sig {\n key_pair.sign(self.to_raw().as_bytes())\n }\n fn verify(\u0026self) -\u003e bool;\n //fn parse_and_verify(v: serde_json::Value, currency: String) -\u003e bool;\n}\n\n#[derive(Debug)]\n/// WS2PFeaturesParseError\npub enum WS2PFeaturesParseError {\n /// UnknowApiFeature\n UnknowApiFeature(String),\n}\n\nimpl ApiModule\u003cDuRsConf, DursMsg\u003e for WS2Pv1Module {\n type ParseErr = WS2PFeaturesParseError;\n /// Parse raw api features\n fn parse_raw_api_features(str_features: \u0026str) -\u003e Result\u003cApiFeatures, Self::ParseErr\u003e {\n let str_features: Vec\u003c\u0026str\u003e = str_features.split(' ').collect();\n let mut api_features = Vec::with_capacity(0);\n for str_feature in str_features {\n match str_feature {\n \"DEF\" =\u003e api_features[0] += 1u8,\n \"LOW\" =\u003e api_features[0] += 2u8,\n \"ABF\" =\u003e api_features[0] += 4u8,\n _ =\u003e {\n return Err(WS2PFeaturesParseError::UnknowApiFeature(String::from(\n str_feature,\n )));\n }\n }\n }\n Ok(ApiFeatures(api_features))\n }\n}\n\nimpl NetworkModule\u003cDuRsConf, DursMsg\u003e for WS2Pv1Module {\n fn sync(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n _conf: WS2PConf,\n _main_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n _sync_params: SyncOpt,\n ) -\u003e Result\u003c(), SyncError\u003e {\n println!(\"Downlaod blockchain from network...\");\n println!(\"Error : not yet implemented !\");\n Ok(())\n }\n}\n\n#[derive(Clone, Debug, StructOpt)]\n#[structopt(\n name = \"ws2p\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n)]\n/// WS2P1 subcommand options\npub struct WS2POpt {\n /// Ws2p1 subcommands\n #[structopt(subcommand)]\n pub subcommand: WS2PSubCommands,\n}\n\nmacro_rules! fields_overload {\n ($struct:ident; $option_struct:ident; [$($field:ident),+]) =\u003e {{\n $(if let Some($field) = $option_struct.$field {\n $struct.$field = $field;\n })+\n }};\n}\n\n#[derive(Clone, Debug, Fail)]\nenum WS2Pv1Error {\n #[fail(display = \"WS2Pv1Module fatal error at load_conf() : keys != NetworkKeyPair\")]\n UnexpectedKeys,\n}\n\nimpl DursModule\u003cDuRsConf, DursMsg\u003e for WS2Pv1Module {\n type ModuleUserConf = WS2PUserConf;\n type ModuleConf = WS2PConf;\n type ModuleOpt = WS2POpt;\n\n fn name() -\u003e ModuleStaticName {\n ModuleStaticName(MODULE_NAME)\n }\n fn priority() -\u003e ModulePriority {\n ModulePriority::Essential()\n }\n fn ask_required_keys() -\u003e RequiredKeys {\n RequiredKeys::NetworkKeyPair()\n }\n fn have_subcommand() -\u003e bool {\n true\n }\n\n fn generate_module_conf(\n currency_name: Option\u003c\u0026CurrencyName\u003e,\n _global_conf: \u0026\u003cDuRsConf as DursConfTrait\u003e::GlobalConf,\n module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n ) -\u003e Result\u003c(Self::ModuleConf, Option\u003cSelf::ModuleUserConf\u003e), ModuleConfError\u003e {\n let mut conf = WS2PConf::default();\n conf.currency = currency_name.cloned();\n\n if currency_name.is_some() \u0026\u0026 unwrap!(currency_name) == \u0026CurrencyName(\"g1-test\".to_owned())\n {\n conf.sync_endpoints = vec![unwrap!(EndpointV1::parse_from_raw(\n \"WS2P 3eaab4c7 ts.gt.librelois.fr 443 /ws2p\",\n PubKey::Ed25519(unwrap!(ed25519::PublicKey::from_base58(\n \"CrznBiyq8G4RVUprH9jHmAw1n1iuzw8y9FdJbrESnaX7\",\n )),),\n 0,\n 0,\n ))];\n }\n\n if let Some(module_user_conf) = module_user_conf.clone() {\n /*if let Some(outcoming_quota) = module_user_conf.outcoming_quota {\n conf.outcoming_quota = outcoming_quota;\n }\n if let Some(sync_endpoints) = module_user_conf.sync_endpoints {\n conf.sync_endpoints = sync_endpoints;\n }*/\n if let Some(prefered_pubkeys) = module_user_conf.prefered_pubkeys {\n conf.prefered_pubkeys = prefered_pubkeys\n .iter()\n .enumerate()\n .map(|(i, p)| {\n PubKey::from_str(p).map_err(|e| ModuleConfError::InvalidField {\n field_name: stringify!(prefered_pubkeys),\n cause: format!(\"pubkey n°{} is invalid: {}\", i, e),\n })\n })\n .collect::\u003cResult\u003cHashSet\u003cPubKey\u003e, ModuleConfError\u003e\u003e()?;\n }\n fields_overload!(\n conf;\n module_user_conf;\n [\n outcoming_quota,\n sync_endpoints\n ]\n )\n }\n\n Ok((conf, module_user_conf))\n }\n fn exec_subcommand(\n _soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n _keys: RequiredKeysContent,\n _module_conf: Self::ModuleConf,\n module_user_conf: Option\u003cSelf::ModuleUserConf\u003e,\n opts: WS2POpt,\n ) -\u003e Option\u003cSelf::ModuleUserConf\u003e {\n match opts.subcommand {\n WS2PSubCommands::Prefered {\n subcommand: prefered_subcommand,\n } =\u003e prefered_subcommand.execute(module_user_conf),\n }\n }\n fn start(\n soft_meta_datas: \u0026SoftwareMetaDatas\u003cDuRsConf\u003e,\n keys: RequiredKeysContent,\n conf: WS2PConf,\n router_sender: mpsc::Sender\u003cRouterThreadMessage\u003cDursMsg\u003e\u003e,\n ) -\u003e Result\u003c(), failure::Error\u003e {\n // Get start time\n let start_time = SystemTime::now();\n\n // Get key_pair\n let key_pair = if let RequiredKeysContent::NetworkKeyPair(key_pair) = keys {\n key_pair\n } else {\n return Err(WS2Pv1Error::UnexpectedKeys.into());\n };\n\n // load conf\n let mut ws2p_endpoints = HashMap::new();\n for ep in \u0026conf.sync_endpoints {\n info!(\"Load sync endpoint {}\", ep.raw_endpoint);\n let node_full_id = ep\n .node_full_id()\n .expect(\"Fail to get endpoint node_full_id\");\n ws2p_endpoints.insert(\n node_full_id,\n DbEndpoint {\n ep: ep.clone(),\n state: WS2PConnectionState::Close,\n last_check: 0,\n },\n );\n }\n\n // Get endpoints file path\n let mut ep_file_path = durs_conf::get_datas_path(soft_meta_datas.profile_path.clone());\n ep_file_path.push(\"ws2pv1\");\n if !ep_file_path.exists() {\n fs::create_dir(ep_file_path.as_path()).expect(\"Impossible to create ws2pv1 dir !\");\n }\n ep_file_path.push(\"endpoints.bin\");\n\n // Define WS2Pv1Module\n let mut ws2p_module = WS2Pv1Module::new(\n soft_meta_datas,\n conf,\n ep_file_path.clone(),\n key_pair,\n router_sender.clone(),\n );\n ws2p_module.ws2p_endpoints = ws2p_endpoints;\n\n // Create ws2p main thread channel\n let ws2p_sender_clone = ws2p_module.main_thread_channel.0.clone();\n\n // Get ws2p endpoints in file\n debug!(\"WS2P SSL={}\", ssl());\n let count;\n match ws2p_db::get_endpoints(\u0026ep_file_path) {\n Ok(ws2p_enpoints) =\u003e {\n let ws2p_enpoints = ws2p_enpoints\n .into_iter()\n .filter(|(_, dal_ep)| cfg!(feature = \"ssl\") || dal_ep.ep.port != 443)\n .map(|(node_full_id, mut dal_ep)| {\n if dal_ep.state == WS2PConnectionState::Established {\n dal_ep.state = WS2PConnectionState::Close;\n }\n (node_full_id, dal_ep)\n })\n .collect::\u003cVec\u003c(NodeFullId, DbEndpoint)\u003e\u003e();\n count = ws2p_enpoints.len();\n ws2p_module.ws2p_endpoints.extend(ws2p_enpoints);\n }\n Err(err) =\u003e fatal_error!(\"WS2Pv1: fail to load endpoints from DB: {:?}\", err),\n }\n info!(\"Load {} endpoints from DB !\", count);\n\n // Create proxy channel\n let (proxy_sender, proxy_receiver): (mpsc::Sender\u003cDursMsg\u003e, mpsc::Receiver\u003cDursMsg\u003e) =\n mpsc::channel();\n let proxy_sender_clone = proxy_sender.clone();\n\n // Launch a proxy thread that transform DursMsg to WS2PThreadSignal(DursMsg)\n thread::spawn(move || {\n // Send proxy sender to main\n router_sender\n .send(RouterThreadMessage::ModuleRegistration {\n static_name: WS2Pv1Module::name(),\n sender: proxy_sender_clone,\n roles: vec![ModuleRole::InterNodesNetwork],\n events_subscription: vec![\n ModuleEvent::CurrencyParameters,\n ModuleEvent::NewValidBlock,\n ModuleEvent::NewWotDocInPool,\n ModuleEvent::NewTxinPool,\n ],\n reserved_apis_parts: vec![ApiPart {\n name: ApiName(WS2P_API.to_owned()),\n versions: hashset![ApiVersion(1)],\n }],\n endpoints: vec![],\n })\n .expect(\"Fatal error : ws2p1 module fail to send is sender channel !\");\n debug!(\"Send ws2p1 sender to main thread.\");\n loop {\n match proxy_receiver.recv() {\n Ok(message) =\u003e {\n let stop = if let DursMsg::Stop = message {\n true\n } else {\n false\n };\n ws2p_sender_clone\n .send(WS2PThreadSignal::DursMsg(Box::new(message)))\n .expect(\n \"Fatal error : fail to relay DursMsgContent to ws2p main thread !\",\n );\n if stop {\n break;\n };\n }\n Err(e) =\u003e fatal_error!(format!(\"{}\", e)),\n }\n }\n });\n\n // Request current blockstamp\n send_dal_request(\u0026mut ws2p_module, \u0026BlockchainRequest::CurrentBlockstamp());\n\n // Start\n connect_to_know_endpoints(\u0026mut ws2p_module);\n ws2p_module.main_loop(start_time);\n\n Ok(())\n }\n}\n\nimpl WS2Pv1Module {\n fn main_loop(mut self, start_time: SystemTime) {\n // Initialize variables\n let mut last_ws2p_connecting_wave = SystemTime::now();\n let mut last_ws2p_state_print = SystemTime::now();\n let mut last_ws2p_endpoints_write = SystemTime::now();\n let mut endpoints_to_update_status: HashMap\u003cNodeFullId, SystemTime\u003e = HashMap::new();\n let mut last_identities_request = UNIX_EPOCH;\n\n loop {\n match self\n .main_thread_channel\n .1\n .recv_timeout(Duration::from_millis(200))\n {\n Ok(message) =\u003e match message {\n WS2PThreadSignal::DursMsg(durs_mesage) =\u003e {\n match durs_mesage.deref() {\n DursMsg::Stop =\u003e {\n // Close all connections\n for ws in self.websockets.values() {\n let _ = ws.0.close(CloseCode::Normal);\n }\n // Break main loop\n break;\n }\n DursMsg::Request {\n ref req_content, ..\n } =\u003e requests::received::receive_req(\u0026mut self, req_content),\n DursMsg::Event {\n ref event_type,\n ref event_content,\n ..\n } =\u003e events::received::receive_event(\n \u0026mut self,\n *event_type,\n event_content,\n ),\n DursMsg::Response {\n req_id,\n res_content,\n ..\n } =\u003e {\n responses::received::receive_response(\n \u0026mut self,\n *req_id,\n res_content,\n );\n }\n _ =\u003e {} // Others DursMsg variants\n }\n }\n WS2PThreadSignal::WS2Pv1Msg(msg) =\u003e {\n match crate::ws_connections::messages::ws2p_recv_message_pretreatment(\n \u0026mut self, msg,\n ) {\n WS2PSignal::NoConnection =\u003e {\n warn!(\"WS2PSignal::NoConnection\");\n }\n WS2PSignal::ConnectionEstablished(ws2p_full_id) =\u003e {\n let module_req_id =\n ModuleReqId(self.requests_awaiting_response.len() as u32);\n let module_id = WS2Pv1Module::name();\n debug!(\"WS2P: send req to: ({:?})\", ws2p_full_id);\n let _current_request_result =\n ws_connections::requests::sent::send_request_to_specific_node(\n \u0026mut self,\n ModuleReqFullId(module_id, module_req_id),\n \u0026ws2p_full_id,\n \u0026WS2Pv1Request {\n id: WS2Pv1ReqId::random(),\n body: WS2Pv1ReqBody::GetCurrent,\n },\n );\n if self.uids_cache.get(\u0026ws2p_full_id.1).is_none() {\n send_dal_request(\n \u0026mut self,\n \u0026BlockchainRequest::UIDs(vec![ws2p_full_id.1]),\n );\n }\n let event = NetworkEvent::ConnectionStateChange(\n ws2p_full_id,\n WS2PConnectionState::Established as u32,\n self.uids_cache.get(\u0026ws2p_full_id.1).cloned(),\n self.ws2p_endpoints[\u0026ws2p_full_id]\n .ep\n .get_url(false, false)\n .expect(\"Endpoint unreachable !\"),\n );\n events::sent::send_network_event(\u0026mut self, event);\n }\n WS2PSignal::WSError(ws2p_full_id) =\u003e {\n endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());\n close_connection(\n \u0026mut self,\n \u0026ws2p_full_id,\n WS2PCloseConnectionReason::WsError,\n );\n let event = NetworkEvent::ConnectionStateChange(\n ws2p_full_id,\n WS2PConnectionState::WSError as u32,\n self.uids_cache.get(\u0026ws2p_full_id.1).cloned(),\n self.ws2p_endpoints[\u0026ws2p_full_id]\n .ep\n .get_url(false, false)\n .expect(\"Endpoint unreachable !\"),\n );\n events::sent::send_network_event(\u0026mut self, event);\n }\n WS2PSignal::NegociationTimeout(ws2p_full_id) =\u003e {\n endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());\n let event = NetworkEvent::ConnectionStateChange(\n ws2p_full_id,\n WS2PConnectionState::Denial as u32,\n self.uids_cache.get(\u0026ws2p_full_id.1).cloned(),\n self.ws2p_endpoints[\u0026ws2p_full_id]\n .ep\n .get_url(false, false)\n .expect(\"Endpoint unreachable !\"),\n );\n events::sent::send_network_event(\u0026mut self, event);\n }\n WS2PSignal::Timeout(ws2p_full_id) =\u003e {\n endpoints_to_update_status.insert(ws2p_full_id, SystemTime::now());\n let event = NetworkEvent::ConnectionStateChange(\n ws2p_full_id,\n WS2PConnectionState::Close as u32,\n self.uids_cache.get(\u0026ws2p_full_id.1).cloned(),\n self.ws2p_endpoints[\u0026ws2p_full_id]\n .ep\n .get_url(false, false)\n .expect(\"Endpoint unreachable !\"),\n );\n events::sent::send_network_event(\u0026mut self, event);\n }\n WS2PSignal::PeerCard(_ws2p_full_id, _peer_card, ws2p_endpoints) =\u003e {\n //trace!(\"WS2PSignal::PeerCard({})\", ws2p_full_id);\n //self.send_network_event(NetworkEvent::ReceivePeers(_));\n for ep in ws2p_endpoints {\n match self.ws2p_endpoints.get(\n \u0026ep.node_full_id()\n .expect(\"WS2P: Fail to get ep.node_full_id() !\"),\n ) {\n Some(_) =\u003e {}\n None =\u003e {\n if let Some(_api) =\n ws2p_db::string_to_api(\u0026ep.api.0.clone())\n {\n endpoints_to_update_status.insert(\n ep.node_full_id().expect(\n \"WS2P: Fail to get ep.node_full_id() !\",\n ),\n SystemTime::now(),\n );\n }\n if cfg!(feature = \"ssl\") || ep.port != 443 {\n connect_to(\u0026mut self, \u0026ep);\n }\n }\n };\n }\n }\n WS2PSignal::Heads(ws2p_full_id, heads) =\u003e {\n trace!(\"WS2PSignal::Heads({}, {:?})\", ws2p_full_id, heads.len());\n send_dal_request(\n \u0026mut self,\n \u0026BlockchainRequest::UIDs(\n heads.iter().map(NetworkHead::pubkey).collect(),\n ),\n );\n let event = NetworkEvent::ReceiveHeads(\n heads\n .iter()\n .map(|head| {\n let mut new_head = head.clone();\n if let Some(uid) = self.uids_cache.get(\u0026head.pubkey()) {\n new_head.set_uid(uid);\n }\n new_head\n })\n .collect(),\n );\n events::sent::send_network_event(\u0026mut self, event);\n }\n WS2PSignal::Blocks(ws2p_full_id, blocks) =\u003e {\n trace!(\"WS2PSignal::Blocks({})\", ws2p_full_id);\n events::sent::send_network_event(\n \u0026mut self,\n NetworkEvent::ReceiveBlocks(blocks),\n );\n }\n WS2PSignal::UserDocuments(ws2p_full_id, user_documents) =\u003e {\n trace!(\"WS2PSignal::UserDocuments({})\", ws2p_full_id);\n events::sent::send_network_event(\n \u0026mut self,\n NetworkEvent::ReceiveDocuments(user_documents),\n );\n }\n WS2PSignal::Request { from, req_id, body } =\u003e {\n ws_connections::requests::received::receive_ws2p_v1_request(\n \u0026mut self, from, req_id, body,\n );\n }\n WS2PSignal::ReqResponse(\n module_req_full_id,\n ws2p_req_body,\n recipient_full_id,\n response,\n ) =\u003e ws_connections::responses::received::receive_response(\n \u0026mut self,\n module_req_full_id,\n ws2p_req_body,\n recipient_full_id,\n response,\n ),\n WS2PSignal::Empty =\u003e {}\n }\n }\n },\n Err(e) =\u003e match e {\n mpsc::RecvTimeoutError::Disconnected =\u003e {\n fatal_error!(\"Disconnected ws2p module !\");\n }\n mpsc::RecvTimeoutError::Timeout =\u003e {}\n },\n }\n if unwrap!(SystemTime::now().duration_since(last_ws2p_endpoints_write))\n \u003e Duration::new(*DURATION_BETWEEN_2_ENDPOINTS_SAVING, 0)\n {\n last_ws2p_endpoints_write = SystemTime::now();\n if let Err(err) = ws2p_db::write_endpoints(\u0026self.ep_file_path, \u0026self.ws2p_endpoints)\n {\n fatal_error!(\"WS2P1: Fail to write endpoints in DB : {:?}\", err);\n }\n }\n if unwrap!(SystemTime::now().duration_since(last_ws2p_state_print))\n \u003e Duration::new(*WS2P_GENERAL_STATE_INTERVAL, 0)\n {\n last_ws2p_state_print = SystemTime::now();\n let mut connected_nodes = Vec::new();\n for (k, DbEndpoint { state, .. }) in self.ws2p_endpoints.clone() {\n if let WS2PConnectionState::Established = state {\n connected_nodes.push(k);\n }\n }\n // Print current_blockstamp\n info!(\n \"WS2Pv1Module : current_blockstamp() = {:?}\",\n self.current_blockstamp\n );\n // New WS2P connection wave\n if connected_nodes.len() \u003c self.conf.clone().outcoming_quota\n \u0026\u0026 (unwrap!(SystemTime::now().duration_since(last_ws2p_connecting_wave))\n \u003e Duration::new(*WS2P_OUTCOMING_INTERVAL, 0)\n || (unwrap!(SystemTime::now().duration_since(last_ws2p_connecting_wave))\n \u003e Duration::new(*WS2P_OUTCOMING_INTERVAL_AT_STARTUP, 0)\n \u0026\u0026 unwrap!(SystemTime::now().duration_since(start_time))\n \u003c Duration::new(*WS2P_OUTCOMING_INTERVAL, 0)))\n {\n last_ws2p_connecting_wave = SystemTime::now();\n info!(\"Connected to know endpoints...\");\n connect_to_know_endpoints(\u0026mut self);\n }\n // Request pending_identities from network\n if unwrap!(SystemTime::now().duration_since(last_identities_request))\n \u003e Duration::new(*PENDING_IDENTITIES_REQUEST_INTERVAL, 0)\n \u0026\u0026 unwrap!(SystemTime::now().duration_since(start_time)) \u003e Duration::new(10, 0)\n {\n /*info!(\"get pending_identities from all connections...\");\n let _blocks_request_result = self.send_request_to_all_connections(\n \u0026OldNetworkRequest::GetRequirementsPending(ModuleReqId(0 as u32), 5),\n );*/\n last_identities_request = SystemTime::now();\n }\n // ..\n // Request current blockstamp\n send_dal_request(\u0026mut self, \u0026BlockchainRequest::CurrentBlockstamp());\n }\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::ws_connections::requests::sent::network_request_to_json;\n use crate::ws_connections::requests::*;\n use dubp_documents::documents::block::{BlockDocument, BlockDocumentTrait};\n use dubp_documents::parsers::blocks::parse_json_block_from_serde_value;\n use dubp_documents::BlockNumber;\n\n #[test]\n fn test_parse_json_block() {\n let json_block = json!({\n \"fork\": false,\n \"version\": 10,\n \"nonce\": 10500000059239 as u64,\n \"number\": 109966,\n \"powMin\": 88,\n \"time\": 1523300656,\n \"medianTime\": 1523295259,\n \"membersCount\": 933,\n \"monetaryMass\": 146881563,\n \"unitbase\": 0,\n \"issuersCount\": 44,\n \"issuersFrame\": 221,\n \"issuersFrameVar\": 0,\n \"currency\": \"g1\",\n \"issuer\": \"GRBPV3Y7PQnB9LaZhSGuS3BqBJbSHyibzYq65kTh1nQ4\",\n \"signature\": \"GCg2Lti3TdxWlhA8JF8pRI+dRQ0XZVtcC4BqO/COTpjTQFdWG6qmUNVvdeYCtR/lu1JQe3N/IhrbyV6L/6I+Cg==\",\n \"hash\": \"000000EF5B2AA849F4C3AF3D35E1284EA1F34A9F617EA806CE8371619023DC74\",\n \"parameters\": \"\",\n \"previousHash\": \"000004C00602F8A27AE078DE6351C0DDA1EA0974A78D2BEFA7DFBE7B7C3146FD\",\n \"previousIssuer\": \"5SwfQubSat5SunNafCsunEGTY93nVM4kLSsuprNqQb6S\",\n \"inner_hash\": \"61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C\",\n \"dividend\": null,\n \"identities\": [],\n \"joiners\": [],\n \"actives\": [],\n \"leavers\": [],\n \"revoked\": [],\n \"excluded\": [],\n \"certifications\": [\n \"Hm5qjaNuHogNRdGZ4vgnLA9DMZVUu5YWzVup5mubuxCc:8AmdBsimcLziXaCS4AcVUfPx7rkjeic7482dLbBkuZw6:109964:yHKBGMeuxyIqFb295gVNK6neRC+U0tmsX1Zed3TLjS3ZZHYYycE1piLcYsTKll4ifNVp6rm+hd/CLdHYB+29CA==\",\n \"BncjgJeFpGsMCCsUfzNLEexjsbuX3V2mg9P67ov2LkwK:DyBUBNpzpfvjtwYYSaVMM6ST6t2DNg3NCE9CU9bRQFhF:105864:cJEGW9WxJwlMA2+4LNAK4YieyseUy1WIkFh1YLYD+JJtJEoCSnIQRXzhiAoRpGaj0bRz8sTpwI6PRkuVoDJJDQ==\"\n ],\n \"transactions\": [\n {\n \"version\": 10,\n \"currency\": \"g1\",\n \"locktime\": 0,\n \"hash\": \"80FE1E83DC4D0B722CA5F8363EFC6A3E29071032EBB71C1E0DF8D4FEA589C698\",\n \"blockstamp\": \"109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B\",\n \"blockstampTime\": 0,\n \"issuers\": [\n \"6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT\"\n ],\n \"inputs\": [\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98284\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98519\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:98779\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99054\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99326\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99599\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:99884\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100174\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100469\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:100746\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:101036\",\n \"1001:0:D:6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT:101327\"\n ],\n \"outputs\": [\n \"12000:0:SIG(HmH5beJqKGMeotcQUrSW7Wo5tKvAksHmfYXfiSQ9EbWz)\",\n \"12:0:SIG(6PiqcuUWhyiBF3Lgcht8c1yfk6gMfQzcUc46CqrJfeLT)\"\n ],\n \"unlocks\": [\n \"0:SIG(0)\",\n \"1:SIG(0)\",\n \"2:SIG(0)\",\n \"3:SIG(0)\",\n \"4:SIG(0)\",\n \"5:SIG(0)\",\n \"6:SIG(0)\",\n \"7:SIG(0)\",\n \"8:SIG(0)\",\n \"9:SIG(0)\",\n \"10:SIG(0)\",\n \"11:SIG(0)\"\n ],\n \"signatures\": [\n \"MZxoKxYgwufh/s5mwLCsYEZXtIsP1hEKCyAzLipJsvCbR9xj7wXUw0C/ahwvZfBtR7+QVPIfLmwYEol1JcHjDw==\"\n ],\n \"comment\": \"Adhesion 2018\"\n },\n {\n \"version\": 10,\n \"currency\": \"g1\",\n \"locktime\": 0,\n \"hash\": \"B80507412B35BD5EB437AE0D3EB97E60E3A4974F5CDEA1AF7E2127C0E943481F\",\n \"blockstamp\": \"109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B\",\n \"blockstampTime\": 0,\n \"issuers\": [\n \"8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU\"\n ],\n \"inputs\": [\n \"1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:91560\",\n \"1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:91850\",\n \"1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92111\",\n \"1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92385\",\n \"1001:0:D:8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU:92635\"\n ],\n \"outputs\": [\n \"5000:0:SIG(BzHnbec1Gov7dLSt1EzJS7vikoQCECeuvZs4wamZAcT1)\",\n \"5:0:SIG(8gundJEbfm73Kx3jjw8YivJyz8qD2igjf6baCBLFCxPU)\"\n ],\n \"unlocks\": [\n \"0:SIG(0)\",\n \"1:SIG(0)\",\n \"2:SIG(0)\",\n \"3:SIG(0)\",\n \"4:SIG(0)\"\n ],\n \"signatures\": [\n \"A+ukwRvLWs1gZQ0KAqAnknEgmRQHdrnOvNuBx/WZqje17BAPrVxSxKpqwU6MiajU+ppigsYp6Bu0FdPf/tGnCQ==\"\n ],\n \"comment\": \"\"\n },\n {\n \"version\": 10,\n \"currency\": \"g1\",\n \"locktime\": 0,\n \"hash\": \"D8970E6629C0381A78534EEDD86803E9215A7EC4C494BAEA79EB19425F9B4D31\",\n \"blockstamp\": \"109964-00000168105D4A8A8BC8C0DC70033F45ABE472782C75A7F2074D0F4D4A3B7B2B\",\n \"blockstampTime\": 0,\n \"issuers\": [\n \"FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg\"\n ],\n \"inputs\": [\n \"1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:36597\",\n \"1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:36880\",\n \"1000:0:D:FnSXE7QyBfs4ozoYAt5NEewWhHEPorf38cNXu3kX9xsg:37082\"\n ],\n \"outputs\": [\n \"3000:0:SIG(BBC8Rnh4CWN1wBrPLevK7GRFFVDVw7Lu24YNMUmhqoHU)\"\n ],\n \"unlocks\": [\n \"0:SIG(0)\",\n \"1:SIG(0)\",\n \"2:SIG(0)\"\n ],\n \"signatures\": [\n \"OpiF/oQfIigOeAtsteukU0w9FPSELE+BVTxhmsQ8bEeYGlwovG2VF8ZFiJkLLPi6vFuKgwzULJfjNGd97twZCw==\"\n ],\n \"comment\": \"1 billet pour une seance.pour un chouette film\"\n }\n ],\n });\n let block: BlockDocument = parse_json_block_from_serde_value(\u0026json_block)\n .expect(\"Fail to parse test json block !\");\n assert_eq!(\n block\n .inner_hash()\n .expect(\"Try to get inner_hash of an uncompleted or reduce block !\")\n .to_hex(),\n \"61F02B1A6AE2E4B9A1FD66CE673258B4B21C0076795571EE3C9DC440DD06C46C\"\n );\n //block.generate_hash();\n assert_eq!(\n block\n .hash()\n .expect(\"Try to get hash of an uncompleted or reduce block !\")\n .0\n .to_hex(),\n \"000000EF5B2AA849F4C3AF3D35E1284EA1F34A9F617EA806CE8371619023DC74\"\n );\n }\n\n #[test]\n fn ws2p_requests() {\n let req_id_str = \"fbcf0bfa-7e18-40cc-b300-5c797d27518e\";\n let req_id = WS2Pv1ReqId::from_str(req_id_str).expect(\"fail to parse req_id\");\n let request = WS2Pv1Request {\n id: req_id,\n body: WS2Pv1ReqBody::GetBlocks {\n count: 50,\n from_number: BlockNumber(0),\n },\n };\n assert_eq!(\n network_request_to_json(\u0026request),\n json!({\n \"reqId\": req_id_str,\n \"body\": {\n \"name\": \"BLOCKS_CHUNK\",\n \"params\": {\n \"count\": 50,\n \"fromNumber\": 0\n }\n }\n })\n );\n assert_eq!(\n network_request_to_json(\u0026request).to_string(),\n \"{\\\"body\\\":{\\\"name\\\":\\\"BLOCKS_CHUNK\\\",\\\"params\\\":{\\\"count\\\":50,\\\"fromNumber\\\":0}},\\\"reqId\\\":\\\"fbcf0bfa-7e18-40cc-b300-5c797d27518e\\\"}\"\n );\n }\n\n #[test]\n fn ws2p_parse_head() {\n let head = json!({\n \"message\": \"WS2POTMIC:HEAD:1:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3\",\n \"sig\": \"trtK9GXvTdfND995ohWEderpO3NkIqi1X6mBeVvMcaHckq+lIGqjWvJ9t9Vccz5t+VGaSmGUihDl4q6eldIYBw==\",\n \"messageV2\": \"WS2POTMIC:HEAD:2:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3:25:22\",\n \"sigV2\": \"x6ehPMuYjGY+z7wEGnJGyMBxMKUdu01RWaF0b0XCtoVjg67cCvT4H0V/Qcxn4bAGqzy5ux2fA7NiI+81bBnqDw==\",\n \"step\": 0\n });\n let mut heads_count = 0;\n if let Ok(head) = NetworkHead::from_json_value(\u0026head) {\n if let NetworkHead::V2(ref head_v2) = head {\n heads_count += 1;\n assert_eq!(\n head_v2.message.to_string(),\n String::from(\"WS2POTMIC:HEAD:1:D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx:104512-0000051B9CE9C1CA89F269375A6751FB88B9E88DE47A36506057E5BFBCFBB276:c1c39a0a:duniter:1.6.21:3\")\n );\n }\n assert_eq!(head.verify(), true);\n } else {\n fatal_error!(\"Fail to parse head !\")\n }\n assert_eq!(heads_count, 1);\n }\n}\n","traces":[{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":259,"address":null,"length":0,"stats":{"Line":0}},{"line":269,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":0}},{"line":273,"address":null,"length":0,"stats":{"Line":0}},{"line":274,"address":null,"length":0,"stats":{"Line":0}},{"line":275,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":279,"address":null,"length":0,"stats":{"Line":0}},{"line":280,"address":null,"length":0,"stats":{"Line":0}},{"line":281,"address":null,"length":0,"stats":{"Line":0}},{"line":282,"address":null,"length":0,"stats":{"Line":0}},{"line":284,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":326,"address":null,"length":0,"stats":{"Line":0}},{"line":327,"address":null,"length":0,"stats":{"Line":0}},{"line":328,"address":null,"length":0,"stats":{"Line":0}},{"line":329,"address":null,"length":0,"stats":{"Line":0}},{"line":330,"address":null,"length":0,"stats":{"Line":0}},{"line":331,"address":null,"length":0,"stats":{"Line":0}},{"line":332,"address":null,"length":0,"stats":{"Line":0}},{"line":333,"address":null,"length":0,"stats":{"Line":0}},{"line":334,"address":null,"length":0,"stats":{"Line":0}},{"line":335,"address":null,"length":0,"stats":{"Line":0}},{"line":336,"address":null,"length":0,"stats":{"Line":0}},{"line":341,"address":null,"length":0,"stats":{"Line":0}},{"line":346,"address":null,"length":0,"stats":{"Line":0}},{"line":353,"address":null,"length":0,"stats":{"Line":0}},{"line":354,"address":null,"length":0,"stats":{"Line":0}},{"line":355,"address":null,"length":0,"stats":{"Line":0}},{"line":390,"address":null,"length":0,"stats":{"Line":0}},{"line":391,"address":null,"length":0,"stats":{"Line":0}},{"line":393,"address":null,"length":0,"stats":{"Line":0}},{"line":396,"address":null,"length":0,"stats":{"Line":0}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":400,"address":null,"length":0,"stats":{"Line":0}},{"line":403,"address":null,"length":0,"stats":{"Line":0}},{"line":408,"address":null,"length":0,"stats":{"Line":0}},{"line":409,"address":null,"length":0,"stats":{"Line":0}},{"line":411,"address":null,"length":0,"stats":{"Line":0}},{"line":413,"address":null,"length":0,"stats":{"Line":0}},{"line":414,"address":null,"length":0,"stats":{"Line":0}},{"line":415,"address":null,"length":0,"stats":{"Line":0}},{"line":416,"address":null,"length":0,"stats":{"Line":0}},{"line":418,"address":null,"length":0,"stats":{"Line":0}},{"line":419,"address":null,"length":0,"stats":{"Line":0}},{"line":423,"address":null,"length":0,"stats":{"Line":0}},{"line":430,"address":null,"length":0,"stats":{"Line":0}},{"line":431,"address":null,"length":0,"stats":{"Line":0}},{"line":432,"address":null,"length":0,"stats":{"Line":0}},{"line":433,"address":null,"length":0,"stats":{"Line":0}},{"line":434,"address":null,"length":0,"stats":{"Line":0}},{"line":435,"address":null,"length":0,"stats":{"Line":0}},{"line":436,"address":null,"length":0,"stats":{"Line":0}},{"line":437,"address":null,"length":0,"stats":{"Line":0}},{"line":440,"address":null,"length":0,"stats":{"Line":0}},{"line":442,"address":null,"length":0,"stats":{"Line":0}},{"line":443,"address":null,"length":0,"stats":{"Line":0}},{"line":444,"address":null,"length":0,"stats":{"Line":0}},{"line":446,"address":null,"length":0,"stats":{"Line":0}},{"line":447,"address":null,"length":0,"stats":{"Line":0}},{"line":452,"address":null,"length":0,"stats":{"Line":0}},{"line":454,"address":null,"length":0,"stats":{"Line":0}},{"line":461,"address":null,"length":0,"stats":{"Line":0}},{"line":462,"address":null,"length":0,"stats":{"Line":0}},{"line":463,"address":null,"length":0,"stats":{"Line":0}},{"line":464,"address":null,"length":0,"stats":{"Line":0}},{"line":467,"address":null,"length":0,"stats":{"Line":0}},{"line":474,"address":null,"length":0,"stats":{"Line":0}},{"line":477,"address":null,"length":0,"stats":{"Line":0}},{"line":478,"address":null,"length":0,"stats":{"Line":0}},{"line":479,"address":null,"length":0,"stats":{"Line":0}},{"line":480,"address":null,"length":0,"stats":{"Line":0}},{"line":484,"address":null,"length":0,"stats":{"Line":0}},{"line":485,"address":null,"length":0,"stats":{"Line":0}},{"line":486,"address":null,"length":0,"stats":{"Line":0}},{"line":487,"address":null,"length":0,"stats":{"Line":0}},{"line":488,"address":null,"length":0,"stats":{"Line":0}},{"line":489,"address":null,"length":0,"stats":{"Line":0}},{"line":490,"address":null,"length":0,"stats":{"Line":0}},{"line":491,"address":null,"length":0,"stats":{"Line":0}},{"line":492,"address":null,"length":0,"stats":{"Line":0}},{"line":493,"address":null,"length":0,"stats":{"Line":0}},{"line":494,"address":null,"length":0,"stats":{"Line":0}},{"line":495,"address":null,"length":0,"stats":{"Line":0}},{"line":501,"address":null,"length":0,"stats":{"Line":0}},{"line":502,"address":null,"length":0,"stats":{"Line":0}},{"line":503,"address":null,"length":0,"stats":{"Line":0}},{"line":504,"address":null,"length":0,"stats":{"Line":0}},{"line":506,"address":null,"length":0,"stats":{"Line":0}},{"line":509,"address":null,"length":0,"stats":{"Line":0}},{"line":510,"address":null,"length":0,"stats":{"Line":0}},{"line":511,"address":null,"length":0,"stats":{"Line":0}},{"line":512,"address":null,"length":0,"stats":{"Line":0}},{"line":513,"address":null,"length":0,"stats":{"Line":0}},{"line":514,"address":null,"length":0,"stats":{"Line":0}},{"line":516,"address":null,"length":0,"stats":{"Line":0}},{"line":519,"address":null,"length":0,"stats":{"Line":0}},{"line":522,"address":null,"length":0,"stats":{"Line":0}},{"line":523,"address":null,"length":0,"stats":{"Line":0}},{"line":524,"address":null,"length":0,"stats":{"Line":0}},{"line":525,"address":null,"length":0,"stats":{"Line":0}},{"line":526,"address":null,"length":0,"stats":{"Line":0}},{"line":527,"address":null,"length":0,"stats":{"Line":0}},{"line":528,"address":null,"length":0,"stats":{"Line":0}},{"line":529,"address":null,"length":0,"stats":{"Line":0}},{"line":530,"address":null,"length":0,"stats":{"Line":0}},{"line":531,"address":null,"length":0,"stats":{"Line":0}},{"line":533,"address":null,"length":0,"stats":{"Line":0}},{"line":535,"address":null,"length":0,"stats":{"Line":0}},{"line":536,"address":null,"length":0,"stats":{"Line":0}},{"line":537,"address":null,"length":0,"stats":{"Line":0}},{"line":539,"address":null,"length":0,"stats":{"Line":0}},{"line":541,"address":null,"length":0,"stats":{"Line":0}},{"line":544,"address":null,"length":0,"stats":{"Line":0}},{"line":545,"address":null,"length":0,"stats":{"Line":0}},{"line":546,"address":null,"length":0,"stats":{"Line":0}},{"line":549,"address":null,"length":0,"stats":{"Line":0}},{"line":551,"address":null,"length":0,"stats":{"Line":0}},{"line":552,"address":null,"length":0,"stats":{"Line":0}},{"line":553,"address":null,"length":0,"stats":{"Line":0}},{"line":554,"address":null,"length":0,"stats":{"Line":0}},{"line":555,"address":null,"length":0,"stats":{"Line":0}},{"line":556,"address":null,"length":0,"stats":{"Line":0}},{"line":557,"address":null,"length":0,"stats":{"Line":0}},{"line":558,"address":null,"length":0,"stats":{"Line":0}},{"line":559,"address":null,"length":0,"stats":{"Line":0}},{"line":560,"address":null,"length":0,"stats":{"Line":0}},{"line":562,"address":null,"length":0,"stats":{"Line":0}},{"line":563,"address":null,"length":0,"stats":{"Line":0}},{"line":564,"address":null,"length":0,"stats":{"Line":0}},{"line":566,"address":null,"length":0,"stats":{"Line":0}},{"line":568,"address":null,"length":0,"stats":{"Line":0}},{"line":569,"address":null,"length":0,"stats":{"Line":0}},{"line":570,"address":null,"length":0,"stats":{"Line":0}},{"line":571,"address":null,"length":0,"stats":{"Line":0}},{"line":572,"address":null,"length":0,"stats":{"Line":0}},{"line":573,"address":null,"length":0,"stats":{"Line":0}},{"line":574,"address":null,"length":0,"stats":{"Line":0}},{"line":575,"address":null,"length":0,"stats":{"Line":0}},{"line":576,"address":null,"length":0,"stats":{"Line":0}},{"line":578,"address":null,"length":0,"stats":{"Line":0}},{"line":579,"address":null,"length":0,"stats":{"Line":0}},{"line":580,"address":null,"length":0,"stats":{"Line":0}},{"line":581,"address":null,"length":0,"stats":{"Line":0}},{"line":583,"address":null,"length":0,"stats":{"Line":0}},{"line":584,"address":null,"length":0,"stats":{"Line":0}},{"line":587,"address":null,"length":0,"stats":{"Line":0}},{"line":593,"address":null,"length":0,"stats":{"Line":0}},{"line":596,"address":null,"length":0,"stats":{"Line":0}},{"line":597,"address":null,"length":0,"stats":{"Line":0}},{"line":599,"address":null,"length":0,"stats":{"Line":0}},{"line":604,"address":null,"length":0,"stats":{"Line":0}},{"line":606,"address":null,"length":0,"stats":{"Line":0}},{"line":607,"address":null,"length":0,"stats":{"Line":0}},{"line":608,"address":null,"length":0,"stats":{"Line":0}},{"line":609,"address":null,"length":0,"stats":{"Line":0}},{"line":610,"address":null,"length":0,"stats":{"Line":0}},{"line":612,"address":null,"length":0,"stats":{"Line":0}},{"line":613,"address":null,"length":0,"stats":{"Line":0}},{"line":614,"address":null,"length":0,"stats":{"Line":0}},{"line":615,"address":null,"length":0,"stats":{"Line":0}},{"line":616,"address":null,"length":0,"stats":{"Line":0}},{"line":618,"address":null,"length":0,"stats":{"Line":0}},{"line":619,"address":null,"length":0,"stats":{"Line":0}},{"line":620,"address":null,"length":0,"stats":{"Line":0}},{"line":621,"address":null,"length":0,"stats":{"Line":0}},{"line":623,"address":null,"length":0,"stats":{"Line":0}},{"line":624,"address":null,"length":0,"stats":{"Line":0}},{"line":627,"address":null,"length":0,"stats":{"Line":0}},{"line":629,"address":null,"length":0,"stats":{"Line":0}},{"line":630,"address":null,"length":0,"stats":{"Line":0}},{"line":631,"address":null,"length":0,"stats":{"Line":0}},{"line":632,"address":null,"length":0,"stats":{"Line":0}},{"line":633,"address":null,"length":0,"stats":{"Line":0}},{"line":634,"address":null,"length":0,"stats":{"Line":0}},{"line":635,"address":null,"length":0,"stats":{"Line":0}},{"line":637,"address":null,"length":0,"stats":{"Line":0}},{"line":638,"address":null,"length":0,"stats":{"Line":0}},{"line":639,"address":null,"length":0,"stats":{"Line":0}},{"line":641,"address":null,"length":0,"stats":{"Line":0}},{"line":642,"address":null,"length":0,"stats":{"Line":0}},{"line":643,"address":null,"length":0,"stats":{"Line":0}},{"line":644,"address":null,"length":0,"stats":{"Line":0}},{"line":645,"address":null,"length":0,"stats":{"Line":0}},{"line":647,"address":null,"length":0,"stats":{"Line":0}},{"line":648,"address":null,"length":0,"stats":{"Line":0}},{"line":649,"address":null,"length":0,"stats":{"Line":0}},{"line":655,"address":null,"length":0,"stats":{"Line":0}},{"line":656,"address":null,"length":0,"stats":{"Line":0}},{"line":657,"address":null,"length":0,"stats":{"Line":0}},{"line":659,"address":null,"length":0,"stats":{"Line":0}},{"line":660,"address":null,"length":0,"stats":{"Line":0}},{"line":662,"address":null,"length":0,"stats":{"Line":0}},{"line":663,"address":null,"length":0,"stats":{"Line":0}},{"line":664,"address":null,"length":0,"stats":{"Line":0}},{"line":665,"address":null,"length":0,"stats":{"Line":0}},{"line":666,"address":null,"length":0,"stats":{"Line":0}},{"line":667,"address":null,"length":0,"stats":{"Line":0}},{"line":668,"address":null,"length":0,"stats":{"Line":0}},{"line":669,"address":null,"length":0,"stats":{"Line":0}},{"line":670,"address":null,"length":0,"stats":{"Line":0}},{"line":671,"address":null,"length":0,"stats":{"Line":0}},{"line":672,"address":null,"length":0,"stats":{"Line":0}},{"line":673,"address":null,"length":0,"stats":{"Line":0}},{"line":674,"address":null,"length":0,"stats":{"Line":0}},{"line":677,"address":null,"length":0,"stats":{"Line":0}},{"line":679,"address":null,"length":0,"stats":{"Line":0}},{"line":680,"address":null,"length":0,"stats":{"Line":0}},{"line":683,"address":null,"length":0,"stats":{"Line":0}},{"line":684,"address":null,"length":0,"stats":{"Line":0}},{"line":685,"address":null,"length":0,"stats":{"Line":0}},{"line":686,"address":null,"length":0,"stats":{"Line":0}},{"line":687,"address":null,"length":0,"stats":{"Line":0}},{"line":688,"address":null,"length":0,"stats":{"Line":0}},{"line":689,"address":null,"length":0,"stats":{"Line":0}},{"line":690,"address":null,"length":0,"stats":{"Line":0}},{"line":692,"address":null,"length":0,"stats":{"Line":0}},{"line":694,"address":null,"length":0,"stats":{"Line":0}},{"line":695,"address":null,"length":0,"stats":{"Line":0}},{"line":697,"address":null,"length":0,"stats":{"Line":0}},{"line":698,"address":null,"length":0,"stats":{"Line":0}},{"line":699,"address":null,"length":0,"stats":{"Line":0}},{"line":701,"address":null,"length":0,"stats":{"Line":0}},{"line":702,"address":null,"length":0,"stats":{"Line":0}},{"line":703,"address":null,"length":0,"stats":{"Line":0}},{"line":704,"address":null,"length":0,"stats":{"Line":0}},{"line":705,"address":null,"length":0,"stats":{"Line":0}},{"line":706,"address":null,"length":0,"stats":{"Line":0}},{"line":707,"address":null,"length":0,"stats":{"Line":0}},{"line":708,"address":null,"length":0,"stats":{"Line":0}},{"line":710,"address":null,"length":0,"stats":{"Line":0}},{"line":712,"address":null,"length":0,"stats":{"Line":0}},{"line":713,"address":null,"length":0,"stats":{"Line":0}},{"line":714,"address":null,"length":0,"stats":{"Line":0}},{"line":715,"address":null,"length":0,"stats":{"Line":0}},{"line":716,"address":null,"length":0,"stats":{"Line":0}},{"line":717,"address":null,"length":0,"stats":{"Line":0}},{"line":718,"address":null,"length":0,"stats":{"Line":0}},{"line":719,"address":null,"length":0,"stats":{"Line":0}},{"line":720,"address":null,"length":0,"stats":{"Line":0}},{"line":721,"address":null,"length":0,"stats":{"Line":0}},{"line":723,"address":null,"length":0,"stats":{"Line":0}},{"line":725,"address":null,"length":0,"stats":{"Line":0}},{"line":726,"address":null,"length":0,"stats":{"Line":0}},{"line":727,"address":null,"length":0,"stats":{"Line":0}},{"line":728,"address":null,"length":0,"stats":{"Line":0}},{"line":729,"address":null,"length":0,"stats":{"Line":0}},{"line":730,"address":null,"length":0,"stats":{"Line":0}},{"line":731,"address":null,"length":0,"stats":{"Line":0}},{"line":732,"address":null,"length":0,"stats":{"Line":0}},{"line":733,"address":null,"length":0,"stats":{"Line":0}},{"line":734,"address":null,"length":0,"stats":{"Line":0}},{"line":736,"address":null,"length":0,"stats":{"Line":0}},{"line":738,"address":null,"length":0,"stats":{"Line":0}},{"line":741,"address":null,"length":0,"stats":{"Line":0}},{"line":742,"address":null,"length":0,"stats":{"Line":0}},{"line":743,"address":null,"length":0,"stats":{"Line":0}},{"line":744,"address":null,"length":0,"stats":{"Line":0}},{"line":746,"address":null,"length":0,"stats":{"Line":0}},{"line":747,"address":null,"length":0,"stats":{"Line":0}},{"line":748,"address":null,"length":0,"stats":{"Line":0}},{"line":749,"address":null,"length":0,"stats":{"Line":0}},{"line":751,"address":null,"length":0,"stats":{"Line":0}},{"line":752,"address":null,"length":0,"stats":{"Line":0}},{"line":753,"address":null,"length":0,"stats":{"Line":0}},{"line":755,"address":null,"length":0,"stats":{"Line":0}},{"line":758,"address":null,"length":0,"stats":{"Line":0}},{"line":759,"address":null,"length":0,"stats":{"Line":0}},{"line":765,"address":null,"length":0,"stats":{"Line":0}},{"line":766,"address":null,"length":0,"stats":{"Line":0}},{"line":768,"address":null,"length":0,"stats":{"Line":0}},{"line":769,"address":null,"length":0,"stats":{"Line":0}},{"line":770,"address":null,"length":0,"stats":{"Line":0}},{"line":773,"address":null,"length":0,"stats":{"Line":0}},{"line":774,"address":null,"length":0,"stats":{"Line":0}},{"line":775,"address":null,"length":0,"stats":{"Line":0}},{"line":776,"address":null,"length":0,"stats":{"Line":0}},{"line":777,"address":null,"length":0,"stats":{"Line":0}},{"line":778,"address":null,"length":0,"stats":{"Line":0}},{"line":779,"address":null,"length":0,"stats":{"Line":0}},{"line":781,"address":null,"length":0,"stats":{"Line":0}},{"line":783,"address":null,"length":0,"stats":{"Line":0}},{"line":785,"address":null,"length":0,"stats":{"Line":0}},{"line":787,"address":null,"length":0,"stats":{"Line":0}},{"line":788,"address":null,"length":0,"stats":{"Line":0}},{"line":790,"address":null,"length":0,"stats":{"Line":0}},{"line":791,"address":null,"length":0,"stats":{"Line":0}},{"line":794,"address":null,"length":0,"stats":{"Line":0}},{"line":795,"address":null,"length":0,"stats":{"Line":0}},{"line":797,"address":null,"length":0,"stats":{"Line":0}},{"line":798,"address":null,"length":0,"stats":{"Line":0}},{"line":801,"address":null,"length":0,"stats":{"Line":0}},{"line":803,"address":null,"length":0,"stats":{"Line":0}},{"line":806,"address":null,"length":0,"stats":{"Line":0}},{"line":807,"address":null,"length":0,"stats":{"Line":0}},{"line":808,"address":null,"length":0,"stats":{"Line":0}},{"line":809,"address":null,"length":0,"stats":{"Line":0}},{"line":810,"address":null,"length":0,"stats":{"Line":0}},{"line":812,"address":null,"length":0,"stats":{"Line":0}},{"line":813,"address":null,"length":0,"stats":{"Line":0}},{"line":814,"address":null,"length":0,"stats":{"Line":0}},{"line":815,"address":null,"length":0,"stats":{"Line":0}},{"line":816,"address":null,"length":0,"stats":{"Line":0}},{"line":818,"address":null,"length":0,"stats":{"Line":0}},{"line":822,"address":null,"length":0,"stats":{"Line":0}},{"line":823,"address":null,"length":0,"stats":{"Line":0}},{"line":824,"address":null,"length":0,"stats":{"Line":0}},{"line":826,"address":null,"length":0,"stats":{"Line":0}},{"line":829,"address":null,"length":0,"stats":{"Line":0}},{"line":830,"address":null,"length":0,"stats":{"Line":0}},{"line":832,"address":null,"length":0,"stats":{"Line":0}},{"line":833,"address":null,"length":0,"stats":{"Line":0}},{"line":835,"address":null,"length":0,"stats":{"Line":0}},{"line":838,"address":null,"length":0,"stats":{"Line":0}},{"line":839,"address":null,"length":0,"stats":{"Line":0}},{"line":841,"address":null,"length":0,"stats":{"Line":0}},{"line":842,"address":null,"length":0,"stats":{"Line":0}},{"line":843,"address":null,"length":0,"stats":{"Line":0}},{"line":844,"address":null,"length":0,"stats":{"Line":0}},{"line":845,"address":null,"length":0,"stats":{"Line":0}},{"line":849,"address":null,"length":0,"stats":{"Line":0}},{"line":851,"address":null,"length":0,"stats":{"Line":0}},{"line":854,"address":null,"length":0,"stats":{"Line":0}},{"line":855,"address":null,"length":0,"stats":{"Line":0}},{"line":856,"address":null,"length":0,"stats":{"Line":0}},{"line":857,"address":null,"length":0,"stats":{"Line":0}},{"line":858,"address":null,"length":0,"stats":{"Line":0}},{"line":859,"address":null,"length":0,"stats":{"Line":0}},{"line":860,"address":null,"length":0,"stats":{"Line":0}},{"line":862,"address":null,"length":0,"stats":{"Line":0}},{"line":863,"address":null,"length":0,"stats":{"Line":0}},{"line":864,"address":null,"length":0,"stats":{"Line":0}},{"line":867,"address":null,"length":0,"stats":{"Line":0}},{"line":868,"address":null,"length":0,"stats":{"Line":0}},{"line":869,"address":null,"length":0,"stats":{"Line":0}},{"line":875,"address":null,"length":0,"stats":{"Line":0}},{"line":879,"address":null,"length":0,"stats":{"Line":0}},{"line":895,"address":4714224,"length":1,"stats":{"Line":2}},{"line":896,"address":4714237,"length":1,"stats":{"Line":1}},{"line":1040,"address":4731282,"length":1,"stats":{"Line":1}},{"line":1042,"address":4731474,"length":1,"stats":{"Line":1}},{"line":1043,"address":4731378,"length":1,"stats":{"Line":1}},{"line":1050,"address":4732011,"length":1,"stats":{"Line":1}},{"line":1051,"address":4731930,"length":1,"stats":{"Line":1}},{"line":1061,"address":4737888,"length":1,"stats":{"Line":2}},{"line":1062,"address":4737902,"length":1,"stats":{"Line":1}},{"line":1063,"address":4737964,"length":1,"stats":{"Line":1}},{"line":1064,"address":4738119,"length":1,"stats":{"Line":1}},{"line":1065,"address":4738040,"length":1,"stats":{"Line":1}},{"line":1066,"address":4738083,"length":1,"stats":{"Line":1}},{"line":1068,"address":4738072,"length":1,"stats":{"Line":1}},{"line":1071,"address":4738250,"length":1,"stats":{"Line":0}},{"line":1072,"address":4738183,"length":1,"stats":{"Line":1}},{"line":1073,"address":4738212,"length":1,"stats":{"Line":1}},{"line":1084,"address":4739872,"length":1,"stats":{"Line":1}},{"line":1085,"address":4739820,"length":1,"stats":{"Line":1}},{"line":1091,"address":4740880,"length":1,"stats":{"Line":2}},{"line":1092,"address":4740887,"length":1,"stats":{"Line":1}},{"line":1099,"address":4741830,"length":1,"stats":{"Line":1}},{"line":1100,"address":4741841,"length":1,"stats":{"Line":1}},{"line":1101,"address":4741958,"length":1,"stats":{"Line":1}},{"line":1102,"address":4741993,"length":1,"stats":{"Line":1}},{"line":1103,"address":4742120,"length":1,"stats":{"Line":1}},{"line":1104,"address":4742035,"length":1,"stats":{"Line":1}},{"line":1105,"address":4742085,"length":1,"stats":{"Line":1}},{"line":1108,"address":4742635,"length":1,"stats":{"Line":1}},{"line":1110,"address":4743139,"length":1,"stats":{"Line":0}},{"line":1112,"address":4744239,"length":1,"stats":{"Line":0}}],"covered":28,"coverable":383},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ok_message.rs"],"content":"use crate::*;\nuse dup_crypto::keys::*;\nuse serde::ser::{Serialize, SerializeStruct, Serializer};\n\n#[derive(Debug, Clone)]\npub struct WS2POkMessageV1 {\n pub currency: String,\n pub pubkey: PubKey,\n pub challenge: String,\n pub signature: Option\u003cSig\u003e,\n}\n\nimpl WS2PMessage for WS2POkMessageV1 {\n fn parse(v: \u0026serde_json::Value, currency: String) -\u003e Result\u003cSelf, WS2PMsgParseErr\u003e {\n let signature = match v.get(\"sig\") {\n Some(signature) =\u003e signature.as_str().ok_or(WS2PMsgParseErr {})?.to_string(),\n None =\u003e return Err(WS2PMsgParseErr {}),\n };\n let pubkey: PubKey = PubKey::Ed25519(ed25519::PublicKey::from_base58(\n \"969qRJs8KhsnkyzqarpL4RKZGMdVKNbZgu8fhsigM7Lj\",\n )?);\n let signature: Option\u003cSig\u003e = Some(Sig::Ed25519(dup_crypto::keys::Signature::from_base64(\n \u0026signature,\n )?));\n Ok(WS2POkMessageV1 {\n currency,\n pubkey,\n challenge: \"\".to_string(),\n signature,\n })\n }\n fn to_raw(\u0026self) -\u003e String {\n format!(\n \"WS2P:OK:{}:{}:{}\",\n self.currency, self.pubkey, self.challenge\n )\n }\n fn verify(\u0026self) -\u003e bool {\n if let Some(sig) = self.signature {\n self.pubkey.verify(self.to_raw().as_bytes(), \u0026sig).is_ok()\n } else {\n false\n }\n }\n}\n\nimpl Serialize for WS2POkMessageV1 {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n let mut connect_message_in_json = serializer.serialize_struct(\"message\", 2)?;\n connect_message_in_json.serialize_field(\"auth\", \"OK\")?;\n connect_message_in_json.serialize_field(\n \"sig\",\n \u0026self\n .signature\n .expect(\"Fail to serialize OK message : the signature field is set to None !\")\n .to_string(),\n )?;\n connect_message_in_json.end()\n }\n}\n","traces":[{"line":14,"address":null,"length":0,"stats":{"Line":0}},{"line":15,"address":null,"length":0,"stats":{"Line":0}},{"line":16,"address":null,"length":0,"stats":{"Line":0}},{"line":17,"address":null,"length":0,"stats":{"Line":0}},{"line":19,"address":null,"length":0,"stats":{"Line":0}},{"line":20,"address":null,"length":0,"stats":{"Line":0}},{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":26,"address":null,"length":0,"stats":{"Line":0}},{"line":27,"address":null,"length":0,"stats":{"Line":0}},{"line":28,"address":null,"length":0,"stats":{"Line":0}},{"line":29,"address":null,"length":0,"stats":{"Line":0}},{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":4495088,"length":1,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":31},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","requests","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules requests received.\n\nuse crate::ws2p_db::DbEndpoint;\nuse crate::ws_connections::requests::{WS2Pv1ReqBody, WS2Pv1ReqId, WS2Pv1Request};\nuse crate::ws_connections::states::WS2PConnectionState;\nuse crate::WS2Pv1Module;\nuse dubp_documents::BlockNumber;\nuse durs_message::requests::DursReqContent;\nuse durs_network::requests::OldNetworkRequest;\n\npub fn receive_req(ws2p_module: \u0026mut WS2Pv1Module, req_content: \u0026DursReqContent) {\n if let DursReqContent::OldNetworkRequest(ref old_net_request) = *req_content {\n match *old_net_request {\n OldNetworkRequest::GetBlocks(ref module_req_full_id, ref count, ref from) =\u003e {\n let mut receiver_index = 0;\n let mut real_receiver = None;\n for (ws2p_full_id, DbEndpoint { state, .. }) in ws2p_module.ws2p_endpoints.clone() {\n if let WS2PConnectionState::Established = state {\n if receiver_index == ws2p_module.next_receiver {\n real_receiver = Some(ws2p_full_id);\n break;\n }\n receiver_index += 1;\n }\n }\n if real_receiver.is_none() {\n ws2p_module.next_receiver = 0;\n for (ws2p_full_id, DbEndpoint { state, .. }) in \u0026ws2p_module.ws2p_endpoints {\n if let WS2PConnectionState::Established = *state {\n real_receiver = Some(*ws2p_full_id);\n break;\n }\n }\n } else {\n ws2p_module.next_receiver += 1;\n }\n if let Some(real_receiver) = real_receiver {\n debug!(\"WS2P: send req to: ({:?})\", real_receiver);\n let _blocks_request_result =\n crate::ws_connections::requests::sent::send_request_to_specific_node(\n ws2p_module,\n *module_req_full_id,\n \u0026real_receiver,\n \u0026WS2Pv1Request {\n id: WS2Pv1ReqId::random(),\n body: WS2Pv1ReqBody::GetBlocks {\n count: *count,\n from_number: BlockNumber(*from),\n },\n },\n );\n } else {\n warn!(\"WS2P: not found peer to send request !\");\n }\n }\n OldNetworkRequest::GetEndpoints(ref _request) =\u003e {}\n _ =\u003e {}\n }\n }\n}\n","traces":[{"line":26,"address":4244480,"length":1,"stats":{"Line":0}},{"line":27,"address":4244503,"length":1,"stats":{"Line":0}},{"line":28,"address":4246707,"length":1,"stats":{"Line":0}},{"line":29,"address":4244570,"length":1,"stats":{"Line":0}},{"line":30,"address":4244700,"length":1,"stats":{"Line":0}},{"line":31,"address":4244712,"length":1,"stats":{"Line":0}},{"line":32,"address":4244720,"length":1,"stats":{"Line":0}},{"line":33,"address":4246869,"length":1,"stats":{"Line":0}},{"line":34,"address":4245143,"length":1,"stats":{"Line":0}},{"line":35,"address":4245168,"length":1,"stats":{"Line":0}},{"line":36,"address":4245251,"length":1,"stats":{"Line":0}},{"line":38,"address":4245256,"length":1,"stats":{"Line":0}},{"line":41,"address":4245334,"length":1,"stats":{"Line":0}},{"line":42,"address":4245364,"length":1,"stats":{"Line":0}},{"line":43,"address":4245383,"length":1,"stats":{"Line":0}},{"line":44,"address":4245630,"length":1,"stats":{"Line":0}},{"line":45,"address":4245658,"length":1,"stats":{"Line":0}},{"line":46,"address":4245735,"length":1,"stats":{"Line":0}},{"line":50,"address":4245742,"length":1,"stats":{"Line":0}},{"line":52,"address":4245802,"length":1,"stats":{"Line":0}},{"line":53,"address":4245873,"length":1,"stats":{"Line":0}},{"line":55,"address":4246404,"length":1,"stats":{"Line":0}},{"line":56,"address":4246212,"length":1,"stats":{"Line":0}},{"line":57,"address":4246220,"length":1,"stats":{"Line":0}},{"line":59,"address":4246340,"length":1,"stats":{"Line":0}},{"line":60,"address":4246263,"length":1,"stats":{"Line":0}},{"line":61,"address":4246308,"length":1,"stats":{"Line":0}},{"line":62,"address":4246281,"length":1,"stats":{"Line":0}},{"line":63,"address":4246291,"length":1,"stats":{"Line":0}},{"line":67,"address":4246446,"length":1,"stats":{"Line":0}},{"line":68,"address":4246471,"length":1,"stats":{"Line":0}},{"line":71,"address":4246709,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":32},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","requests","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules requests sent.\n\nuse crate::WS2Pv1Module;\nuse durs_message::requests::{BlockchainRequest, DursReqContent};\nuse durs_message::*;\nuse durs_module::{DursModule, ModuleReqId, ModuleRole, RouterThreadMessage};\n\npub fn send_dal_request(ws2p_module: \u0026mut WS2Pv1Module, req: \u0026BlockchainRequest) -\u003e ModuleReqId {\n ws2p_module.count_dal_requests += 1;\n if ws2p_module.count_dal_requests == std::u32::MAX {\n ws2p_module.count_dal_requests = 0;\n }\n\n let req_id = ModuleReqId(ws2p_module.count_dal_requests);\n\n ws2p_module\n .router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Request {\n req_from: WS2Pv1Module::name(),\n req_to: ModuleRole::BlockchainDatas,\n req_id,\n req_content: DursReqContent::BlockchainRequest(req.clone()),\n }))\n .expect(\"Fail to send message to router !\");\n\n req_id\n}\n","traces":[{"line":23,"address":5752096,"length":1,"stats":{"Line":0}},{"line":24,"address":5752113,"length":1,"stats":{"Line":0}},{"line":25,"address":5752158,"length":1,"stats":{"Line":0}},{"line":26,"address":5752172,"length":1,"stats":{"Line":0}},{"line":29,"address":5752187,"length":1,"stats":{"Line":0}},{"line":31,"address":5752202,"length":1,"stats":{"Line":0}},{"line":41,"address":5752530,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":7},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","responses","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules responses received.\n\nuse crate::ws_connections::responses::{WS2Pv1ReqRes, WS2Pv1ReqResBody};\nuse crate::*;\n\npub fn receive_response(\n ws2p_module: \u0026mut WS2Pv1Module,\n req_id: ModuleReqId,\n res_content: \u0026DursResContent,\n) {\n if let DursResContent::BlockchainResponse(ref bc_res) = *res_content {\n match *bc_res.deref() {\n BlockchainResponse::CurrentBlockstamp(ref current_blockstamp_) =\u003e {\n debug!(\n \"WS2Pv1Module : receive DALResBc::CurrentBlockstamp({})\",\n ws2p_module.current_blockstamp\n );\n ws2p_module.current_blockstamp = *current_blockstamp_;\n if ws2p_module.my_head.is_none() {\n ws2p_module.my_head = Some(heads::generate_my_head(\n \u0026ws2p_module.key_pair,\n ws2p_module.node_id,\n ws2p_module.soft_name,\n ws2p_module.soft_version,\n \u0026ws2p_module.current_blockstamp,\n None,\n ));\n }\n let event = NetworkEvent::ReceiveHeads(vec![unwrap!(ws2p_module.my_head.clone())]);\n events::sent::send_network_event(ws2p_module, event);\n }\n BlockchainResponse::UIDs(ref uids) =\u003e {\n // Add uids to heads\n for head in ws2p_module.heads_cache.values_mut() {\n if let Some(uid_option) = uids.get(\u0026head.pubkey()) {\n if let Some(ref uid) = *uid_option {\n head.set_uid(uid);\n ws2p_module\n .uids_cache\n .insert(head.pubkey(), uid.to_string());\n } else {\n ws2p_module.uids_cache.remove(\u0026head.pubkey());\n }\n }\n }\n // Resent heads to other modules\n let event =\n NetworkEvent::ReceiveHeads(ws2p_module.heads_cache.values().cloned().collect());\n events::sent::send_network_event(ws2p_module, event);\n // Resent to other modules connections that match receive uids\n let events = ws2p_module\n .ws2p_endpoints\n .iter()\n .filter_map(|(node_full_id, DbEndpoint { ep, state, .. })| {\n if let Some(uid_option) = uids.get(\u0026node_full_id.1) {\n Some(NetworkEvent::ConnectionStateChange(\n *node_full_id,\n *state as u32,\n uid_option.clone(),\n ep.get_url(false, false).expect(\"Endpoint unreachable !\"),\n ))\n } else {\n None\n }\n })\n .collect();\n events::sent::send_network_events(ws2p_module, events);\n }\n BlockchainResponse::CurrentBlock(ref block_box, _blockstamp) =\u003e {\n if let Some(ws2p_req_full_id) =\n ws2p_module.pending_received_requests.remove(\u0026req_id)\n {\n ws_connections::responses::sent::send_response(\n ws2p_module,\n ws2p_req_full_id.from,\n WS2Pv1ReqRes {\n req_id: ws2p_req_full_id.req_id,\n body: WS2Pv1ReqResBody::GetCurrent(block_box.deref().clone()),\n },\n )\n }\n }\n _ =\u003e {} // Others BlockchainResponse variants\n }\n }\n}\n","traces":[{"line":21,"address":4323872,"length":1,"stats":{"Line":0}},{"line":26,"address":4323908,"length":1,"stats":{"Line":0}},{"line":27,"address":4323967,"length":1,"stats":{"Line":0}},{"line":28,"address":4323996,"length":1,"stats":{"Line":0}},{"line":29,"address":4324101,"length":1,"stats":{"Line":0}},{"line":31,"address":4324283,"length":1,"stats":{"Line":0}},{"line":33,"address":4324475,"length":1,"stats":{"Line":0}},{"line":34,"address":4324570,"length":1,"stats":{"Line":0}},{"line":35,"address":4324722,"length":1,"stats":{"Line":0}},{"line":36,"address":4324615,"length":1,"stats":{"Line":0}},{"line":37,"address":4324629,"length":1,"stats":{"Line":0}},{"line":38,"address":4324643,"length":1,"stats":{"Line":0}},{"line":39,"address":4324665,"length":1,"stats":{"Line":0}},{"line":40,"address":4324687,"length":1,"stats":{"Line":0}},{"line":41,"address":4324702,"length":1,"stats":{"Line":0}},{"line":44,"address":4324841,"length":1,"stats":{"Line":0}},{"line":45,"address":4325116,"length":1,"stats":{"Line":0}},{"line":47,"address":4325209,"length":1,"stats":{"Line":0}},{"line":49,"address":4325221,"length":1,"stats":{"Line":0}},{"line":50,"address":4325430,"length":1,"stats":{"Line":0}},{"line":51,"address":4325583,"length":1,"stats":{"Line":0}},{"line":52,"address":4325630,"length":1,"stats":{"Line":0}},{"line":53,"address":4325690,"length":1,"stats":{"Line":0}},{"line":55,"address":4325704,"length":1,"stats":{"Line":0}},{"line":57,"address":4325801,"length":1,"stats":{"Line":0}},{"line":63,"address":4325470,"length":1,"stats":{"Line":0}},{"line":64,"address":4325991,"length":1,"stats":{"Line":0}},{"line":66,"address":4326059,"length":1,"stats":{"Line":0}},{"line":69,"address":4326089,"length":1,"stats":{"Line":0}},{"line":70,"address":4550718,"length":1,"stats":{"Line":0}},{"line":71,"address":4550970,"length":1,"stats":{"Line":0}},{"line":72,"address":4550820,"length":1,"stats":{"Line":0}},{"line":73,"address":4550849,"length":1,"stats":{"Line":0}},{"line":74,"address":4550861,"length":1,"stats":{"Line":0}},{"line":75,"address":4550885,"length":1,"stats":{"Line":0}},{"line":78,"address":4551159,"length":1,"stats":{"Line":0}},{"line":82,"address":4326155,"length":1,"stats":{"Line":0}},{"line":84,"address":4326237,"length":1,"stats":{"Line":0}},{"line":85,"address":4326327,"length":1,"stats":{"Line":0}},{"line":86,"address":4326285,"length":1,"stats":{"Line":0}},{"line":89,"address":4326394,"length":1,"stats":{"Line":0}},{"line":90,"address":4326407,"length":1,"stats":{"Line":0}},{"line":91,"address":4326560,"length":1,"stats":{"Line":0}},{"line":92,"address":4326436,"length":1,"stats":{"Line":0}},{"line":93,"address":4326468,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":45},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","responses","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the inter-modules responses sent.\n\nuse crate::*;\nuse durs_message::*;\n\npub fn send_network_req_response(\n ws2p_module: \u0026WS2Pv1Module,\n requester: ModuleStaticName,\n req_id: ModuleReqId,\n response: NetworkResponse,\n) {\n ws2p_module\n .router_sender\n .send(RouterThreadMessage::ModuleMessage(DursMsg::Response {\n res_from: WS2Pv1Module::name(),\n res_to: requester,\n req_id,\n res_content: DursResContent::NetworkResponse(response),\n }))\n .expect(\"Fail to send message to router !\");\n}\n","traces":[{"line":21,"address":4928672,"length":1,"stats":{"Line":0}},{"line":27,"address":4928698,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","block.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize BlockDocument into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse dubp_documents::documents::block::{BlockDocumentStringified, BlockDocumentV10Stringified};\n\nimpl IntoWS2Pv1Json for BlockDocumentStringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n match self {\n BlockDocumentStringified::V10(block_str_v10) =\u003e block_str_v10.into_ws2p_v1_json(),\n }\n }\n}\n\nimpl IntoWS2Pv1Json for BlockDocumentV10Stringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n let actives = self\n .actives\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n let certifications = self\n .certifications\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n let identities = self\n .identities\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n let joiners = self\n .joiners\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n let leavers = self\n .leavers\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n let revoked = self\n .revoked\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n let transactions = self\n .transactions\n .into_iter()\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect::\u003cVec\u003cserde_json::Value\u003e\u003e();\n\n json!( {\n \"actives\": actives,\n \"certifications\": certifications,\n \"currency\": self.currency,\n \"dividend\": null,\n \"excluded\": self.excluded,\n \"fork\": false,\n \"hash\": self.hash,\n \"identities\": identities,\n \"inner_hash\": self.inner_hash,\n \"issuer\": self.issuers[0],\n \"issuersCount\": self.issuers_count,\n \"issuersFrame\": self.issuers_frame,\n \"issuersFrameVar\": self.issuers_frame_var,\n \"joiners\": joiners,\n \"leavers\": leavers,\n \"medianTime\": self.median_time,\n \"membersCount\": self.members_count,\n \"monetaryMass\": self.monetary_mass,\n \"nonce\": self.nonce,\n \"number\": self.number,\n \"parameters\": self.parameters.unwrap_or_else(|| \"\".to_owned()),\n \"powMin\": self.pow_min,\n \"previousHash\": self.previous_hash,\n \"previousIssuer\": self.previous_issuer,\n \"revoked\": revoked,\n \"signature\": self.signatures[0],\n \"time\": self.time,\n \"transactions\": transactions,\n \"unitbase\": self.unit_base,\n \"version\": 10\n })\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":31,"address":null,"length":0,"stats":{"Line":0}},{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":34,"address":null,"length":0,"stats":{"Line":0}},{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":0}},{"line":37,"address":null,"length":0,"stats":{"Line":0}},{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":70},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","certification.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize CompactCertificationDocumentV10Stringified into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse dubp_documents::documents::certification::v10::CompactCertificationDocumentV10Stringified;\n\nimpl IntoWS2Pv1Json for CompactCertificationDocumentV10Stringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n format!(\n \"{issuer}:{target}:{block_number}:{signature}\",\n issuer = self.issuer,\n target = self.target,\n block_number = self.block_number,\n signature = self.signature,\n )\n .into()\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","head.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize HEAD into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse durs_common_tools::fatal_error;\nuse durs_network_documents::network_head::*;\nuse std::ops::Deref;\n\nimpl IntoWS2Pv1Json for NetworkHead {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n match self {\n NetworkHead::V2(box_head_v2) =\u003e {\n let head_v2 = box_head_v2.deref();\n json!({\n \"message\": head_v2.message.to_string(),\n \"sig\": head_v2.sig.to_string(),\n \"messageV2\": head_v2.message_v2.to_string(),\n \"sigV2\": head_v2.sig_v2.to_string(),\n \"step\": head_v2.step + 1\n })\n }\n _ =\u003e fatal_error!(\"HEAD version not supported !\"),\n }\n }\n}\n","traces":[{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":26,"address":null,"length":0,"stats":{"Line":0}},{"line":27,"address":null,"length":0,"stats":{"Line":0}},{"line":28,"address":null,"length":0,"stats":{"Line":0}},{"line":29,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":31,"address":null,"length":0,"stats":{"Line":0}},{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","identity.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize IdentityDocumentV10Stringified into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse dubp_documents::documents::identity::IdentityDocumentV10Stringified;\n\nimpl IntoWS2Pv1Json for IdentityDocumentV10Stringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n format!(\n \"{}:{}:{}:{}\",\n self.issuer, self.signature, self.blockstamp, self.username\n )\n .into()\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","membership.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize MembershipDocumentV10Stringified into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse dubp_documents::documents::membership::MembershipDocumentV10Stringified;\n\nimpl IntoWS2Pv1Json for MembershipDocumentV10Stringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n format!(\n \"{issuer}:{signature}:{blockstamp}:{idty_blockstamp}:{username}\",\n issuer = self.issuer,\n signature = self.signature,\n blockstamp = self.blockstamp,\n idty_blockstamp = self.identity_blockstamp,\n username = self.username,\n )\n .into()\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","revoked.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize CompactRevocationDocumentV10Stringified into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse dubp_documents::documents::revocation::CompactRevocationDocumentV10Stringified;\n\nimpl IntoWS2Pv1Json for CompactRevocationDocumentV10Stringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n format!(\"{}:{}\", self.issuer, self.signature,).into()\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","serializers","transaction.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module that serialize TransactionDocument into WS2Pv1 json format\n\nuse super::IntoWS2Pv1Json;\nuse dubp_documents::documents::transaction::TransactionDocumentStringified;\n\nimpl IntoWS2Pv1Json for TransactionDocumentStringified {\n fn into_ws2p_v1_json(self) -\u003e serde_json::Value {\n json!( {\n \"blockstamp\": self.blockstamp,\n \"blockstampTime\": 0,\n \"comment\": self.comment,\n \"currency\": self.currency,\n \"hash\": self.hash,\n \"inputs\": self.inputs,\n \"issuers\": self.issuers,\n \"locktime\": self.locktime,\n \"outputs\": self.outputs,\n \"signatures\": self.signatures,\n \"unlocks\": self.unlocks,\n \"version\": 10\n })\n }\n}\n","traces":[{"line":22,"address":null,"length":0,"stats":{"Line":0}},{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":24,"address":null,"length":0,"stats":{"Line":0}},{"line":25,"address":null,"length":0,"stats":{"Line":0}},{"line":26,"address":null,"length":0,"stats":{"Line":0}},{"line":27,"address":null,"length":0,"stats":{"Line":0}},{"line":28,"address":null,"length":0,"stats":{"Line":0}},{"line":29,"address":null,"length":0,"stats":{"Line":0}},{"line":30,"address":null,"length":0,"stats":{"Line":0}},{"line":31,"address":null,"length":0,"stats":{"Line":0}},{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":34,"address":null,"length":0,"stats":{"Line":0}},{"line":35,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":14},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","subcommands","prefered.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P1 module subcommand prefered\n\nuse dup_crypto::keys::PubKey;\nuse std::collections::HashSet;\nuse std::fs;\nuse std::io::BufRead;\nuse std::path::PathBuf;\nuse std::str::FromStr;\n\n#[derive(Clone, Debug, StructOpt)]\n/// Ws2p1 prefered subcommands\npub enum Ws2pPreferedSubCommands {\n /// Add prefered pubkey\n #[structopt(\n name = \"add\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Add {\n /// Public key to add\n public_keys: Vec\u003cPubKey\u003e,\n },\n /// Add prefered pubkeys from file (one pubkey per line)\n #[structopt(\n name = \"add-file\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n AddFromFile {\n /// File path\n #[structopt(parse(from_os_str))]\n file_path: PathBuf,\n },\n /// Clear prefered pubkeys\n #[structopt(\n name = \"clear\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Clear,\n /// Remove prefered pubkey\n #[structopt(\n name = \"rem\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Rem {\n /// Public key to remove\n public_keys: Vec\u003cPubKey\u003e,\n },\n /// Show prefered pubkeys\n #[structopt(\n name = \"show\",\n raw(setting = \"structopt::clap::AppSettings::ColoredHelp\")\n )]\n Show,\n}\n\nimpl Ws2pPreferedSubCommands {\n pub fn execute(\n self,\n module_user_conf: Option\u003ccrate::WS2PUserConf\u003e,\n ) -\u003e Option\u003ccrate::WS2PUserConf\u003e {\n {\n let mut prefered_pubkeys = if let Some(ref module_user_conf) = module_user_conf {\n module_user_conf\n .prefered_pubkeys\n .clone()\n .unwrap_or_else(HashSet::new)\n } else {\n HashSet::new()\n };\n\n match self {\n Ws2pPreferedSubCommands::Add { public_keys } =\u003e {\n for pubkey in public_keys {\n prefered_pubkeys.insert(pubkey.to_string());\n println!(\n \"Pubkey '{}' successfully added to the list of preferred keys.\",\n pubkey\n );\n }\n let mut new_user_conf = module_user_conf.unwrap_or_default();\n new_user_conf.prefered_pubkeys = Some(prefered_pubkeys);\n Some(new_user_conf)\n }\n Ws2pPreferedSubCommands::AddFromFile { file_path } =\u003e {\n if file_path.as_path().exists() {\n match fs::File::open(file_path.as_path()) {\n Ok(file) =\u003e {\n let mut new_prefered_pubkeys = HashSet::new();\n for (i, line) in std::io::BufReader::new(file).lines().enumerate() {\n match line {\n Ok(line) =\u003e match PubKey::from_str(\u0026line) {\n Ok(pubkey) =\u003e {\n new_prefered_pubkeys.insert(pubkey.to_string());\n println!(\n \"Pubkey '{}' successfully added to the list of preferred keys.\",\n pubkey\n );\n }\n Err(e) =\u003e {\n println!(\"Line n°{} is invalid: {}\", i + 1, e);\n }\n },\n Err(e) =\u003e {\n println!(\"Fail to read line n°{}: {}\", i + 1, e);\n return module_user_conf;\n }\n }\n }\n let mut new_user_conf = module_user_conf.unwrap_or_default();\n if let Some(ref mut prefered_pubkeys) =\n new_user_conf.prefered_pubkeys\n {\n prefered_pubkeys.extend(new_prefered_pubkeys.into_iter());\n } else {\n new_user_conf.prefered_pubkeys = Some(new_prefered_pubkeys);\n }\n Some(new_user_conf)\n }\n Err(e) =\u003e {\n println!(\"Fail to open file: {}\", e);\n module_user_conf\n }\n }\n } else {\n println!(\"Error: file note exist !\");\n module_user_conf\n }\n }\n Ws2pPreferedSubCommands::Clear =\u003e {\n if let Some(mut module_user_conf) = module_user_conf {\n module_user_conf.prefered_pubkeys = None;\n println!(\"All preferred keys removed !\");\n Some(module_user_conf)\n } else {\n module_user_conf\n }\n }\n Ws2pPreferedSubCommands::Rem { public_keys } =\u003e {\n for pubkey in public_keys {\n prefered_pubkeys.remove(\u0026pubkey.to_string());\n println!(\n \"Pubkey '{}' successfully removed from the list of preferred keys\",\n pubkey\n );\n }\n let mut new_user_conf = module_user_conf.unwrap_or_default();\n new_user_conf.prefered_pubkeys = Some(prefered_pubkeys);\n Some(new_user_conf)\n }\n Ws2pPreferedSubCommands::Show =\u003e {\n println!(\"{} preferred keys: \", prefered_pubkeys.len());\n for pubkey in \u0026prefered_pubkeys {\n println!(\"{}\", pubkey);\n }\n module_user_conf\n }\n }\n }\n }\n}\n","traces":[{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":67},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws2p_db.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Manage WS2Pv1 storage.\n\nuse crate::ws_connections::states::WS2PConnectionState;\nuse durs_network_documents::network_endpoint::EndpointV1;\nuse durs_network_documents::NodeFullId;\nuse serde::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse std::fs::File;\nuse std::path::Path;\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum EndpointApi {\n WS2P,\n //WS2PS,\n //WS2PTOR,\n //DASA,\n //BMA,\n //BMAS,\n}\n\npub fn string_to_api(api: \u0026str) -\u003e Option\u003cEndpointApi\u003e {\n match api {\n \"WS2P\" =\u003e Some(EndpointApi::WS2P),\n //\"WS2PS\" =\u003e Some(EndpointApi::WS2PS),\n //\"WS2PTOR\" =\u003e Some(EndpointApi::WS2PTOR),\n //\"DASA\" =\u003e Some(EndpointApi::DASA),\n //\"BASIC_MERKLED_API\" =\u003e Some(EndpointApi::BMA),\n //\"BMAS\" =\u003e Some(EndpointApi::BMAS),\n \u0026_ =\u003e None,\n }\n}\n\n#[derive(Debug)]\npub enum Ws2pPeersDbError {\n IoErr(std::io::Error),\n SerdeErr(bincode::Error),\n}\n\nimpl From\u003cstd::io::Error\u003e for Ws2pPeersDbError {\n fn from(e: std::io::Error) -\u003e Self {\n Ws2pPeersDbError::IoErr(e)\n }\n}\n\nimpl From\u003cbincode::Error\u003e for Ws2pPeersDbError {\n fn from(e: bincode::Error) -\u003e Self {\n Ws2pPeersDbError::SerdeErr(e)\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub struct DbEndpoint {\n pub ep: EndpointV1,\n pub state: WS2PConnectionState,\n pub last_check: u64,\n}\n\npub fn get_endpoints(\n file_path: \u0026Path,\n) -\u003e Result\u003cHashMap\u003cNodeFullId, DbEndpoint\u003e, Ws2pPeersDbError\u003e {\n if file_path.exists() {\n let bin_endpoints = durs_common_tools::fns::bin_file::read_bin_file(file_path)?;\n if bin_endpoints.is_empty() {\n Ok(HashMap::new())\n } else {\n Ok(bincode::deserialize(\u0026bin_endpoints[..])?)\n }\n } else {\n File::create(file_path)?;\n Ok(HashMap::new())\n }\n}\n\npub fn write_endpoints\u003cS: std::hash::BuildHasher\u003e(\n file_path: \u0026Path,\n endpoints: \u0026HashMap\u003cNodeFullId, DbEndpoint, S\u003e,\n) -\u003e Result\u003c(), Ws2pPeersDbError\u003e {\n let bin_endpoints = bincode::serialize(\u0026endpoints)?;\n durs_common_tools::fns::bin_file::write_bin_file(file_path, \u0026bin_endpoints)?;\n\n Ok(())\n}\n","traces":[{"line":36,"address":5867872,"length":1,"stats":{"Line":0}},{"line":37,"address":5867918,"length":1,"stats":{"Line":0}},{"line":38,"address":5867893,"length":1,"stats":{"Line":0}},{"line":44,"address":5867920,"length":1,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":5868048,"length":1,"stats":{"Line":0}},{"line":76,"address":5868071,"length":1,"stats":{"Line":0}},{"line":77,"address":5868153,"length":1,"stats":{"Line":0}},{"line":78,"address":5868512,"length":1,"stats":{"Line":0}},{"line":79,"address":5868539,"length":1,"stats":{"Line":0}},{"line":81,"address":5868596,"length":1,"stats":{"Line":0}},{"line":83,"address":5868490,"length":1,"stats":{"Line":0}},{"line":84,"address":5868988,"length":1,"stats":{"Line":0}},{"line":85,"address":5869309,"length":1,"stats":{"Line":0}},{"line":89,"address":4629264,"length":1,"stats":{"Line":0}},{"line":93,"address":4629289,"length":1,"stats":{"Line":0}},{"line":94,"address":4629640,"length":1,"stats":{"Line":0}},{"line":96,"address":4629981,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":21},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","handler.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P connections handler.\n\nuse super::messages::*;\nuse super::meta_datas::WS2PConnectionMetaDatas;\nuse super::states::WS2PConnectionState;\nuse crate::constants::*;\nuse crate::*;\nuse dup_crypto::keys::*;\nuse std::sync::mpsc;\n#[allow(deprecated)]\nuse ws::util::{Timeout, Token};\nuse ws::{CloseCode, Frame, Handler, Handshake, Message, Sender};\n\nconst CONNECT: Token = Token(1);\nconst EXPIRE: Token = Token(2);\n\n// Our Handler struct.\n// Here we explicity indicate that the Client needs a Sender,\n// whereas a closure captures the Sender for us automatically.\n#[allow(deprecated)]\n#[derive(Debug)]\npub struct Client {\n ws: Sender,\n conductor_sender: mpsc::Sender\u003cWS2PThreadSignal\u003e,\n currency: String,\n key_pair: KeyPairEnum,\n connect_message: Message,\n conn_meta_datas: WS2PConnectionMetaDatas,\n last_mess_time: SystemTime,\n spam_interval: bool,\n spam_counter: usize,\n timeout: Option\u003cTimeout\u003e,\n}\n\npub fn connect_to_ws2p_endpoint(\n endpoint: \u0026EndpointV1,\n conductor_sender: \u0026mpsc::Sender\u003cWS2PThreadSignal\u003e,\n currency: \u0026str,\n key_pair: KeyPairEnum,\n) -\u003e ws::Result\u003c()\u003e {\n // Get endpoint url\n let ws_url = endpoint.get_url(true, false).expect(\"Endpoint unreachable\");\n\n // Create WS2PConnectionMetaDatass\n let mut conn_meta_datas = WS2PConnectionMetaDatas::new(\n \"b60a14fd-0826-4ae0-83eb-1a92cd59fd5308535fd3-78f2-4678-9315-cd6e3b7871b1\".to_string(),\n );\n conn_meta_datas.remote_pubkey = Some(endpoint.issuer);\n conn_meta_datas.remote_uuid = Some(\n endpoint\n .node_id\n .expect(\"WS2P: Fail to get ep.node_uuid() !\"),\n );\n\n // Generate connect message\n let connect_message =\n generate_connect_message(currency, key_pair, conn_meta_datas.challenge.clone());\n\n // Log\n info!(\"WS2P: Try connection to {} ...\", ws_url);\n\n // Connect to websocket\n ws::connect(ws_url, |ws| Client {\n ws,\n conductor_sender: conductor_sender.clone(),\n currency: String::from(currency),\n key_pair,\n connect_message: connect_message.clone(),\n conn_meta_datas: conn_meta_datas.clone(),\n last_mess_time: SystemTime::now(),\n spam_interval: false,\n spam_counter: 0,\n timeout: None,\n })\n}\n\n// We implement the Handler trait for Client so that we can get more\n// fine-grained control of the connection.\nimpl Handler for Client {\n // `on_open` will be called only after the WebSocket handshake is successful\n // so at this point we know that the connection is ready to send/receive messages.\n // We ignore the `Handshake` for now, but you could also use this method to setup\n // Handler state or reject the connection based on the details of the Request\n // or Response, such as by checking cookies or Auth headers.\n fn on_open(\u0026mut self, _: Handshake) -\u003e ws::Result\u003c()\u003e {\n // Define timeouts\n self.ws.timeout(WS2P_NEGOTIATION_TIMEOUT * 1_000, CONNECT)?;\n self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;\n // Send ws::Sender to WS2PConductor\n let result = self\n .conductor_sender\n .send(WS2PThreadSignal::WS2Pv1Msg(WS2Pv1Msg {\n from: self.conn_meta_datas.node_full_id(),\n payload: WS2Pv1MsgPayload::WebsocketOk(WsSender(self.ws.clone())),\n }));\n // If WS2PConductor is unrechable, close connection.\n if result.is_err() {\n debug!(\"Close ws2p connection because ws2p main thread is unrechable !\");\n self.ws.close(CloseCode::Normal)\n } else {\n // Send CONNECT Message\n self.ws.send(self.connect_message.clone())\n }\n }\n\n // `on_message` is roughly equivalent to the Handler closure. It takes a `Message`\n // and returns a `Result\u003c()\u003e`.\n fn on_message(\u0026mut self, msg: Message) -\u003e ws::Result\u003c()\u003e {\n // Spam ?\n if unwrap!(SystemTime::now().duration_since(self.last_mess_time))\n \u003e Duration::new(*WS2P_SPAM_INTERVAL_IN_MILLI_SECS, 0)\n {\n if self.spam_interval {\n self.spam_counter += 1;\n } else {\n self.spam_interval = true;\n self.spam_counter = 2;\n }\n } else {\n self.spam_interval = false;\n self.spam_counter = 0;\n }\n // Spam ?\n if self.spam_counter \u003e= *WS2P_SPAM_LIMIT {\n thread::sleep(Duration::from_millis(*WS2P_SPAM_SLEEP_TIME_IN_SEC));\n self.last_mess_time = SystemTime::now();\n return Ok(());\n }\n self.last_mess_time = SystemTime::now();\n\n // Parse and check incoming message\n if msg.is_text() {\n let s: String = msg\n .into_text()\n .expect(\"WS2P: Fail to convert message payload to String !\");\n debug!(\"WS2P: receive mess: {}\", s);\n let json_message: serde_json::Value = serde_json::from_str(\u0026s)\n .expect(\"WS2P: Fail to convert string message ton json value !\");\n let result = self\n .conductor_sender\n .send(WS2PThreadSignal::WS2Pv1Msg(WS2Pv1Msg {\n from: self.conn_meta_datas.node_full_id(),\n payload: self.conn_meta_datas.parse_and_check_incoming_message(\n \u0026self.currency,\n self.key_pair,\n \u0026json_message,\n ),\n }));\n if result.is_err() {\n info!(\"Close ws2p connection because ws2p main thread is unrechable !\");\n self.ws.close(CloseCode::Normal)?;\n }\n }\n Ok(())\n }\n fn on_timeout(\u0026mut self, event: Token) -\u003e ws::Result\u003c()\u003e {\n match event {\n CONNECT =\u003e {\n if self.conn_meta_datas.state != WS2PConnectionState::Established {\n let _result =\n self.conductor_sender\n .send(WS2PThreadSignal::WS2Pv1Msg(WS2Pv1Msg {\n from: self.conn_meta_datas.node_full_id(),\n payload: WS2Pv1MsgPayload::NegociationTimeout,\n }));\n self.ws.close(CloseCode::Away)\n } else {\n Ok(())\n }\n }\n EXPIRE =\u003e {\n let _result = self\n .conductor_sender\n .send(WS2PThreadSignal::WS2Pv1Msg(WS2Pv1Msg {\n from: self.conn_meta_datas.node_full_id(),\n payload: WS2Pv1MsgPayload::Timeout,\n }));\n self.ws.close(CloseCode::Away)\n }\n _ =\u003e Ok(()),\n }\n }\n #[allow(deprecated)]\n fn on_new_timeout(\u0026mut self, event: Token, timeout: Timeout) -\u003e ws::Result\u003c()\u003e {\n if event == EXPIRE {\n if let Some(t) = self.timeout.take() {\n self.ws.cancel(t)?;\n }\n self.timeout = Some(timeout)\n }\n Ok(())\n }\n fn on_frame(\u0026mut self, frame: Frame) -\u003e ws::Result\u003cOption\u003cFrame\u003e\u003e {\n // some activity has occurred, let's reset the expiration timeout\n self.ws.timeout(WS2P_EXPIRE_TIMEOUT * 1_000, EXPIRE)?;\n Ok(Some(frame))\n }\n fn on_close(\u0026mut self, code: CloseCode, reason: \u0026str) {\n // The WebSocket protocol allows for a utf8 reason for the closing state after the\n // close code. WS-RS will attempt to interpret this data as a utf8 description of the\n // reason for closing the connection. I many cases, `reason` will be an empty string.\n // So, you may not normally want to display `reason` to the user,\n // but let's assume that we know that `reason` is human-readable.\n match code {\n CloseCode::Normal =\u003e info!(\"The remote server close the connection.\"),\n CloseCode::Away =\u003e info!(\"The remote server is leaving.\"),\n _ =\u003e warn!(\"The remote server encountered an error: {}\", reason),\n }\n let _result = self\n .conductor_sender\n .send(WS2PThreadSignal::WS2Pv1Msg(WS2Pv1Msg {\n from: self.conn_meta_datas.node_full_id(),\n payload: WS2Pv1MsgPayload::Close,\n }));\n }\n}\n","traces":[{"line":50,"address":4982480,"length":1,"stats":{"Line":0}},{"line":57,"address":4982516,"length":1,"stats":{"Line":0}},{"line":60,"address":4982674,"length":1,"stats":{"Line":0}},{"line":61,"address":4982631,"length":1,"stats":{"Line":0}},{"line":63,"address":4982681,"length":1,"stats":{"Line":0}},{"line":64,"address":4982810,"length":1,"stats":{"Line":0}},{"line":65,"address":4982772,"length":1,"stats":{"Line":0}},{"line":72,"address":4982828,"length":1,"stats":{"Line":0}},{"line":75,"address":4983029,"length":1,"stats":{"Line":0}},{"line":78,"address":4414512,"length":1,"stats":{"Line":0}},{"line":79,"address":4414527,"length":1,"stats":{"Line":0}},{"line":80,"address":4414544,"length":1,"stats":{"Line":0}},{"line":81,"address":4414605,"length":1,"stats":{"Line":0}},{"line":82,"address":4414660,"length":1,"stats":{"Line":0}},{"line":83,"address":4414750,"length":1,"stats":{"Line":0}},{"line":84,"address":4414789,"length":1,"stats":{"Line":0}},{"line":85,"address":4414825,"length":1,"stats":{"Line":0}},{"line":88,"address":4414861,"length":1,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":206,"address":null,"length":0,"stats":{"Line":0}},{"line":208,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":105},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","messages.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Define ws2p connections messages.\n\nuse super::*;\nuse crate::ws_connections::requests::WS2Pv1ReqBody;\nuse dubp_documents::documents::DocumentDUBP;\nuse durs_network_documents::NodeFullId;\nuse ws::Message;\n\n#[derive(Debug)]\n/// WS2Pv1 Message\npub struct WS2Pv1Msg {\n pub from: NodeFullId,\n pub payload: WS2Pv1MsgPayload,\n}\n\n#[derive(Debug)]\n/// WS2Pv1 Message payload\npub enum WS2Pv1MsgPayload {\n FailOpenWS,\n WrongUrl,\n FailToSplitWS,\n TryToSendConnectMess,\n FailSendConnectMess,\n WebsocketOk(WsSender),\n NegociationTimeout,\n ValidConnectMessage(String, WS2PConnectionState),\n ValidAckMessage(String, WS2PConnectionState),\n ValidOk(WS2PConnectionState),\n Request {\n req_id: WS2Pv1ReqId,\n body: WS2Pv1ReqBody,\n },\n PeerCard(serde_json::Value, Vec\u003cEndpointV1\u003e),\n Heads(Vec\u003cserde_json::Value\u003e),\n Document(DocumentDUBP),\n ReqResponse(WS2Pv1ReqId, serde_json::Value),\n InvalidMessage,\n WrongFormatMessage,\n UnknowMessage,\n Timeout,\n Close,\n}\n\npub fn generate_connect_message(\n currency: \u0026str,\n key_pair: KeyPairEnum,\n challenge: String,\n) -\u003e Message {\n // Create CONNECT Message\n let mut connect_message = WS2PConnectMessageV1 {\n currency: String::from(currency),\n pubkey: key_pair.public_key(),\n challenge,\n signature: None,\n };\n connect_message.signature = Some(connect_message.sign(key_pair));\n Message::text(\n serde_json::to_string(\u0026connect_message).expect(\"Fail to serialize CONNECT message !\"),\n )\n}\n\npub fn ws2p_recv_message_pretreatment(\n ws2p_module: \u0026mut WS2Pv1Module,\n message: WS2Pv1Msg,\n) -\u003e WS2PSignal {\n check_timeout_requests(ws2p_module);\n\n let ws2p_full_id = message.from;\n match message.payload {\n WS2Pv1MsgPayload::WrongUrl\n | WS2Pv1MsgPayload::FailOpenWS\n | WS2Pv1MsgPayload::FailToSplitWS =\u003e {\n let dal_ep = ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\");\n dal_ep.state = WS2PConnectionState::WSError;\n dal_ep.last_check = durs_common_tools::fns::time::current_timestamp();\n return WS2PSignal::WSError(ws2p_full_id);\n }\n WS2Pv1MsgPayload::TryToSendConnectMess =\u003e {\n ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\")\n .state = WS2PConnectionState::TryToSendConnectMess;\n }\n WS2Pv1MsgPayload::FailSendConnectMess =\u003e {\n let dal_ep = ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\");\n dal_ep.state = WS2PConnectionState::Unreachable;\n dal_ep.last_check = durs_common_tools::fns::time::current_timestamp();\n }\n WS2Pv1MsgPayload::WebsocketOk(sender) =\u003e {\n ws2p_module.websockets.insert(ws2p_full_id, sender);\n }\n WS2Pv1MsgPayload::ValidConnectMessage(response, new_con_state) =\u003e {\n ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\")\n .state = new_con_state;\n debug!(\"Send: {:#?}\", response);\n if let Some(websocket) = ws2p_module.websockets.get_mut(\u0026ws2p_full_id) {\n if websocket.0.send(Message::text(response)).is_err() {\n return WS2PSignal::WSError(ws2p_full_id);\n }\n } else {\n // Connection closed by remote peer\n let dal_ep = ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\");\n dal_ep.state = WS2PConnectionState::Close;\n dal_ep.last_check = durs_common_tools::fns::time::current_timestamp();\n }\n }\n WS2Pv1MsgPayload::ValidAckMessage(response, new_con_state) =\u003e {\n ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\")\n .state = new_con_state;\n if let WS2PConnectionState::AckMessOk = ws2p_module.ws2p_endpoints[\u0026ws2p_full_id].state\n {\n debug!(\"Send: {:#?}\", response);\n if let Some(websocket) = ws2p_module.websockets.get_mut(\u0026ws2p_full_id) {\n if websocket.0.send(Message::text(response)).is_err() {\n return WS2PSignal::WSError(ws2p_full_id);\n }\n } else {\n fatal_error!(\"Fatal error : no websocket for {} !\", ws2p_full_id);\n }\n }\n }\n WS2Pv1MsgPayload::ValidOk(new_con_state) =\u003e {\n ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\")\n .state = new_con_state;\n let mut close_conn = false;\n let signal = match ws2p_module.ws2p_endpoints[\u0026ws2p_full_id].state {\n WS2PConnectionState::OkMessOkWaitingAckMess =\u003e WS2PSignal::Empty,\n WS2PConnectionState::Established =\u003e WS2PSignal::ConnectionEstablished(ws2p_full_id),\n _ =\u003e {\n close_conn = true;\n WS2PSignal::Empty\n }\n };\n if close_conn {\n close_connection(\n ws2p_module,\n \u0026ws2p_full_id,\n WS2PCloseConnectionReason::Unknow,\n );\n }\n\n return signal;\n }\n WS2Pv1MsgPayload::Request { req_id, body } =\u003e {\n return WS2PSignal::Request {\n from: ws2p_full_id,\n req_id,\n body,\n };\n }\n WS2Pv1MsgPayload::PeerCard(body, ws2p_endpoints) =\u003e {\n return WS2PSignal::PeerCard(ws2p_full_id, body, ws2p_endpoints);\n }\n WS2Pv1MsgPayload::Heads(heads) =\u003e {\n let mut applied_heads = Vec::with_capacity(heads.len());\n for head in heads {\n if let Ok(head) = NetworkHead::from_json_value(\u0026head) {\n if head.verify()\n \u0026\u0026 (ws2p_module.my_head.is_none()\n || head.node_full_id()\n != ws2p_module\n .my_head\n .clone()\n .expect(\"WS2P: Fail to clone my_head\")\n .node_full_id())\n \u0026\u0026 head.apply(\u0026mut ws2p_module.heads_cache)\n {\n applied_heads.push(head);\n }\n }\n }\n return WS2PSignal::Heads(ws2p_full_id, applied_heads);\n }\n WS2Pv1MsgPayload::Document(doc) =\u003e match doc {\n DocumentDUBP::Block(block_doc) =\u003e {\n return WS2PSignal::Blocks(ws2p_full_id, vec![block_doc.deref().clone()])\n }\n DocumentDUBP::UserDocument(user_doc) =\u003e {\n return WS2PSignal::UserDocuments(ws2p_full_id, vec![user_doc]);\n }\n },\n WS2Pv1MsgPayload::ReqResponse(ws2p_req_id, response) =\u003e {\n if let Some(WS2Pv1PendingReqInfos {\n ref requester_module,\n ref req_body,\n ref recipient_node,\n ..\n }) = ws2p_module.requests_awaiting_response.remove(\u0026ws2p_req_id)\n {\n return WS2PSignal::ReqResponse(\n *requester_module,\n *req_body,\n *recipient_node,\n response,\n );\n }\n }\n WS2Pv1MsgPayload::NegociationTimeout =\u003e {\n match ws2p_module.ws2p_endpoints[\u0026ws2p_full_id].state {\n WS2PConnectionState::AckMessOk | WS2PConnectionState::ConnectMessOk =\u003e {\n ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\")\n .state = WS2PConnectionState::Denial\n }\n WS2PConnectionState::WaitingConnectMess =\u003e {\n ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\")\n .state = WS2PConnectionState::NoResponse\n }\n _ =\u003e {\n let dal_ep = ws2p_module\n .ws2p_endpoints\n .get_mut(\u0026ws2p_full_id)\n .expect(\"WS2P: Fail to get mut ep !\");\n dal_ep.state = WS2PConnectionState::Unreachable;\n dal_ep.last_check = durs_common_tools::fns::time::current_timestamp();\n }\n }\n close_connection(\n ws2p_module,\n \u0026ws2p_full_id,\n WS2PCloseConnectionReason::NegociationTimeout,\n );\n return WS2PSignal::NegociationTimeout(ws2p_full_id);\n }\n WS2Pv1MsgPayload::Timeout =\u003e {\n close_connection(\n ws2p_module,\n \u0026ws2p_full_id,\n WS2PCloseConnectionReason::Timeout,\n );\n return WS2PSignal::Timeout(ws2p_full_id);\n }\n WS2Pv1MsgPayload::UnknowMessage =\u003e {}\n WS2Pv1MsgPayload::WrongFormatMessage =\u003e warn!(\n \"WS2P : Receive Wrong Format Message from {}.\",\n \u0026ws2p_full_id.1\n ),\n WS2Pv1MsgPayload::InvalidMessage =\u003e return WS2PSignal::Empty,\n WS2Pv1MsgPayload::Close =\u003e close_connection(\n ws2p_module,\n \u0026ws2p_full_id,\n WS2PCloseConnectionReason::AuthMessInvalidSig,\n ),\n }\n let connections_count = ws2p_module.websockets.len();\n if connections_count == 0 {\n return WS2PSignal::NoConnection;\n }\n WS2PSignal::Empty\n}\n\nfn check_timeout_requests(ws2p_module: \u0026mut WS2Pv1Module) {\n // Detect timeout requests\n let mut requests_timeout = Vec::new();\n\n for (ws2p_req_id, pending_req_infos) in ws2p_module.requests_awaiting_response.iter() {\n if unwrap!(SystemTime::now().duration_since(pending_req_infos.timestamp))\n \u003e Duration::from_secs(*WS2P_V1_REQUESTS_TIMEOUT_IN_SECS)\n {\n requests_timeout.push(*ws2p_req_id);\n warn!(\n \"request timeout : {:?} (sent to {:?})\",\n pending_req_infos.req_body, pending_req_infos.recipient_node\n );\n }\n }\n // Delete timeout requests\n for ws2p_req_id in requests_timeout {\n let _request_option = ws2p_module.requests_awaiting_response.remove(\u0026ws2p_req_id);\n }\n}\n","traces":[{"line":59,"address":4352576,"length":1,"stats":{"Line":0}},{"line":65,"address":4352596,"length":1,"stats":{"Line":0}},{"line":66,"address":4352612,"length":1,"stats":{"Line":0}},{"line":67,"address":4352674,"length":1,"stats":{"Line":0}},{"line":68,"address":4352697,"length":1,"stats":{"Line":0}},{"line":69,"address":4352733,"length":1,"stats":{"Line":0}},{"line":71,"address":4352915,"length":1,"stats":{"Line":0}},{"line":73,"address":4353133,"length":1,"stats":{"Line":0}},{"line":77,"address":4353344,"length":1,"stats":{"Line":0}},{"line":81,"address":4353368,"length":1,"stats":{"Line":0}},{"line":83,"address":4353665,"length":1,"stats":{"Line":0}},{"line":84,"address":4354186,"length":1,"stats":{"Line":0}},{"line":85,"address":4353710,"length":1,"stats":{"Line":0}},{"line":88,"address":4353766,"length":1,"stats":{"Line":0}},{"line":92,"address":4353857,"length":1,"stats":{"Line":0}},{"line":93,"address":4353872,"length":1,"stats":{"Line":0}},{"line":94,"address":4353910,"length":1,"stats":{"Line":0}},{"line":97,"address":4354096,"length":1,"stats":{"Line":0}},{"line":104,"address":4354191,"length":1,"stats":{"Line":0}},{"line":108,"address":4354282,"length":1,"stats":{"Line":0}},{"line":109,"address":4354297,"length":1,"stats":{"Line":0}},{"line":111,"address":4354340,"length":1,"stats":{"Line":0}},{"line":112,"address":4354380,"length":1,"stats":{"Line":0}},{"line":114,"address":4354545,"length":1,"stats":{"Line":0}},{"line":115,"address":4354610,"length":1,"stats":{"Line":0}},{"line":119,"address":4354603,"length":1,"stats":{"Line":0}},{"line":120,"address":4354714,"length":1,"stats":{"Line":0}},{"line":121,"address":4355139,"length":1,"stats":{"Line":0}},{"line":122,"address":4355243,"length":1,"stats":{"Line":0}},{"line":123,"address":4355428,"length":1,"stats":{"Line":0}},{"line":127,"address":4355550,"length":1,"stats":{"Line":0}},{"line":131,"address":4355641,"length":1,"stats":{"Line":0}},{"line":132,"address":4355656,"length":1,"stats":{"Line":0}},{"line":135,"address":4355699,"length":1,"stats":{"Line":0}},{"line":136,"address":4355764,"length":1,"stats":{"Line":0}},{"line":140,"address":4355757,"length":1,"stats":{"Line":0}},{"line":141,"address":4355868,"length":1,"stats":{"Line":0}},{"line":143,"address":4355936,"length":1,"stats":{"Line":0}},{"line":144,"address":4356361,"length":1,"stats":{"Line":0}},{"line":145,"address":4356465,"length":1,"stats":{"Line":0}},{"line":146,"address":4356650,"length":1,"stats":{"Line":0}},{"line":149,"address":4356780,"length":1,"stats":{"Line":0}},{"line":153,"address":4358268,"length":1,"stats":{"Line":0}},{"line":154,"address":4358285,"length":1,"stats":{"Line":0}},{"line":158,"address":4358278,"length":1,"stats":{"Line":0}},{"line":159,"address":4358389,"length":1,"stats":{"Line":0}},{"line":160,"address":4358397,"length":1,"stats":{"Line":0}},{"line":161,"address":4358446,"length":1,"stats":{"Line":0}},{"line":162,"address":4358516,"length":1,"stats":{"Line":0}},{"line":164,"address":4358619,"length":1,"stats":{"Line":0}},{"line":165,"address":4358627,"length":1,"stats":{"Line":0}},{"line":168,"address":4358635,"length":1,"stats":{"Line":0}},{"line":170,"address":4358645,"length":1,"stats":{"Line":0}},{"line":172,"address":4358653,"length":1,"stats":{"Line":0}},{"line":176,"address":4358712,"length":1,"stats":{"Line":0}},{"line":178,"address":4358765,"length":1,"stats":{"Line":0}},{"line":179,"address":4358933,"length":1,"stats":{"Line":0}},{"line":180,"address":4358813,"length":1,"stats":{"Line":0}},{"line":181,"address":4358861,"length":1,"stats":{"Line":0}},{"line":182,"address":4358893,"length":1,"stats":{"Line":0}},{"line":185,"address":4359044,"length":1,"stats":{"Line":0}},{"line":186,"address":4359152,"length":1,"stats":{"Line":0}},{"line":188,"address":4359467,"length":1,"stats":{"Line":0}},{"line":189,"address":4359515,"length":1,"stats":{"Line":0}},{"line":190,"address":4359569,"length":1,"stats":{"Line":0}},{"line":191,"address":4360105,"length":1,"stats":{"Line":0}},{"line":192,"address":4360195,"length":1,"stats":{"Line":0}},{"line":193,"address":4360309,"length":1,"stats":{"Line":0}},{"line":194,"address":4360395,"length":1,"stats":{"Line":0}},{"line":195,"address":4360453,"length":1,"stats":{"Line":0}},{"line":199,"address":4360603,"length":1,"stats":{"Line":0}},{"line":200,"address":4360234,"length":1,"stats":{"Line":0}},{"line":202,"address":4360708,"length":1,"stats":{"Line":0}},{"line":206,"address":4360795,"length":1,"stats":{"Line":0}},{"line":208,"address":4361012,"length":1,"stats":{"Line":0}},{"line":209,"address":4361097,"length":1,"stats":{"Line":0}},{"line":210,"address":4361191,"length":1,"stats":{"Line":0}},{"line":212,"address":4361516,"length":1,"stats":{"Line":0}},{"line":213,"address":4361581,"length":1,"stats":{"Line":0}},{"line":216,"address":4361891,"length":1,"stats":{"Line":0}},{"line":217,"address":4361988,"length":1,"stats":{"Line":0}},{"line":218,"address":4362015,"length":1,"stats":{"Line":0}},{"line":219,"address":4362034,"length":1,"stats":{"Line":0}},{"line":220,"address":4362049,"length":1,"stats":{"Line":0}},{"line":222,"address":4361943,"length":1,"stats":{"Line":0}},{"line":224,"address":4362261,"length":1,"stats":{"Line":0}},{"line":225,"address":4362061,"length":1,"stats":{"Line":0}},{"line":226,"address":4362104,"length":1,"stats":{"Line":0}},{"line":227,"address":4362135,"length":1,"stats":{"Line":0}},{"line":228,"address":4362189,"length":1,"stats":{"Line":0}},{"line":233,"address":4362501,"length":1,"stats":{"Line":0}},{"line":234,"address":4362549,"length":1,"stats":{"Line":0}},{"line":235,"address":4362641,"length":1,"stats":{"Line":0}},{"line":242,"address":4362736,"length":1,"stats":{"Line":0}},{"line":249,"address":4362831,"length":1,"stats":{"Line":0}},{"line":253,"address":4362922,"length":1,"stats":{"Line":0}},{"line":254,"address":4362937,"length":1,"stats":{"Line":0}},{"line":258,"address":4362975,"length":1,"stats":{"Line":0}},{"line":260,"address":4362983,"length":1,"stats":{"Line":0}},{"line":262,"address":4363014,"length":1,"stats":{"Line":0}},{"line":266,"address":4363111,"length":1,"stats":{"Line":0}},{"line":268,"address":4363119,"length":1,"stats":{"Line":0}},{"line":270,"address":4363150,"length":1,"stats":{"Line":0}},{"line":273,"address":4363247,"length":1,"stats":{"Line":0}},{"line":275,"address":4363438,"length":1,"stats":{"Line":0}},{"line":277,"address":4363638,"length":1,"stats":{"Line":0}},{"line":279,"address":4363646,"length":1,"stats":{"Line":0}},{"line":281,"address":4363654,"length":1,"stats":{"Line":0}},{"line":284,"address":4363790,"length":1,"stats":{"Line":0}},{"line":285,"address":4363832,"length":1,"stats":{"Line":0}},{"line":286,"address":4363851,"length":1,"stats":{"Line":0}},{"line":288,"address":4363867,"length":1,"stats":{"Line":0}},{"line":291,"address":4366576,"length":1,"stats":{"Line":0}},{"line":293,"address":4366591,"length":1,"stats":{"Line":0}},{"line":295,"address":4366637,"length":1,"stats":{"Line":0}},{"line":296,"address":4366946,"length":1,"stats":{"Line":0}},{"line":297,"address":4367274,"length":1,"stats":{"Line":0}},{"line":299,"address":4367364,"length":1,"stats":{"Line":0}},{"line":300,"address":4367406,"length":1,"stats":{"Line":0}},{"line":302,"address":4367597,"length":1,"stats":{"Line":0}},{"line":307,"address":4366973,"length":1,"stats":{"Line":0}},{"line":308,"address":4368103,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":122},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","meta_datas.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! WS2P connections meta datas.\n\nuse super::messages::WS2Pv1MsgPayload;\nuse super::states::WS2PConnectionState;\nuse crate::ws_connections::requests::{WS2Pv1ReqBody, WS2Pv1ReqId};\nuse crate::*;\nuse dubp_documents::documents::DocumentDUBP;\nuse dubp_documents::parsers::blocks::parse_json_block_from_serde_value;\nuse dup_crypto::keys::*;\nuse durs_network_documents::network_endpoint::{ApiName, EndpointV1};\nuse durs_network_documents::NodeId;\nuse std::convert::TryFrom;\n\n#[allow(deprecated)]\n#[derive(Debug, Clone)]\npub struct WS2PConnectionMetaDatas {\n pub state: WS2PConnectionState,\n pub remote_uuid: Option\u003cNodeId\u003e,\n pub remote_pubkey: Option\u003cPubKey\u003e,\n pub challenge: String,\n pub remote_challenge: String,\n pub current_blockstamp: Option\u003c(u32, String)\u003e,\n}\n\nimpl WS2PConnectionMetaDatas {\n pub fn new(challenge: String) -\u003e Self {\n WS2PConnectionMetaDatas {\n state: WS2PConnectionState::WaitingConnectMess,\n remote_uuid: None,\n remote_pubkey: None,\n challenge,\n remote_challenge: \"\".to_string(),\n current_blockstamp: None,\n }\n }\n\n pub fn node_full_id(\u0026self) -\u003e NodeFullId {\n NodeFullId(\n self.clone()\n .remote_uuid\n .expect(\"Fail to get NodeFullId : remote_uuid is None !\"),\n self.remote_pubkey\n .expect(\"Fail to get NodeFullId : remote_pubkey is None !\"),\n )\n }\n pub fn parse_and_check_incoming_message(\n \u0026mut self,\n currency: \u0026str,\n key_pair: KeyPairEnum,\n msg: \u0026serde_json::Value,\n ) -\u003e WS2Pv1MsgPayload {\n if let Some(s) = msg.get(\"auth\") {\n if s.is_string() {\n match s.as_str().unwrap_or(\"\") {\n \"CONNECT\" =\u003e {\n let message = WS2PConnectMessageV1::parse(msg, currency.to_string())\n .expect(\"Failed to parsing CONNECT Message !\");\n if message.verify() \u0026\u0026 message.pubkey == unwrap!(self.remote_pubkey) {\n match self.state {\n WS2PConnectionState::WaitingConnectMess =\u003e {\n debug!(\"CONNECT sig is valid.\");\n self.state = WS2PConnectionState::ConnectMessOk;\n self.remote_challenge = message.challenge.clone();\n let mut response = WS2PAckMessageV1 {\n currency: currency.to_string(),\n pubkey: key_pair.public_key(),\n challenge: self.remote_challenge.clone(),\n signature: None,\n };\n response.signature = Some(response.sign(key_pair));\n return WS2Pv1MsgPayload::ValidConnectMessage(\n unwrap!(serde_json::to_string(\u0026response)),\n self.state,\n );\n }\n _ =\u003e return WS2Pv1MsgPayload::InvalidMessage,\n }\n } else {\n warn!(\"The signature of message CONNECT is invalid !\")\n }\n }\n \"ACK\" =\u003e {\n let mut message = WS2PAckMessageV1::parse(msg, currency.to_string())\n .expect(\"Failed to parsing ACK Message !\");\n message.challenge = self.challenge.to_string();\n if message.verify() {\n trace!(\"ACK sig is valid.\");\n self.state = match self.state {\n WS2PConnectionState::ConnectMessOk =\u003e {\n WS2PConnectionState::AckMessOk\n }\n WS2PConnectionState::OkMessOkWaitingAckMess =\u003e {\n WS2PConnectionState::Established\n }\n _ =\u003e return WS2Pv1MsgPayload::InvalidMessage,\n };\n let mut response = WS2POkMessageV1 {\n currency: currency.to_string(),\n pubkey: key_pair.public_key(),\n challenge: self.challenge.to_string(),\n signature: None,\n };\n response.signature = Some(response.sign(key_pair));\n return WS2Pv1MsgPayload::ValidAckMessage(\n unwrap!(serde_json::to_string(\u0026response)),\n self.state,\n );\n } else {\n warn!(\"The signature of message ACK is invalid !\")\n }\n }\n \"OK\" =\u003e {\n let mut message = WS2POkMessageV1::parse(msg, currency.to_string())\n .expect(\"Failed to parsing OK Message !\");\n trace!(\"Received OK\");\n message.challenge = self.remote_challenge.to_string();\n message.pubkey = self.remote_pubkey.expect(\"fail to get remote pubkey !\");\n if message.verify() {\n trace!(\"OK sig is valid.\");\n match self.state {\n WS2PConnectionState::ConnectMessOk =\u003e {\n self.state = WS2PConnectionState::OkMessOkWaitingAckMess;\n return WS2Pv1MsgPayload::ValidOk(self.state);\n }\n WS2PConnectionState::AckMessOk =\u003e {\n info!(\n \"WS2P Connection established with the key {}\",\n self.remote_pubkey.expect(\"fail to get remote pubkey !\")\n );\n self.state = WS2PConnectionState::Established;\n return WS2Pv1MsgPayload::ValidOk(self.state);\n }\n _ =\u003e {\n warn!(\"WS2P Error : OK message not expected !\");\n return WS2Pv1MsgPayload::InvalidMessage;\n }\n }\n } else {\n warn!(\"The signature of message OK is invalid !\");\n return WS2Pv1MsgPayload::InvalidMessage;\n }\n }\n \u0026_ =\u003e debug!(\"unknow message\"),\n };\n }\n };\n if let Some(req_id) = msg.get(\"reqId\") {\n match req_id.as_str() {\n Some(req_id) =\u003e match msg.get(\"body\") {\n Some(body) =\u003e {\n trace!(\"WS2P : Receive DAL Request from {}.\", self.node_full_id());\n\n let req_id = match WS2Pv1ReqId::from_str(req_id) {\n Ok(req_id) =\u003e req_id,\n Err(_) =\u003e {\n warn!(\n \"WS2Pv1: receive invalid request: invalid req_id: '{}'\",\n req_id\n );\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n };\n\n match WS2Pv1ReqBody::try_from(body) {\n Ok(body) =\u003e {\n return WS2Pv1MsgPayload::Request { req_id, body };\n }\n Err(_) =\u003e {\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n }\n }\n None =\u003e {\n warn!(\"WS2P Error : invalid format : Request must contain a field body !\");\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n },\n None =\u003e {\n warn!(\"WS2P Error : invalid format : Request must contain a field body !\");\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n }\n }\n if let Some(req_id) = msg.get(\"resId\") {\n match req_id.as_str() {\n Some(req_id_str) =\u003e match msg.get(\"body\") {\n Some(body) =\u003e match WS2Pv1ReqId::from_str(req_id_str) {\n Ok(req_id) =\u003e {\n return WS2Pv1MsgPayload::ReqResponse(req_id, body.clone());\n }\n Err(_) =\u003e {\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n },\n None =\u003e match msg.get(\"err\") {\n Some(err) =\u003e warn!(\"Error in req : {:?}\", err),\n None =\u003e {\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n },\n },\n None =\u003e {\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n }\n }\n if let Some(body) = msg.get(\"body\") {\n match body.get(\"name\") {\n Some(s) =\u003e {\n if s.is_string() {\n match s.as_str().unwrap_or(\"\") {\n \"BLOCK\" =\u003e match body.get(\"block\") {\n Some(block) =\u003e match parse_json_block_from_serde_value(\u0026block) {\n Ok(block_doc) =\u003e {\n return WS2Pv1MsgPayload::Document(DocumentDUBP::Block(\n Box::new(block_doc),\n ))\n }\n Err(e) =\u003e info!(\"WS2Pv1Signal: receive invalid block: {}\", e),\n },\n None =\u003e return WS2Pv1MsgPayload::WrongFormatMessage,\n },\n \"HEAD\" =\u003e match body.get(\"heads\") {\n Some(heads) =\u003e match heads.as_array() {\n Some(heads_array) =\u003e {\n return WS2Pv1MsgPayload::Heads(heads_array.clone());\n }\n None =\u003e return WS2Pv1MsgPayload::WrongFormatMessage,\n },\n None =\u003e return WS2Pv1MsgPayload::WrongFormatMessage,\n },\n \"PEER\" =\u003e return self.parse_and_check_peer_message(body),\n \"CERTIFICATION\" =\u003e {\n trace!(\n \"WS2P : Receive CERTIFICATION from {}.\",\n self.node_full_id()\n );\n /*return WS2Pv1MsgPayload::Document(\n BlockchainDocument::Certification(_)\n );*/\n }\n \"IDENTITY\" =\u003e {\n trace!(\"WS2P : Receive IDENTITY from {}.\", self.node_full_id());\n /*return WS2Pv1MsgPayload::Document(\n BlockchainDocument::Identity(_)\n );*/\n }\n \"MEMBERSHIP\" =\u003e {\n trace!(\"WS2P : Receive MEMBERSHIP from {}.\", self.node_full_id());\n /*return WS2Pv1MsgPayload::Document(\n BlockchainDocument::Membership(_)\n );*/\n }\n \"TRANSACTION\" =\u003e {\n trace!(\"WS2P : Receive TRANSACTION from {}.\", self.node_full_id());\n /*return WS2Pv1MsgPayload::Document(\n BlockchainDocument::Transaction(_)\n );*/\n }\n name =\u003e {\n warn!(\n \"WS2P : Receive unknown document name '{}' from '{}'.\",\n name,\n self.node_full_id()\n );\n return WS2Pv1MsgPayload::UnknowMessage;\n }\n };\n }\n }\n None =\u003e {\n warn!(\"WS2P Error : invalid format : Body must contain a field name !\");\n return WS2Pv1MsgPayload::WrongFormatMessage;\n }\n }\n };\n debug!(\n \"WS2P : Receive unknown message from '{}'.\",\n self.node_full_id()\n );\n WS2Pv1MsgPayload::UnknowMessage\n }\n\n pub fn parse_and_check_peer_message(\u0026mut self, body: \u0026serde_json::Value) -\u003e WS2Pv1MsgPayload {\n match body.get(\"peer\") {\n Some(peer) =\u003e match peer.get(\"pubkey\") {\n Some(raw_pubkey) =\u003e {\n match ed25519::PublicKey::from_base58(raw_pubkey.as_str().unwrap_or(\"\")) {\n Ok(pubkey) =\u003e {\n let mut ws2p_endpoints: Vec\u003cEndpointV1\u003e = Vec::new();\n match peer.get(\"endpoints\") {\n Some(endpoints) =\u003e match endpoints.as_array() {\n Some(array_endpoints) =\u003e {\n for endpoint in array_endpoints {\n if let Ok(ep) = EndpointV1::parse_from_raw(\n endpoint.as_str().unwrap_or(\"\"),\n PubKey::Ed25519(pubkey),\n 0,\n 0,\n ) {\n if ep.api == ApiName(String::from(\"WS2P\")) {\n ws2p_endpoints.push(ep);\n }\n }\n }\n WS2Pv1MsgPayload::PeerCard(body.clone(), ws2p_endpoints)\n }\n None =\u003e WS2Pv1MsgPayload::WrongFormatMessage,\n },\n None =\u003e WS2Pv1MsgPayload::WrongFormatMessage,\n }\n }\n Err(_) =\u003e WS2Pv1MsgPayload::WrongFormatMessage,\n }\n }\n None =\u003e WS2Pv1MsgPayload::WrongFormatMessage,\n },\n None =\u003e WS2Pv1MsgPayload::WrongFormatMessage,\n }\n }\n}\n","traces":[{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":206,"address":null,"length":0,"stats":{"Line":0}},{"line":207,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":223,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":0}},{"line":230,"address":null,"length":0,"stats":{"Line":0}},{"line":231,"address":null,"length":0,"stats":{"Line":0}},{"line":234,"address":null,"length":0,"stats":{"Line":0}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":238,"address":null,"length":0,"stats":{"Line":0}},{"line":239,"address":null,"length":0,"stats":{"Line":0}},{"line":240,"address":null,"length":0,"stats":{"Line":0}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":247,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":249,"address":null,"length":0,"stats":{"Line":0}},{"line":251,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":0}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":269,"address":null,"length":0,"stats":{"Line":0}},{"line":270,"address":null,"length":0,"stats":{"Line":0}},{"line":275,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":279,"address":null,"length":0,"stats":{"Line":0}},{"line":281,"address":null,"length":0,"stats":{"Line":0}},{"line":286,"address":null,"length":0,"stats":{"Line":0}},{"line":287,"address":null,"length":0,"stats":{"Line":0}},{"line":288,"address":null,"length":0,"stats":{"Line":0}},{"line":292,"address":null,"length":0,"stats":{"Line":0}},{"line":294,"address":null,"length":0,"stats":{"Line":0}},{"line":296,"address":null,"length":0,"stats":{"Line":0}},{"line":299,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":303,"address":null,"length":0,"stats":{"Line":0}},{"line":304,"address":null,"length":0,"stats":{"Line":0}},{"line":305,"address":null,"length":0,"stats":{"Line":0}},{"line":306,"address":null,"length":0,"stats":{"Line":0}},{"line":307,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":311,"address":null,"length":0,"stats":{"Line":0}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":313,"address":null,"length":0,"stats":{"Line":0}},{"line":314,"address":null,"length":0,"stats":{"Line":0}},{"line":316,"address":null,"length":0,"stats":{"Line":0}},{"line":317,"address":null,"length":0,"stats":{"Line":0}},{"line":321,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":0}},{"line":325,"address":null,"length":0,"stats":{"Line":0}},{"line":328,"address":null,"length":0,"stats":{"Line":0}},{"line":331,"address":null,"length":0,"stats":{"Line":0}},{"line":333,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":180},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Manage websockets connections.\n\npub mod handler;\npub mod messages;\nmod meta_datas;\npub mod requests;\npub mod responses;\npub mod states;\n\nuse crate::*;\nuse dup_crypto::keys::*;\nuse durs_network_documents::network_endpoint::EndpointV1;\nuse rand::Rng;\nuse states::WS2PConnectionState;\nuse std::cmp::Ordering;\nuse std::collections::HashSet;\n#[allow(deprecated)]\nuse ws::Sender;\n\n/// Store a websocket sender\npub struct WsSender(pub Sender);\n\nimpl ::std::fmt::Debug for WsSender {\n fn fmt(\u0026self, f: \u0026mut ::std::fmt::Formatter) -\u003e ::std::fmt::Result {\n write!(f, \"WsSender {{ }}\")\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq)]\npub enum WS2PCloseConnectionReason {\n AuthMessInvalidSig,\n NegociationTimeout,\n Timeout,\n WsError,\n Unknow,\n}\n\npub fn connect_to_know_endpoints(ws2p_module: \u0026mut WS2Pv1Module) {\n info!(\"WS2P: connect to know endpoints...\");\n let mut count_established_connections = 0;\n let mut pubkeys = HashSet::new();\n let mut reachable_endpoints = Vec::new();\n let mut unreachable_endpoints = Vec::new();\n for (_ws2p_full_id, DbEndpoint { ep, state, .. }) in ws2p_module.ws2p_endpoints.clone() {\n if ep.issuer == ws2p_module.key_pair.public_key() || !pubkeys.contains(\u0026ep.issuer) {\n match state {\n WS2PConnectionState::Established =\u003e count_established_connections += 1,\n WS2PConnectionState::NeverTry\n | WS2PConnectionState::Close\n | WS2PConnectionState::Denial =\u003e {\n pubkeys.insert(ep.issuer);\n if ws2p_module.ssl || ep.port != 443 {\n reachable_endpoints.push(ep);\n }\n }\n _ =\u003e {\n pubkeys.insert(ep.issuer);\n unreachable_endpoints.push(ep);\n }\n }\n }\n }\n if !ws2p_module.conf.prefered_pubkeys.is_empty() {\n reachable_endpoints.sort_unstable_by(|ep1, ep2| {\n if ws2p_module.conf.prefered_pubkeys.contains(\u0026ep1.issuer) {\n if ws2p_module.conf.prefered_pubkeys.contains(\u0026ep2.issuer) {\n Ordering::Equal\n } else {\n Ordering::Greater\n }\n } else {\n Ordering::Less\n }\n });\n }\n let mut free_outcoming_rooms =\n ws2p_module.conf.clone().outcoming_quota - count_established_connections;\n while free_outcoming_rooms \u003e 0 {\n let ep = if !reachable_endpoints.is_empty() {\n reachable_endpoints\n .pop()\n .expect(\"WS2P: Fail to pop() reachable_endpoints !\")\n } else if !unreachable_endpoints.is_empty() {\n unreachable_endpoints\n .pop()\n .expect(\"WS2P: Fail to pop() unreachable_endpoints !\")\n } else {\n break;\n };\n connect_to_without_checking_quotas(ws2p_module, unwrap!(ep.node_full_id()));\n free_outcoming_rooms -= 1;\n }\n}\n\npub fn connect_to(ws2p_module: \u0026mut WS2Pv1Module, ep: \u0026EndpointV1) {\n // Add endpoint to endpoints list (if there isn't already)\n let node_full_id = ep\n .node_full_id()\n .expect(\"WS2P: Fail to get ep.node_full_id() !\");\n ws2p_module\n .ws2p_endpoints\n .entry(node_full_id)\n .or_insert(DbEndpoint {\n ep: ep.clone(),\n state: WS2PConnectionState::NeverTry,\n last_check: 0,\n });\n let count_established_connections = count_established_connections(\u0026ws2p_module);\n if ws2p_module.conf.outcoming_quota \u003e count_established_connections {\n connect_to_without_checking_quotas(ws2p_module, node_full_id);\n }\n}\n\npub fn connect_to_without_checking_quotas(\n ws2p_module: \u0026mut WS2Pv1Module,\n node_full_id: NodeFullId,\n) {\n let endpoint = unwrap!(ws2p_module.ws2p_endpoints.get(\u0026node_full_id));\n let endpoint_copy = endpoint.ep.clone();\n let conductor_sender_copy = ws2p_module.main_thread_channel.0.clone();\n let currency_copy = ws2p_module.conf.currency.clone();\n let key_pair_copy = ws2p_module.key_pair;\n thread::spawn(move || {\n let _result = crate::ws_connections::handler::connect_to_ws2p_endpoint(\n \u0026endpoint_copy,\n \u0026conductor_sender_copy,\n \u0026currency_copy.expect(\"WS2PError : No currency !\").0,\n key_pair_copy,\n );\n });\n}\n\npub fn close_connection(\n ws2p_module: \u0026mut WS2Pv1Module,\n ws2p_full_id: \u0026NodeFullId,\n reason: WS2PCloseConnectionReason,\n) {\n match reason {\n WS2PCloseConnectionReason::NegociationTimeout =\u003e {}\n WS2PCloseConnectionReason::AuthMessInvalidSig\n | WS2PCloseConnectionReason::Timeout\n | WS2PCloseConnectionReason::WsError\n | WS2PCloseConnectionReason::Unknow =\u003e {\n if let Some(dal_ep) = ws2p_module.ws2p_endpoints.get_mut(ws2p_full_id) {\n dal_ep.state = WS2PConnectionState::Close;\n dal_ep.last_check = durs_common_tools::fns::time::current_timestamp();\n }\n }\n }\n if let Some(websocket) = ws2p_module.websockets.get(\u0026ws2p_full_id) {\n let _result = websocket.0.close(ws::CloseCode::Normal);\n }\n let _result = ws2p_module.websockets.remove(ws2p_full_id);\n}\n\npub fn get_random_connection\u003cS: ::std::hash::BuildHasher\u003e(\n connections: HashSet\u003c\u0026NodeFullId, S\u003e,\n) -\u003e NodeFullId {\n let mut rng = rand::thread_rng();\n let mut loop_count = 0;\n loop {\n for ws2p_full_id in \u0026connections {\n if loop_count \u003e 10 {\n return **ws2p_full_id;\n }\n if rng.gen::\u003cbool\u003e() {\n return **ws2p_full_id;\n }\n }\n loop_count += 1;\n }\n}\n\npub fn count_established_connections(ws2p_module: \u0026WS2Pv1Module) -\u003e usize {\n let mut count_established_connections = 0;\n for DbEndpoint { state, .. } in ws2p_module.ws2p_endpoints.values() {\n if let WS2PConnectionState::Established = state {\n count_established_connections += 1;\n }\n }\n count_established_connections\n}\n","traces":[{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":4238832,"length":1,"stats":{"Line":0}},{"line":54,"address":4238854,"length":1,"stats":{"Line":0}},{"line":55,"address":4239150,"length":1,"stats":{"Line":0}},{"line":56,"address":4239162,"length":1,"stats":{"Line":0}},{"line":57,"address":4239183,"length":1,"stats":{"Line":0}},{"line":58,"address":4239198,"length":1,"stats":{"Line":0}},{"line":59,"address":4239223,"length":1,"stats":{"Line":0}},{"line":60,"address":4239689,"length":1,"stats":{"Line":0}},{"line":61,"address":4239936,"length":1,"stats":{"Line":0}},{"line":62,"address":4239807,"length":1,"stats":{"Line":0}},{"line":66,"address":4239941,"length":1,"stats":{"Line":0}},{"line":67,"address":4240017,"length":1,"stats":{"Line":0}},{"line":68,"address":4240078,"length":1,"stats":{"Line":0}},{"line":72,"address":4240165,"length":1,"stats":{"Line":0}},{"line":73,"address":4240238,"length":1,"stats":{"Line":0}},{"line":78,"address":4240378,"length":1,"stats":{"Line":0}},{"line":79,"address":4240424,"length":1,"stats":{"Line":0}},{"line":80,"address":4376147,"length":1,"stats":{"Line":0}},{"line":81,"address":4376193,"length":1,"stats":{"Line":0}},{"line":82,"address":4376242,"length":1,"stats":{"Line":0}},{"line":84,"address":4376249,"length":1,"stats":{"Line":0}},{"line":87,"address":4376256,"length":1,"stats":{"Line":0}},{"line":92,"address":4240484,"length":1,"stats":{"Line":0}},{"line":93,"address":4240586,"length":1,"stats":{"Line":0}},{"line":94,"address":4240625,"length":1,"stats":{"Line":0}},{"line":95,"address":4240664,"length":1,"stats":{"Line":0}},{"line":98,"address":4240716,"length":1,"stats":{"Line":0}},{"line":99,"address":4240755,"length":1,"stats":{"Line":0}},{"line":103,"address":4240799,"length":1,"stats":{"Line":0}},{"line":105,"address":4240804,"length":1,"stats":{"Line":0}},{"line":106,"address":4240972,"length":1,"stats":{"Line":0}},{"line":110,"address":4241632,"length":1,"stats":{"Line":0}},{"line":112,"address":4241649,"length":1,"stats":{"Line":0}},{"line":115,"address":4241695,"length":1,"stats":{"Line":0}},{"line":118,"address":4241794,"length":1,"stats":{"Line":0}},{"line":119,"address":4241768,"length":1,"stats":{"Line":0}},{"line":120,"address":4241786,"length":1,"stats":{"Line":0}},{"line":123,"address":4241885,"length":1,"stats":{"Line":0}},{"line":124,"address":4241903,"length":1,"stats":{"Line":0}},{"line":125,"address":4241922,"length":1,"stats":{"Line":0}},{"line":129,"address":4242000,"length":1,"stats":{"Line":0}},{"line":133,"address":4242012,"length":1,"stats":{"Line":0}},{"line":134,"address":4242152,"length":1,"stats":{"Line":0}},{"line":135,"address":4242181,"length":1,"stats":{"Line":0}},{"line":136,"address":4242246,"length":1,"stats":{"Line":0}},{"line":137,"address":4242266,"length":1,"stats":{"Line":0}},{"line":138,"address":4242374,"length":1,"stats":{"Line":0}},{"line":139,"address":4376579,"length":1,"stats":{"Line":0}},{"line":140,"address":4376279,"length":1,"stats":{"Line":0}},{"line":141,"address":4376282,"length":1,"stats":{"Line":0}},{"line":142,"address":4376292,"length":1,"stats":{"Line":0}},{"line":143,"address":4376444,"length":1,"stats":{"Line":0}},{"line":148,"address":4242800,"length":1,"stats":{"Line":0}},{"line":153,"address":4242991,"length":1,"stats":{"Line":0}},{"line":154,"address":4242823,"length":1,"stats":{"Line":0}},{"line":159,"address":4242888,"length":1,"stats":{"Line":0}},{"line":160,"address":4242952,"length":1,"stats":{"Line":0}},{"line":161,"address":4242964,"length":1,"stats":{"Line":0}},{"line":165,"address":4242993,"length":1,"stats":{"Line":0}},{"line":166,"address":4243057,"length":1,"stats":{"Line":0}},{"line":168,"address":4243116,"length":1,"stats":{"Line":0}},{"line":189,"address":4243184,"length":1,"stats":{"Line":0}},{"line":190,"address":4243196,"length":1,"stats":{"Line":0}},{"line":191,"address":4243205,"length":1,"stats":{"Line":0}},{"line":192,"address":4243407,"length":1,"stats":{"Line":0}},{"line":193,"address":4243441,"length":1,"stats":{"Line":0}},{"line":196,"address":4243428,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":69},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","requests","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the WS2Pv1 requests sent and received.\n\npub mod received;\npub mod sent;\n\nuse dubp_documents::BlockNumber;\nuse durs_network_documents::NodeFullId;\nuse serde::Serialize;\nuse std::convert::TryFrom;\nuse uuid::Uuid;\n\n#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, Serialize)]\npub struct WS2Pv1ReqId(pub Uuid);\n\nimpl WS2Pv1ReqId {\n #[inline]\n pub fn random() -\u003e Self {\n WS2Pv1ReqId(Uuid::new_v4())\n }\n #[inline]\n pub fn to_hyphenated_string(\u0026self) -\u003e String {\n self.0.to_hyphenated().to_string()\n }\n}\n\nimpl std::str::FromStr for WS2Pv1ReqId {\n type Err = uuid::parser::ParseError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n Ok(WS2Pv1ReqId(Uuid::parse_str(source)?))\n }\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct WS2Pv1ReqFullId {\n pub from: NodeFullId,\n pub req_id: WS2Pv1ReqId,\n}\n\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n/// WS2Pv1 requet\npub struct WS2Pv1Request {\n pub id: WS2Pv1ReqId,\n pub body: WS2Pv1ReqBody,\n}\n\n#[derive(Copy, Clone, Debug, Eq, PartialEq)]\n/// WS2Pv1 requets body\npub enum WS2Pv1ReqBody {\n /// get current block\n GetCurrent,\n /// Get one block\n GetBlock {\n /// Block number\n number: BlockNumber,\n },\n /// Get a chunk of blocks\n GetBlocks {\n /// Number of blocks\n count: u32,\n /// First block number\n from_number: BlockNumber,\n },\n /// Get wot mempool\n GetRequirementsPending {\n /// The identities transmitted must have at least `minCert` certifications\n min_cert: usize,\n },\n}\n\n#[derive(Copy, Clone, Debug)]\npub struct WS2Pv1InvalidReqError;\n\nimpl TryFrom\u003c\u0026serde_json::Value\u003e for WS2Pv1ReqBody {\n type Error = WS2Pv1InvalidReqError;\n\n fn try_from(json: \u0026serde_json::Value) -\u003e Result\u003cWS2Pv1ReqBody, WS2Pv1InvalidReqError\u003e {\n let req_name = json.get(\"name\").ok_or(WS2Pv1InvalidReqError)?;\n match req_name.as_str().ok_or(WS2Pv1InvalidReqError)? {\n \"CURRENT\" =\u003e Ok(WS2Pv1ReqBody::GetCurrent),\n \"BLOCK_BY_NUMBER\" =\u003e {\n let params = json\n .get(\"params\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_object()\n .ok_or(WS2Pv1InvalidReqError)?;\n let number = params\n .get(\"number\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_u64()\n .ok_or(WS2Pv1InvalidReqError)?;\n Ok(WS2Pv1ReqBody::GetBlock {\n number: BlockNumber(u32::try_from(number).map_err(|_| WS2Pv1InvalidReqError)?),\n })\n }\n \"BLOCKS_CHUNK\" =\u003e {\n let params = json\n .get(\"params\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_object()\n .ok_or(WS2Pv1InvalidReqError)?;\n let count = params\n .get(\"count\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_u64()\n .ok_or(WS2Pv1InvalidReqError)?;\n let from_number = params\n .get(\"fromNumber\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_u64()\n .ok_or(WS2Pv1InvalidReqError)?;\n Ok(WS2Pv1ReqBody::GetBlocks {\n count: u32::try_from(count).map_err(|_| WS2Pv1InvalidReqError)?,\n from_number: BlockNumber(\n u32::try_from(from_number).map_err(|_| WS2Pv1InvalidReqError)?,\n ),\n })\n }\n \"WOT_REQUIREMENTS_OF_PENDING\" =\u003e {\n let params = json\n .get(\"params\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_object()\n .ok_or(WS2Pv1InvalidReqError)?;\n let min_cert = params\n .get(\"minCert\")\n .ok_or(WS2Pv1InvalidReqError)?\n .as_u64()\n .ok_or(WS2Pv1InvalidReqError)?;\n Ok(WS2Pv1ReqBody::GetRequirementsPending {\n min_cert: usize::try_from(min_cert).map_err(|_| WS2Pv1InvalidReqError)?,\n })\n }\n _ =\u003e Err(WS2Pv1InvalidReqError),\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n use serde_json::json;\n\n #[test]\n fn parse_ws2p_v1_req_get_current() -\u003e Result\u003c(), WS2Pv1InvalidReqError\u003e {\n let json_req_body = json!({\n \"name\": \"CURRENT\",\n \"params\": {}\n });\n\n let parsed_req = WS2Pv1ReqBody::try_from(\u0026json_req_body)?;\n\n assert_eq!(WS2Pv1ReqBody::GetCurrent, parsed_req);\n\n Ok(())\n }\n\n #[test]\n fn parse_ws2p_v1_req_get_block() -\u003e Result\u003c(), WS2Pv1InvalidReqError\u003e {\n let json_req_body = json!({\n \"name\": \"BLOCK_BY_NUMBER\",\n \"params\": {\n \"number\": 42,\n }\n });\n\n let parsed_req = WS2Pv1ReqBody::try_from(\u0026json_req_body)?;\n\n assert_eq!(\n WS2Pv1ReqBody::GetBlock {\n number: BlockNumber(42),\n },\n parsed_req\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_ws2p_v1_req_get_blocks() -\u003e Result\u003c(), WS2Pv1InvalidReqError\u003e {\n let json_req_body = json!({\n \"name\": \"BLOCKS_CHUNK\",\n \"params\": {\n \"count\": 50,\n \"fromNumber\": 100,\n }\n });\n\n let parsed_req = WS2Pv1ReqBody::try_from(\u0026json_req_body)?;\n\n assert_eq!(\n WS2Pv1ReqBody::GetBlocks {\n count: 50,\n from_number: BlockNumber(100),\n },\n parsed_req\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_ws2p_v1_req_get_requirements_pending() -\u003e Result\u003c(), WS2Pv1InvalidReqError\u003e {\n let json_req_body = json!({\n \"name\": \"WOT_REQUIREMENTS_OF_PENDING\",\n \"params\": {\n \"minCert\": 3,\n }\n });\n\n let parsed_req = WS2Pv1ReqBody::try_from(\u0026json_req_body)?;\n\n assert_eq!(\n WS2Pv1ReqBody::GetRequirementsPending { min_cert: 3 },\n parsed_req\n );\n\n Ok(())\n }\n}\n","traces":[{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":1}},{"line":37,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":1}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":1}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":1}},{"line":117,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":1}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":1}},{"line":127,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":1}},{"line":129,"address":null,"length":0,"stats":{"Line":1}},{"line":130,"address":null,"length":0,"stats":{"Line":1}},{"line":134,"address":null,"length":0,"stats":{"Line":1}},{"line":135,"address":null,"length":0,"stats":{"Line":1}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":1}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":1}},{"line":140,"address":null,"length":0,"stats":{"Line":1}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":1}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":4210432,"length":1,"stats":{"Line":2}},{"line":162,"address":4210439,"length":1,"stats":{"Line":1}},{"line":167,"address":4210888,"length":1,"stats":{"Line":1}},{"line":169,"address":4211075,"length":1,"stats":{"Line":1}},{"line":171,"address":4211480,"length":1,"stats":{"Line":1}},{"line":175,"address":4211680,"length":1,"stats":{"Line":2}},{"line":176,"address":4211687,"length":1,"stats":{"Line":1}},{"line":183,"address":4212348,"length":1,"stats":{"Line":1}},{"line":185,"address":4212535,"length":1,"stats":{"Line":1}},{"line":192,"address":4212940,"length":1,"stats":{"Line":1}},{"line":196,"address":4213216,"length":1,"stats":{"Line":2}},{"line":197,"address":4213223,"length":1,"stats":{"Line":1}},{"line":205,"address":4214046,"length":1,"stats":{"Line":1}},{"line":207,"address":4214233,"length":1,"stats":{"Line":1}},{"line":215,"address":4214638,"length":1,"stats":{"Line":1}},{"line":219,"address":4214992,"length":1,"stats":{"Line":2}},{"line":220,"address":4214999,"length":1,"stats":{"Line":1}},{"line":227,"address":4215660,"length":1,"stats":{"Line":1}},{"line":229,"address":4215847,"length":1,"stats":{"Line":1}},{"line":234,"address":4216252,"length":1,"stats":{"Line":1}}],"covered":60,"coverable":77},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","requests","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the WS2Pv1 requests received.\n\nuse crate::requests::sent::send_dal_request;\nuse crate::ws_connections::requests::{WS2Pv1ReqBody, WS2Pv1ReqFullId, WS2Pv1ReqId};\nuse crate::ws_connections::responses::{WS2Pv1ReqRes, WS2Pv1ReqResBody};\nuse crate::WS2Pv1Module;\nuse durs_message::requests::BlockchainRequest;\nuse durs_network_documents::NodeFullId;\n\npub fn receive_ws2p_v1_request(\n ws2p_module: \u0026mut WS2Pv1Module,\n from: NodeFullId,\n ws2p_req_id: WS2Pv1ReqId,\n req_boby: WS2Pv1ReqBody,\n) {\n let module_req_id_opt = match req_boby {\n WS2Pv1ReqBody::GetCurrent =\u003e Some(send_dal_request(\n ws2p_module,\n \u0026BlockchainRequest::CurrentBlock,\n )),\n WS2Pv1ReqBody::GetBlock { number } =\u003e Some(send_dal_request(\n ws2p_module,\n \u0026BlockchainRequest::BlockByNumber {\n block_number: number,\n },\n )),\n WS2Pv1ReqBody::GetBlocks { from_number, count } =\u003e Some(send_dal_request(\n ws2p_module,\n \u0026BlockchainRequest::Chunk {\n first_block_number: from_number,\n count,\n },\n )),\n WS2Pv1ReqBody::GetRequirementsPending { .. } =\u003e {\n crate::ws_connections::responses::sent::send_response(\n ws2p_module,\n from,\n WS2Pv1ReqRes {\n req_id: ws2p_req_id,\n body: WS2Pv1ReqResBody::GetRequirementsPending { identities: vec![] },\n },\n );\n None\n }\n };\n\n if let Some(module_req_id) = module_req_id_opt {\n ws2p_module.pending_received_requests.insert(\n module_req_id,\n WS2Pv1ReqFullId {\n from,\n req_id: ws2p_req_id,\n },\n );\n }\n}\n","traces":[{"line":25,"address":5553008,"length":1,"stats":{"Line":0}},{"line":31,"address":5553278,"length":1,"stats":{"Line":0}},{"line":32,"address":5553023,"length":1,"stats":{"Line":0}},{"line":33,"address":5553109,"length":1,"stats":{"Line":0}},{"line":36,"address":5553161,"length":1,"stats":{"Line":0}},{"line":37,"address":5553171,"length":1,"stats":{"Line":0}},{"line":38,"address":5553186,"length":1,"stats":{"Line":0}},{"line":39,"address":5553179,"length":1,"stats":{"Line":0}},{"line":42,"address":5553288,"length":1,"stats":{"Line":0}},{"line":43,"address":5553308,"length":1,"stats":{"Line":0}},{"line":44,"address":5553330,"length":1,"stats":{"Line":0}},{"line":45,"address":5553316,"length":1,"stats":{"Line":0}},{"line":46,"address":5553323,"length":1,"stats":{"Line":0}},{"line":50,"address":5553687,"length":1,"stats":{"Line":0}},{"line":51,"address":5553438,"length":1,"stats":{"Line":0}},{"line":52,"address":5553446,"length":1,"stats":{"Line":0}},{"line":53,"address":5553610,"length":1,"stats":{"Line":0}},{"line":54,"address":5553490,"length":1,"stats":{"Line":0}},{"line":55,"address":5553518,"length":1,"stats":{"Line":0}},{"line":58,"address":5553705,"length":1,"stats":{"Line":0}},{"line":62,"address":5553716,"length":1,"stats":{"Line":0}},{"line":63,"address":5553749,"length":1,"stats":{"Line":0}},{"line":64,"address":5553764,"length":1,"stats":{"Line":0}},{"line":65,"address":5553854,"length":1,"stats":{"Line":0}},{"line":66,"address":5553771,"length":1,"stats":{"Line":0}},{"line":67,"address":5553831,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":26},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","requests","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the WS2Pv1 requests sent.\n\nuse super::{WS2Pv1ReqBody, WS2Pv1Request};\nuse crate::{WS2Pv1Module, WS2Pv1PendingReqInfos};\nuse durs_module::ModuleReqFullId;\nuse durs_network_documents::NodeFullId;\nuse std::time::SystemTime;\nuse ws::Message;\n\npub fn send_request_to_specific_node(\n ws2p_module: \u0026mut WS2Pv1Module,\n module_req_full_id: ModuleReqFullId,\n ws2p_full_id: \u0026NodeFullId,\n ws2p_request: \u0026WS2Pv1Request,\n) -\u003e ws::Result\u003c()\u003e {\n if let Some(ws) = ws2p_module.websockets.get_mut(ws2p_full_id) {\n let json_req = network_request_to_json(ws2p_request).to_string();\n debug!(\"send request {} to {}\", json_req, ws2p_full_id);\n ws.0.send(Message::text(json_req))?;\n ws2p_module.requests_awaiting_response.insert(\n ws2p_request.id,\n WS2Pv1PendingReqInfos {\n req_body: ws2p_request.body,\n requester_module: module_req_full_id,\n recipient_node: *ws2p_full_id,\n timestamp: SystemTime::now(),\n },\n );\n } else {\n warn!(\"WS2P: Fail to get mut websocket !\");\n }\n Ok(())\n}\n\npub fn network_request_to_json(request: \u0026WS2Pv1Request) -\u003e serde_json::Value {\n let (request_type, request_params) = match request.body {\n WS2Pv1ReqBody::GetCurrent =\u003e (\"CURRENT\", json!({})),\n WS2Pv1ReqBody::GetBlock { ref number } =\u003e (\"BLOCK_BY_NUMBER\", json!({ \"number\": number })),\n WS2Pv1ReqBody::GetBlocks { count, from_number } =\u003e (\n \"BLOCKS_CHUNK\",\n json!({\n \"count\": count,\n \"fromNumber\": from_number\n }),\n ),\n WS2Pv1ReqBody::GetRequirementsPending { min_cert } =\u003e (\n \"WOT_REQUIREMENTS_OF_PENDING\",\n json!({ \"minCert\": min_cert }),\n ),\n };\n\n json!({\n \"reqId\": request.id.to_hyphenated_string(),\n \"body\" : {\n \"name\": request_type,\n \"params\": request_params\n }\n })\n}\n","traces":[{"line":25,"address":4248128,"length":1,"stats":{"Line":0}},{"line":31,"address":4248162,"length":1,"stats":{"Line":0}},{"line":32,"address":4248298,"length":1,"stats":{"Line":0}},{"line":33,"address":4248383,"length":1,"stats":{"Line":0}},{"line":34,"address":4248905,"length":1,"stats":{"Line":0}},{"line":35,"address":4249345,"length":1,"stats":{"Line":0}},{"line":36,"address":4249359,"length":1,"stats":{"Line":0}},{"line":37,"address":4249498,"length":1,"stats":{"Line":0}},{"line":38,"address":4249379,"length":1,"stats":{"Line":0}},{"line":39,"address":4249406,"length":1,"stats":{"Line":0}},{"line":40,"address":4249429,"length":1,"stats":{"Line":0}},{"line":41,"address":4249472,"length":1,"stats":{"Line":0}},{"line":44,"address":4249656,"length":1,"stats":{"Line":0}},{"line":45,"address":4249676,"length":1,"stats":{"Line":0}},{"line":47,"address":4249920,"length":1,"stats":{"Line":0}},{"line":50,"address":4250096,"length":1,"stats":{"Line":1}},{"line":51,"address":4250111,"length":1,"stats":{"Line":1}},{"line":52,"address":4250175,"length":1,"stats":{"Line":1}},{"line":53,"address":4250404,"length":1,"stats":{"Line":0}},{"line":54,"address":4250795,"length":1,"stats":{"Line":1}},{"line":56,"address":4250825,"length":1,"stats":{"Line":1}},{"line":61,"address":4251354,"length":1,"stats":{"Line":0}},{"line":63,"address":4251371,"length":1,"stats":{"Line":0}},{"line":67,"address":4251791,"length":1,"stats":{"Line":1}},{"line":68,"address":4251852,"length":1,"stats":{"Line":1}}],"covered":7,"coverable":25},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","responses","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the WS2Pv1 responses sent and received.\n\npub mod received;\npub mod sent;\n\nuse crate::serializers::IntoWS2Pv1Json;\nuse crate::ws_connections::requests::WS2Pv1ReqId;\nuse dubp_documents::documents::block::BlockDocument;\nuse dubp_documents::ToStringObject;\nuse dup_crypto::keys::PubKey;\n\n/// WS2Pv1 request response\n#[derive(Clone, Debug)]\npub struct WS2Pv1ReqRes {\n /// WS2Pv1 request id\n pub req_id: WS2Pv1ReqId,\n /// WS2Pv1 request response body\n pub body: WS2Pv1ReqResBody,\n}\n\nimpl Into\u003cserde_json::Value\u003e for WS2Pv1ReqRes {\n fn into(self) -\u003e serde_json::Value {\n let mut map = serde_json::map::Map::with_capacity(2);\n map.insert(\n \"resId\".to_owned(),\n self.req_id.to_hyphenated_string().into(),\n );\n map.insert(\"body\".to_owned(), self.body.into());\n serde_json::Value::Object(map)\n }\n}\n\n/// WS2Pv1 request response body\n#[derive(Clone, Debug)]\npub enum WS2Pv1ReqResBody {\n /// Response to request getCurrent\n GetCurrent(BlockDocument),\n // Response to request getBlock\n GetBlock(BlockDocument),\n // Response to request getBlocks\n GetBlocks(Vec\u003cBlockDocument\u003e),\n // Response to request getRequirementsPending\n GetRequirementsPending {\n identities: Vec\u003cWS2Pv1IdentityRequirementsPending\u003e,\n },\n}\n\nimpl Into\u003cserde_json::Value\u003e for WS2Pv1ReqResBody {\n fn into(self) -\u003e serde_json::Value {\n match self {\n WS2Pv1ReqResBody::GetCurrent(block_doc) =\u003e {\n block_doc.to_string_object().into_ws2p_v1_json()\n }\n WS2Pv1ReqResBody::GetBlock(block_doc) =\u003e {\n block_doc.to_string_object().into_ws2p_v1_json()\n }\n WS2Pv1ReqResBody::GetBlocks(blocks) =\u003e serde_json::Value::Array(\n blocks\n .iter()\n .map(ToStringObject::to_string_object)\n .map(IntoWS2Pv1Json::into_ws2p_v1_json)\n .collect(),\n ),\n WS2Pv1ReqResBody::GetRequirementsPending { .. } =\u003e {\n let mut map = serde_json::map::Map::with_capacity(1);\n map.insert(\"identities\".to_owned(), serde_json::Value::Array(vec![]));\n serde_json::Value::Object(map)\n }\n }\n }\n}\n\n/// WS2Pv1 Identity requirements pending\n#[derive(Clone, Debug)]\npub struct WS2Pv1IdentityRequirementsPending {\n pub certifications: Vec\u003cWS2pv1CertificationPending\u003e,\n pub expired: bool,\n pub is_sentry: bool,\n pub membership_expires_in: u64,\n pub membership_pending_expires_in: u64,\n // Some fields missing ...\n}\n\n/// WS2Pv1 Certification pending\n#[derive(Copy, Clone, Debug)]\npub struct WS2pv1CertificationPending {\n /// Expires in\n pub expires_in: u64,\n /// From\n pub from: PubKey,\n /// Timestamp\n pub timestamp: u64,\n /// To\n pub to: PubKey,\n}\n","traces":[{"line":37,"address":null,"length":0,"stats":{"Line":0}},{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":22},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","responses","received.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the WS2Pv1 responses received.\n\nuse crate::*;\nuse dubp_documents::parsers::blocks::parse_json_block_from_serde_value;\nuse durs_module::ModuleReqFullId;\nuse durs_network::requests::*;\nuse durs_network_documents::NodeFullId;\n\npub fn receive_response(\n ws2p_module: \u0026mut WS2Pv1Module,\n module_req_full_id: ModuleReqFullId,\n ws2p_req_body: WS2Pv1ReqBody,\n recipient_full_id: NodeFullId,\n response: serde_json::Value,\n) {\n match ws2p_req_body {\n WS2Pv1ReqBody::GetCurrent =\u003e {\n info!(\n \"WS2PSignal::ReceiveCurrent({}, {:?})\",\n (module_req_full_id.1).0,\n ws2p_req_body\n );\n match parse_json_block_from_serde_value(\u0026response) {\n Ok(block) =\u003e {\n crate::responses::sent::send_network_req_response(\n ws2p_module,\n module_req_full_id.0,\n module_req_full_id.1,\n NetworkResponse::CurrentBlock(\n ModuleReqFullId(WS2Pv1Module::name(), module_req_full_id.1),\n recipient_full_id,\n Box::new(block),\n ),\n );\n }\n Err(e) =\u003e warn!(\"WS2Pv1: receive invalid block: {}.\", e),\n }\n }\n WS2Pv1ReqBody::GetBlocks {\n from_number: from, ..\n } =\u003e {\n if response.is_array() {\n let mut chunk = Vec::new();\n for json_block in unwrap!(response.as_array()) {\n match parse_json_block_from_serde_value(json_block) {\n Ok(block) =\u003e chunk.push(block),\n Err(e) =\u003e warn!(\"WS2Pv1Module: Error : fail to parse json block: {}\", e),\n }\n }\n info!(\n \"WS2PSignal::ReceiveChunk({}, {} blocks from {})\",\n (module_req_full_id.1).0,\n chunk.len(),\n from\n );\n debug!(\"Send chunk to followers : {}\", from);\n events::sent::send_network_event(ws2p_module, NetworkEvent::ReceiveBlocks(chunk));\n }\n }\n WS2Pv1ReqBody::GetRequirementsPending { min_cert } =\u003e {\n info!(\n \"WS2PSignal::ReceiveRequirementsPending({}, {})\",\n module_req_full_id.0, min_cert\n );\n debug!(\"----------------------------------------\");\n debug!(\"- BEGIN IDENTITIES PENDING -\");\n debug!(\"----------------------------------------\");\n debug!(\"{:#?}\", response);\n debug!(\"----------------------------------------\");\n debug!(\"- END IDENTITIES PENDING -\");\n debug!(\"----------------------------------------\");\n }\n _ =\u003e {}\n }\n}\n","traces":[{"line":24,"address":4656256,"length":1,"stats":{"Line":0}},{"line":31,"address":4660614,"length":1,"stats":{"Line":0}},{"line":32,"address":4656277,"length":1,"stats":{"Line":0}},{"line":33,"address":4656466,"length":1,"stats":{"Line":0}},{"line":35,"address":4656707,"length":1,"stats":{"Line":0}},{"line":38,"address":4657020,"length":1,"stats":{"Line":0}},{"line":39,"address":4657063,"length":1,"stats":{"Line":0}},{"line":41,"address":4657162,"length":1,"stats":{"Line":0}},{"line":42,"address":4657178,"length":1,"stats":{"Line":0}},{"line":43,"address":4657193,"length":1,"stats":{"Line":0}},{"line":44,"address":4657507,"length":1,"stats":{"Line":0}},{"line":45,"address":4657237,"length":1,"stats":{"Line":0}},{"line":46,"address":4657318,"length":1,"stats":{"Line":0}},{"line":47,"address":4657353,"length":1,"stats":{"Line":0}},{"line":51,"address":4657665,"length":1,"stats":{"Line":0}},{"line":55,"address":4658182,"length":1,"stats":{"Line":0}},{"line":57,"address":4658192,"length":1,"stats":{"Line":0}},{"line":58,"address":4658242,"length":1,"stats":{"Line":0}},{"line":59,"address":4658249,"length":1,"stats":{"Line":0}},{"line":60,"address":4658585,"length":1,"stats":{"Line":0}},{"line":61,"address":4658672,"length":1,"stats":{"Line":0}},{"line":62,"address":4658885,"length":1,"stats":{"Line":0}},{"line":65,"address":4658612,"length":1,"stats":{"Line":0}},{"line":67,"address":4659578,"length":1,"stats":{"Line":0}},{"line":68,"address":4659582,"length":1,"stats":{"Line":0}},{"line":71,"address":4660092,"length":1,"stats":{"Line":0}},{"line":72,"address":4660500,"length":1,"stats":{"Line":0}},{"line":75,"address":4660627,"length":1,"stats":{"Line":0}},{"line":76,"address":4660639,"length":1,"stats":{"Line":0}},{"line":80,"address":4661171,"length":1,"stats":{"Line":0}},{"line":81,"address":4661461,"length":1,"stats":{"Line":0}},{"line":82,"address":4661751,"length":1,"stats":{"Line":0}},{"line":83,"address":4662041,"length":1,"stats":{"Line":0}},{"line":84,"address":4662438,"length":1,"stats":{"Line":0}},{"line":85,"address":4662704,"length":1,"stats":{"Line":0}},{"line":86,"address":4662970,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":36},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","responses","sent.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Sub-module managing the WS2Pv1 responses sent.\n\nuse crate::ws_connections::responses::WS2Pv1ReqRes;\nuse crate::WS2Pv1Module;\nuse durs_network_documents::NodeFullId;\nuse ws::{CloseCode, Message};\n\npub fn send_response(\n ws2p_module: \u0026mut WS2Pv1Module,\n ws2p_req_from: NodeFullId,\n response: WS2Pv1ReqRes,\n) {\n if let Some(ws_sender) = ws2p_module.websockets.get(\u0026ws2p_req_from) {\n let json_response: serde_json::Value = response.into();\n if ws_sender\n .0\n .send(Message::text(json_response.to_string()))\n .is_err()\n {\n let _ = ws_sender\n .0\n .close_with_reason(CloseCode::Error, \"Fail to send request response !\");\n }\n }\n}\n","traces":[{"line":23,"address":4542928,"length":1,"stats":{"Line":0}},{"line":28,"address":4542940,"length":1,"stats":{"Line":0}},{"line":29,"address":4543049,"length":1,"stats":{"Line":0}},{"line":30,"address":4543111,"length":1,"stats":{"Line":0}},{"line":35,"address":4543273,"length":1,"stats":{"Line":0}},{"line":37,"address":4543278,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":6},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","modules","ws2p-v1-legacy","src","ws_connections","states.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Define ws2p connections states.\n\nuse serde::{Deserialize, Serialize};\n\n#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Serialize)]\npub enum WS2PConnectionState {\n NeverTry = 0,\n TryToOpenWS = 1,\n WSError = 2,\n TryToSendConnectMess = 3,\n Unreachable = 4,\n WaitingConnectMess = 5,\n NoResponse = 6,\n ConnectMessOk = 7,\n OkMessOkWaitingAckMess = 8,\n AckMessOk = 9,\n Denial = 10,\n Close = 11,\n Established = 12,\n}\n\nimpl From\u003cu32\u003e for WS2PConnectionState {\n fn from(integer: u32) -\u003e Self {\n match integer {\n 1 | 2 =\u003e WS2PConnectionState::WSError,\n 3 | 4 =\u003e WS2PConnectionState::Unreachable,\n 5 | 6 =\u003e WS2PConnectionState::NoResponse,\n 7 | 8 | 9 | 10 =\u003e WS2PConnectionState::Denial,\n 11 | 12 =\u003e WS2PConnectionState::Close,\n _ =\u003e WS2PConnectionState::NeverTry,\n }\n }\n}\n\nimpl WS2PConnectionState {\n pub fn from_u32(integer: u32, from_db: bool) -\u003e Self {\n if from_db {\n WS2PConnectionState::from(integer)\n } else {\n match integer {\n 1 =\u003e WS2PConnectionState::TryToOpenWS,\n 2 =\u003e WS2PConnectionState::WSError,\n 3 | 4 =\u003e WS2PConnectionState::Unreachable,\n 5 | 6 =\u003e WS2PConnectionState::NoResponse,\n 7 =\u003e WS2PConnectionState::ConnectMessOk,\n 8 =\u003e WS2PConnectionState::OkMessOkWaitingAckMess,\n 9 =\u003e WS2PConnectionState::AckMessOk,\n 10 =\u003e WS2PConnectionState::Denial,\n 11 =\u003e WS2PConnectionState::Close,\n 12 =\u003e WS2PConnectionState::Established,\n _ =\u003e WS2PConnectionState::NeverTry,\n }\n }\n }\n pub fn to_u32(self) -\u003e u32 {\n match self {\n WS2PConnectionState::NeverTry =\u003e 0,\n _ =\u003e 1,\n }\n }\n}\n","traces":[{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":28},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tests-tools","common-tests-tools","src","collections","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Common test tools for collections\n\nuse std::collections::HashMap;\nuse std::hash::Hash;\n\n/// Returns true if both slices contain the same elements but not necessarily in the same order\npub fn slice_same_elems\u003cT: Clone + Eq + Hash\u003e(a: \u0026[T], b: \u0026[T]) -\u003e bool {\n if a.len() != b.len() {\n return false;\n }\n\n let mut last_pos_elements: HashMap\u003cT, usize\u003e = HashMap::with_capacity(a.len());\n\n for e in a {\n let last_pos: \u0026mut usize = last_pos_elements.entry(e.clone()).or_insert(0);\n let begin = if *last_pos \u003e 0 { 1 } else { 0 };\n if let Some(pos) = find_element_in_slice(\u0026b[(*last_pos + begin)..], e) {\n *last_pos += pos + begin;\n } else {\n return false;\n }\n }\n\n true\n}\n\nfn find_element_in_slice\u003cT: Clone + Eq + Hash\u003e(s: \u0026[T], x: \u0026T) -\u003e Option\u003cusize\u003e {\n for (i, e) in s.iter().enumerate() {\n if *e == *x {\n return Some(i);\n }\n }\n\n None\n}\n\n#[cfg(test)]\nmod test {\n\n use super::*;\n\n #[test]\n fn test_find_element_in_slice() {\n let s1 = [0, 1, 2, 3];\n\n assert_eq!(Some(2), find_element_in_slice(\u0026s1, \u00262));\n\n let s2 = [0, 1, 2, 1, 1, 1, 2];\n\n assert_eq!(Some(3), find_element_in_slice(\u0026s2[3..], \u00262));\n }\n\n #[test]\n fn test_slice_same_elems() {\n assert!(slice_same_elems(\u0026[1, 2, 2, 3, 3, 3], \u0026[3, 2, 1, 2, 3, 3]));\n assert!(slice_same_elems(\u0026[4, 1, 4, 4, 4], \u0026[1, 4, 4, 4, 4]));\n assert!(slice_same_elems(\u0026[1, 4, 4, 4, 4], \u0026[4, 4, 4, 4, 1]));\n }\n}\n","traces":[{"line":22,"address":4240736,"length":1,"stats":{"Line":3}},{"line":23,"address":4240775,"length":1,"stats":{"Line":3}},{"line":24,"address":4240873,"length":1,"stats":{"Line":0}},{"line":27,"address":4240901,"length":1,"stats":{"Line":3}},{"line":29,"address":4240952,"length":1,"stats":{"Line":3}},{"line":30,"address":4241160,"length":1,"stats":{"Line":3}},{"line":31,"address":4241269,"length":1,"stats":{"Line":3}},{"line":32,"address":4241309,"length":1,"stats":{"Line":3}},{"line":33,"address":4241507,"length":1,"stats":{"Line":3}},{"line":35,"address":4241584,"length":1,"stats":{"Line":0}},{"line":39,"address":4241179,"length":1,"stats":{"Line":3}},{"line":42,"address":4241696,"length":1,"stats":{"Line":3}},{"line":43,"address":4241718,"length":1,"stats":{"Line":3}},{"line":44,"address":4241994,"length":1,"stats":{"Line":3}},{"line":45,"address":4242039,"length":1,"stats":{"Line":3}},{"line":49,"address":4242018,"length":1,"stats":{"Line":0}},{"line":58,"address":4211040,"length":1,"stats":{"Line":2}},{"line":59,"address":4211047,"length":1,"stats":{"Line":1}},{"line":61,"address":4211079,"length":1,"stats":{"Line":1}},{"line":63,"address":4211490,"length":1,"stats":{"Line":1}},{"line":65,"address":4211567,"length":1,"stats":{"Line":1}},{"line":69,"address":4212048,"length":1,"stats":{"Line":2}},{"line":70,"address":4212063,"length":1,"stats":{"Line":1}},{"line":71,"address":4212145,"length":1,"stats":{"Line":1}},{"line":72,"address":4212227,"length":1,"stats":{"Line":1}}],"covered":22,"coverable":25},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tests-tools","common-tests-tools","src","logger.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Common test tools for DURS project.\n\nuse log::Level;\nuse simplelog::{Config, LevelFilter, SimpleLogger, TermLogger};\n\n/// Initialize simple stdout logger\npub fn init_logger_stdout() {\n let colors = match std::env::var(\"DURS_TESTS_LOG_COLOR\")\n .unwrap_or_else(|_| String::from(\"no\"))\n .as_str()\n {\n \"yes\" =\u003e true,\n \"no\" =\u003e false,\n v =\u003e panic!(\n \"Unexpected value '{}' for env var DURS_TESTS_LOG_COLOR !\",\n v\n ),\n };\n\n let level_filter = match std::env::var(\"DURS_TESTS_LOG_LEVEL\")\n .unwrap_or_else(|_| String::from(\"debug\"))\n .as_str()\n {\n \"off\" =\u003e LevelFilter::Off,\n \"error\" =\u003e LevelFilter::Error,\n \"warn\" =\u003e LevelFilter::Warn,\n \"info\" =\u003e LevelFilter::Info,\n \"debug\" =\u003e LevelFilter::Debug,\n \"trace\" =\u003e LevelFilter::Trace,\n v =\u003e panic!(\n \"Unexpected value '{}' for env var DURS_TESTS_LOG_LEVEL !\",\n v\n ),\n };\n\n // Config logger\n let logger_config = Config {\n time: Some(Level::Error),\n level: Some(Level::Error),\n target: Some(Level::Debug),\n location: Some(Level::Debug),\n time_format: Some(\"%Y-%m-%d %H:%M:%S%:z\"),\n };\n\n // Active stdout logger\n if colors {\n TermLogger::init(level_filter, logger_config).expect(\"TESTS: fail to init stdout logger !\");\n } else {\n SimpleLogger::init(level_filter, logger_config)\n .expect(\"TESTS: fail to init stdout logger !\");\n }\n}\n","traces":[{"line":22,"address":4219344,"length":1,"stats":{"Line":0}},{"line":23,"address":4219358,"length":1,"stats":{"Line":0}},{"line":24,"address":4243280,"length":1,"stats":{"Line":0}},{"line":27,"address":4219496,"length":1,"stats":{"Line":0}},{"line":28,"address":4219545,"length":1,"stats":{"Line":0}},{"line":29,"address":4219558,"length":1,"stats":{"Line":0}},{"line":35,"address":4219838,"length":1,"stats":{"Line":0}},{"line":36,"address":4243376,"length":1,"stats":{"Line":0}},{"line":39,"address":4219950,"length":1,"stats":{"Line":0}},{"line":40,"address":4220000,"length":1,"stats":{"Line":0}},{"line":41,"address":4220017,"length":1,"stats":{"Line":0}},{"line":42,"address":4220034,"length":1,"stats":{"Line":0}},{"line":43,"address":4220051,"length":1,"stats":{"Line":0}},{"line":44,"address":4220068,"length":1,"stats":{"Line":0}},{"line":45,"address":4220085,"length":1,"stats":{"Line":0}},{"line":52,"address":4220673,"length":1,"stats":{"Line":0}},{"line":53,"address":4220541,"length":1,"stats":{"Line":0}},{"line":54,"address":4220569,"length":1,"stats":{"Line":0}},{"line":55,"address":4220597,"length":1,"stats":{"Line":0}},{"line":56,"address":4220625,"length":1,"stats":{"Line":0}},{"line":57,"address":4220653,"length":1,"stats":{"Line":0}},{"line":61,"address":4220769,"length":1,"stats":{"Line":0}},{"line":62,"address":4220779,"length":1,"stats":{"Line":0}},{"line":64,"address":4220881,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":24},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tests-tools","crypto-tests-tools","src","mocks","mod.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Crypto mocks for projects use dup-crypto\n\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\n\n/// Generate mock hash from one character\npub fn hash(character: char) -\u003e Hash {\n let str_hash: String = (0..64).map(|_| character).collect();\n\n Hash::from_hex(\u0026str_hash).expect(\"Fail to create mock hash !\")\n}\n\n/// Generate mock hash from one byte\npub fn hash_from_byte(byte: u8) -\u003e Hash {\n let mut hash_bin = [0u8; 32];\n for b in \u0026mut hash_bin {\n *b = byte\n }\n\n Hash(hash_bin)\n}\n\n/// Generate mock pubkey from one character\npub fn pubkey(character: char) -\u003e PubKey {\n let str_pub: String = (0..44).map(|_| character).collect();\n\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\u0026str_pub).expect(\"Fail to create mock pubkey !\"),\n )\n}\n","traces":[{"line":22,"address":4211920,"length":1,"stats":{"Line":2}},{"line":23,"address":4211934,"length":1,"stats":{"Line":4}},{"line":25,"address":4212033,"length":1,"stats":{"Line":2}},{"line":29,"address":4212160,"length":1,"stats":{"Line":2}},{"line":30,"address":4212189,"length":1,"stats":{"Line":2}},{"line":31,"address":4212221,"length":1,"stats":{"Line":2}},{"line":32,"address":4212365,"length":1,"stats":{"Line":2}},{"line":35,"address":4212381,"length":1,"stats":{"Line":2}},{"line":39,"address":4212512,"length":1,"stats":{"Line":1}},{"line":40,"address":4212526,"length":1,"stats":{"Line":2}},{"line":43,"address":4212625,"length":1,"stats":{"Line":1}}],"covered":11,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tests-tools","documents-tests-tools","src","mocks","identity.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Mocks for projects use dubp-documents\n\nuse dubp_documents::documents::identity::v10::IdentityDocumentV10Builder;\nuse dubp_documents::documents::identity::*;\nuse dubp_documents::*;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::PubKey;\n\n/// Generate mock identity document\npub fn gen_mock_idty(pubkey: PubKey, created_on: BlockNumber) -\u003e IdentityDocumentV10 {\n let idty_builder = IdentityDocumentV10Builder {\n currency: \"\",\n username: \"\",\n blockstamp: \u0026Blockstamp {\n id: created_on,\n hash: BlockHash(Hash::default()),\n },\n issuer: \u0026pubkey,\n };\n idty_builder.build_with_signature(vec![])\n}\n","traces":[{"line":25,"address":4209040,"length":1,"stats":{"Line":1}},{"line":26,"address":4209222,"length":1,"stats":{"Line":1}},{"line":29,"address":4209166,"length":1,"stats":{"Line":1}},{"line":30,"address":4209054,"length":1,"stats":{"Line":1}},{"line":31,"address":4209058,"length":1,"stats":{"Line":1}},{"line":35,"address":4209277,"length":1,"stats":{"Line":1}}],"covered":6,"coverable":6},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tests-tools","documents-tests-tools","src","mocks","mod.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Mocks for projects use dubp-documents\n\npub mod identity;\n\nuse dubp_documents::documents::block::{v10::TxDocOrTxHash, BlockDocument, BlockDocumentV10};\nuse dubp_documents::documents::certification::{\n CertificationDocument, CertificationDocumentParser,\n};\nuse dubp_documents::documents::transaction::TransactionDocumentParser;\nuse dubp_documents::text_document_traits::TextDocumentFormat;\n\nuse dubp_documents::*;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\n\n/// Generate n mock blockstamps\npub fn generate_blockstamps(n: usize) -\u003e Vec\u003cBlockstamp\u003e {\n (0..n)\n .map(|i| Blockstamp {\n id: BlockNumber(i as u32),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash_from_byte(\n (i % 255) as u8,\n )),\n })\n .collect()\n}\n\n/// Generate n empty timed block document\npub fn gen_empty_timed_blocks_v10(n: usize, time_step: u64) -\u003e Vec\u003cBlockDocument\u003e {\n (0..n)\n .map(|i| {\n BlockDocument::V10(gen_empty_timed_block_v10(\n Blockstamp {\n id: BlockNumber(i as u32),\n hash: BlockHash(dup_crypto_tests_tools::mocks::hash_from_byte(\n (i % 255) as u8,\n )),\n },\n time_step * n as u64,\n if i == 0 {\n Hash::default()\n } else {\n dup_crypto_tests_tools::mocks::hash_from_byte(((i - 1) % 255) as u8)\n },\n ))\n })\n .collect()\n}\n\n/// Generate empty timed block document\n/// (usefull for tests that only need blockstamp and median_time fields)\npub fn gen_empty_timed_block_v10(\n blockstamp: Blockstamp,\n time: u64,\n previous_hash: Hash,\n) -\u003e BlockDocumentV10 {\n BlockDocumentV10 {\n version: 10,\n nonce: 0,\n number: blockstamp.id,\n pow_min: 0,\n time: 0,\n median_time: time,\n members_count: 0,\n monetary_mass: 0,\n unit_base: 0,\n issuers_count: 0,\n issuers_frame: 0,\n issuers_frame_var: 0,\n currency: CurrencyName(\"test_currency\".to_owned()),\n issuers: vec![],\n signatures: vec![],\n hash: Some(blockstamp.hash),\n parameters: None,\n previous_hash: Some(previous_hash),\n previous_issuer: None,\n dividend: None,\n identities: vec![],\n joiners: vec![],\n actives: vec![],\n leavers: vec![],\n revoked: vec![],\n excluded: vec![],\n certifications: vec![],\n transactions: vec![],\n inner_hash: None,\n }\n}\n\nuse dup_currency_params::CurrencyParameters;\n\n/// Generate mock currency parameters\npub fn gen_mock_currency_parameters() -\u003e CurrencyParameters {\n CurrencyParameters {\n protocol_version: 10,\n c: 0.004, // UD target growth rate (see Relative Theorie of Money)\n dt: 1000, // Duration between the creation of two UD (in seconds)\n ud0: 10, // Amount of the initial UD\n sig_period: 10000, // Minimum duration between the writing of 2 certifications from the same issuer (in seconds)\n sig_renew_period: 1000, // Minimum duration between two renewals of the same certification\n sig_stock: 100, // Maximum number of active certifications at the same time (for the same issuer)\n sig_window: 100,\n sig_validity: 100,\n sig_qty: 100,\n idty_window: 100,\n ms_window: 100,\n tx_window: 100,\n x_percent: 0.8,\n ms_validity: 100,\n ms_period: 100,\n step_max: 100,\n median_time_blocks: 100,\n avg_gen_time: 100,\n dt_diff_eval: 100,\n percent_rot: 0.5,\n ud_time0: 100,\n ud_reeval_time0: 100,\n dt_reeval: 100,\n fork_window_size: 100,\n }\n}\n\nuse dup_crypto::bases::b16::str_hex_to_32bytes;\n\n/// Generate mock block which is not a genesis block\npub fn gen_mock_normal_block_v10() -\u003e BlockDocumentV10 {\n let cert1 = CertificationDocumentParser::parse(\"Version: 10\nType: Certification\nCurrency: g1\nIssuer: 6TAzLWuNcSqgNDNpAutrKpPXcGJwy1ZEMeVvZSZNs2e3\nIdtyIssuer: CYPsYTdt87Tx6cCiZs9KD4jqPgYxbcVEqVZpRgJ9jjoV\nIdtyUniqueID: PascaleM\nIdtyTimestamp: 97401-0000003821911909F98519CC773D2D3E5CFE3D5DBB39F4F4FF33B96B4D41800E\nIdtySignature: QncUVXxZ2NfARjdJOn6luILvDuG1NuK9qSoaU4CST2Ij8z7oeVtEgryHl+EXOjSe6XniALsCT0gU8wtadcA/Cw==\nCertTimestamp: 106669-000003682E6FE38C44433DCE92E8B2A26C69B6D7867A2BAED231E788DDEF4251\nUmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==\").expect(\"Fail to parse cert1\");\n let CertificationDocument::V10(cert1) = cert1;\n\n let tx1 = TransactionDocumentParser::parse(\"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B\nLocktime: 0\nIssuers:\n8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3\nInputs:\n1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345\nUnlocks:\n0:SIG(0)\nOutputs:\n1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX)\nComment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci\nT0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==\").expect(\"Fail to parse tx1\");\n\n let tx2 = TransactionDocumentParser::parse(\"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B\nLocktime: 0\nIssuers:\n8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3\nInputs:\n1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106614\nUnlocks:\n0:SIG(0)\nOutputs:\n1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8)\nComment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci\na9PHPuSfw7jW8FRQHXFsGi/bnLjbtDnTYvEVgUC9u0WlR7GVofa+Xb+l5iy6NwuEXiwvueAkf08wPVY8xrNcCg==\").expect(\"Fail to parse tx2\");\n\n BlockDocumentV10 {\n nonce: 10_300_000_018_323,\n version: 10,\n number: BlockNumber(107_984),\n pow_min: 88,\n time: 1_522_685_861,\n median_time: 1_522_683_184,\n members_count: 896,\n monetary_mass: 140_469_765,\n unit_base: 0,\n issuers_count: 42,\n issuers_frame: 211,\n issuers_frame_var: 0,\n currency: CurrencyName(String::from(\"g1\")),\n issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(\"DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ\").unwrap())],\n signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64(\"92id58VmkhgVNee4LDqBGSm8u/ooHzAD67JM6fhAE/CV8LCz7XrMF1DvRl+eRpmlaVkp6I+Iy8gmZ1WUM5C8BA==\").unwrap())],\n hash: None,\n parameters: None,\n previous_hash: Some(Hash::from_hex(\"000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB\").expect(\"fail to parse previous_hash\")),\n previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58(\"D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH\").unwrap())),\n inner_hash: Some(Hash(\n str_hex_to_32bytes(\n \"C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1\",\n ).unwrap() )),\n dividend: None,\n identities: Vec::new(),\n joiners: Vec::new(),\n actives: Vec::new(),\n leavers: Vec::new(),\n revoked: Vec::new(),\n excluded: Vec::new(),\n certifications: vec![TextDocumentFormat::Complete(cert1)],\n transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))],\n }\n}\n\n/// Generate a mock genesis block\npub fn gen_mock_genesis_block_v10() -\u003e BlockDocumentV10 {\n use dup_currency_params::genesis_block_params::v10::BlockV10Parameters;\n\n BlockDocumentV10 {\n nonce: 0,\n version: 10,\n number: BlockNumber(0),\n pow_min: 0,\n time: 0,\n median_time: 0,\n members_count: 0,\n monetary_mass: 0,\n unit_base: 0,\n issuers_count: 0,\n issuers_frame: 0,\n issuers_frame_var: 0,\n currency: CurrencyName(String::from(\"g1\")),\n issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(\"DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ\").unwrap())],\n signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64(\"92id58VmkhgVNee4LDqBGSm8u/ooHzAD67JM6fhAE/CV8LCz7XrMF1DvRl+eRpmlaVkp6I+Iy8gmZ1WUM5C8BA==\").unwrap())],\n hash: None,\n parameters: Some(BlockV10Parameters::default()),\n previous_hash: None,\n previous_issuer: None,\n inner_hash: None,\n dividend: Some(10),\n identities: Vec::new(),\n joiners: Vec::new(),\n actives: Vec::new(),\n leavers: Vec::new(),\n revoked: Vec::new(),\n excluded: Vec::new(),\n certifications: Vec::new(),\n transactions: Vec::new(),\n }\n}\n","traces":[{"line":33,"address":4227024,"length":1,"stats":{"Line":1}},{"line":34,"address":4227036,"length":1,"stats":{"Line":1}},{"line":45,"address":4227136,"length":1,"stats":{"Line":1}},{"line":46,"address":4227153,"length":1,"stats":{"Line":1}},{"line":68,"address":4227264,"length":1,"stats":{"Line":1}},{"line":76,"address":4227289,"length":1,"stats":{"Line":1}},{"line":86,"address":4227300,"length":1,"stats":{"Line":1}},{"line":87,"address":4227425,"length":1,"stats":{"Line":1}},{"line":88,"address":4227499,"length":1,"stats":{"Line":1}},{"line":89,"address":4227562,"length":1,"stats":{"Line":1}},{"line":91,"address":4227643,"length":1,"stats":{"Line":1}},{"line":94,"address":4227740,"length":1,"stats":{"Line":1}},{"line":95,"address":4227811,"length":1,"stats":{"Line":1}},{"line":96,"address":4227871,"length":1,"stats":{"Line":1}},{"line":97,"address":4227931,"length":1,"stats":{"Line":1}},{"line":98,"address":4227991,"length":1,"stats":{"Line":1}},{"line":99,"address":4228051,"length":1,"stats":{"Line":1}},{"line":100,"address":4228115,"length":1,"stats":{"Line":1}},{"line":101,"address":4228175,"length":1,"stats":{"Line":1}},{"line":109,"address":4229280,"length":1,"stats":{"Line":0}},{"line":142,"address":4229536,"length":1,"stats":{"Line":0}},{"line":143,"address":4229559,"length":1,"stats":{"Line":0}},{"line":153,"address":4229660,"length":1,"stats":{"Line":0}},{"line":155,"address":4229698,"length":1,"stats":{"Line":0}},{"line":169,"address":4229806,"length":1,"stats":{"Line":0}},{"line":171,"address":4229814,"length":1,"stats":{"Line":0}},{"line":185,"address":4229880,"length":1,"stats":{"Line":0}},{"line":190,"address":4229888,"length":1,"stats":{"Line":0}},{"line":200,"address":4229899,"length":1,"stats":{"Line":0}},{"line":201,"address":4229968,"length":1,"stats":{"Line":0}},{"line":202,"address":4230223,"length":1,"stats":{"Line":0}},{"line":205,"address":4230528,"length":1,"stats":{"Line":0}},{"line":206,"address":4230652,"length":1,"stats":{"Line":0}},{"line":207,"address":4230846,"length":1,"stats":{"Line":0}},{"line":212,"address":4230938,"length":1,"stats":{"Line":0}},{"line":213,"address":4230945,"length":1,"stats":{"Line":0}},{"line":214,"address":4230964,"length":1,"stats":{"Line":0}},{"line":215,"address":4231001,"length":1,"stats":{"Line":0}},{"line":216,"address":4231035,"length":1,"stats":{"Line":0}},{"line":217,"address":4231069,"length":1,"stats":{"Line":0}},{"line":218,"address":4231113,"length":1,"stats":{"Line":0}},{"line":219,"address":4231360,"length":1,"stats":{"Line":0}},{"line":224,"address":4233088,"length":1,"stats":{"Line":0}},{"line":230,"address":4233105,"length":1,"stats":{"Line":0}},{"line":240,"address":4233113,"length":1,"stats":{"Line":0}},{"line":241,"address":4233194,"length":1,"stats":{"Line":0}},{"line":242,"address":4233409,"length":1,"stats":{"Line":0}},{"line":244,"address":4233684,"length":1,"stats":{"Line":0}},{"line":248,"address":4233787,"length":1,"stats":{"Line":0}},{"line":249,"address":4233824,"length":1,"stats":{"Line":0}},{"line":250,"address":4233831,"length":1,"stats":{"Line":0}},{"line":251,"address":4233850,"length":1,"stats":{"Line":0}},{"line":252,"address":4233887,"length":1,"stats":{"Line":0}},{"line":253,"address":4233921,"length":1,"stats":{"Line":0}},{"line":254,"address":4233955,"length":1,"stats":{"Line":0}},{"line":255,"address":4233989,"length":1,"stats":{"Line":0}},{"line":256,"address":4234023,"length":1,"stats":{"Line":0}}],"covered":19,"coverable":57},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","common-tools","src","fns","bin_file.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Common rust functions for read/write binary files.\n\nuse std::fs::File;\nuse std::io::Read;\nuse std::io::Write;\nuse std::path::Path;\n\n/// Read bin file\npub fn read_bin_file(file_path: \u0026Path) -\u003e Result\u003cVec\u003cu8\u003e, std::io::Error\u003e {\n let mut file = File::open(file_path)?;\n if file.metadata()?.len() == 0 {\n Ok(vec![])\n } else {\n let mut bin_datas = Vec::new();\n file.read_to_end(\u0026mut bin_datas)?;\n\n Ok(bin_datas)\n }\n}\n\n/// Write bin file\npub fn write_bin_file(file_path: \u0026Path, datas: \u0026[u8]) -\u003e Result\u003c(), std::io::Error\u003e {\n let mut file = File::create(file_path)?;\n file.write_all(\u0026datas[..])?;\n\n Ok(())\n}\n","traces":[{"line":24,"address":4214096,"length":1,"stats":{"Line":0}},{"line":25,"address":4214122,"length":1,"stats":{"Line":0}},{"line":26,"address":4214490,"length":1,"stats":{"Line":0}},{"line":27,"address":4214860,"length":1,"stats":{"Line":0}},{"line":29,"address":4214960,"length":1,"stats":{"Line":0}},{"line":30,"address":4214991,"length":1,"stats":{"Line":0}},{"line":32,"address":4215247,"length":1,"stats":{"Line":0}},{"line":37,"address":4215728,"length":1,"stats":{"Line":0}},{"line":38,"address":4215758,"length":1,"stats":{"Line":0}},{"line":39,"address":4216060,"length":1,"stats":{"Line":0}},{"line":41,"address":4216370,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","common-tools","src","fns","str_escape.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Common rust functions to (un)escape strings.\n\n/// Unescape backslash\npub fn unescape_str(source: \u0026str) -\u003e String {\n let mut previous_char = None;\n let mut str_result = String::with_capacity(source.len());\n\n for current_char in source.chars() {\n if previous_char.is_some() \u0026\u0026 previous_char.unwrap() == '\\\\' {\n match current_char {\n '\\\\' =\u003e {} // Do nothing\n _ =\u003e str_result.push(current_char),\n }\n } else {\n str_result.push(current_char);\n }\n previous_char = Some(current_char);\n }\n\n str_result\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n pub fn test_unescape_str() {\n assert_eq!(\"\\\\\".to_owned(), unescape_str(\"\\\\\\\\\"));\n }\n}\n","traces":[{"line":19,"address":4240416,"length":1,"stats":{"Line":3}},{"line":20,"address":4240436,"length":1,"stats":{"Line":3}},{"line":21,"address":4240444,"length":1,"stats":{"Line":3}},{"line":23,"address":4240509,"length":1,"stats":{"Line":3}},{"line":24,"address":4240732,"length":1,"stats":{"Line":3}},{"line":25,"address":4240891,"length":1,"stats":{"Line":0}},{"line":26,"address":4240862,"length":1,"stats":{"Line":1}},{"line":27,"address":4240872,"length":1,"stats":{"Line":0}},{"line":30,"address":4240893,"length":1,"stats":{"Line":3}},{"line":32,"address":4240914,"length":1,"stats":{"Line":3}},{"line":35,"address":4240743,"length":1,"stats":{"Line":3}},{"line":43,"address":4213904,"length":1,"stats":{"Line":2}},{"line":44,"address":4230910,"length":1,"stats":{"Line":1}}],"covered":11,"coverable":13},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","common-tools","src","fns","time.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Common rust functions for manage time.\n\nuse std::time::SystemTime;\nuse std::time::UNIX_EPOCH;\n\n#[inline]\n/// Get current timestamp in seconds\npub fn current_timestamp() -\u003e u64 {\n SystemTime::now()\n .duration_since(UNIX_EPOCH)\n .expect(\"SystemTime::duration_since failed\")\n .as_secs()\n}\n","traces":[{"line":23,"address":null,"length":0,"stats":{"Line":0}},{"line":24,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","common-tools","src","lib.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Common rust tools for DURS project.\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces\n)]\n\npub mod fns;\npub mod macros;\npub mod traits;\n\n/// Percent\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub struct Percent(u8);\n\n/// Percent error\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub enum PercentError {\n /// Integer too large (greater than 100)\n TooLarge(u8),\n}\n\nimpl Percent {\n /// New percent\n pub fn new(percent: u8) -\u003e Result\u003cPercent, PercentError\u003e {\n if percent \u003c= 100 {\n Ok(Percent(percent))\n } else {\n Err(PercentError::TooLarge(percent))\n }\n }\n}\n\nimpl Into\u003cu8\u003e for Percent {\n fn into(self) -\u003e u8 {\n self.0\n }\n}\n","traces":[{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":7},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","bases","b16.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide base16 convertion tools\n\nuse crate::bases::BaseConvertionError;\n\n/// Convert a hexadecimal string in an array of 32 bytes.\n///\n/// The hex string must only contains hex characters\n/// and produce a 32 bytes value.\npub fn str_hex_to_32bytes(text: \u0026str) -\u003e Result\u003c[u8; 32], BaseConvertionError\u003e {\n if text.len() != 64 {\n Err(BaseConvertionError::InvalidLength {\n expected: 64,\n found: text.len(),\n })\n } else {\n let mut bytes = [0u8; 32];\n\n let chars: Vec\u003cchar\u003e = text.chars().collect();\n\n for i in 0..64 {\n if i % 2 != 0 {\n continue;\n }\n\n let byte1 = chars[i].to_digit(16);\n let byte2 = chars[i + 1].to_digit(16);\n\n if let Some(byte1) = byte1 {\n if let Some(byte2) = byte2 {\n let byte = ((byte1 as u8) \u003c\u003c 4) | byte2 as u8;\n bytes[i / 2] = byte;\n } else {\n return Err(BaseConvertionError::InvalidCharacter {\n character: chars[i + 1],\n offset: i + 1,\n });\n }\n } else {\n return Err(BaseConvertionError::InvalidCharacter {\n character: chars[i],\n offset: i,\n });\n }\n }\n\n Ok(bytes)\n }\n}\n","traces":[{"line":24,"address":4497856,"length":1,"stats":{"Line":7}},{"line":25,"address":4497882,"length":1,"stats":{"Line":7}},{"line":26,"address":4497990,"length":1,"stats":{"Line":1}},{"line":28,"address":4497961,"length":1,"stats":{"Line":1}},{"line":31,"address":4498088,"length":1,"stats":{"Line":7}},{"line":33,"address":4498098,"length":1,"stats":{"Line":7}},{"line":35,"address":4498165,"length":1,"stats":{"Line":7}},{"line":36,"address":4498440,"length":1,"stats":{"Line":7}},{"line":37,"address":4498615,"length":1,"stats":{"Line":7}},{"line":40,"address":4498620,"length":1,"stats":{"Line":7}},{"line":41,"address":4498701,"length":1,"stats":{"Line":7}},{"line":43,"address":4498810,"length":1,"stats":{"Line":7}},{"line":44,"address":4498843,"length":1,"stats":{"Line":7}},{"line":45,"address":4498876,"length":1,"stats":{"Line":7}},{"line":46,"address":4498914,"length":1,"stats":{"Line":7}},{"line":48,"address":4499094,"length":1,"stats":{"Line":1}},{"line":49,"address":4498990,"length":1,"stats":{"Line":1}},{"line":50,"address":4499055,"length":1,"stats":{"Line":1}},{"line":54,"address":4499248,"length":1,"stats":{"Line":1}},{"line":55,"address":4499203,"length":1,"stats":{"Line":1}},{"line":56,"address":4499240,"length":1,"stats":{"Line":1}},{"line":61,"address":4498458,"length":1,"stats":{"Line":7}}],"covered":22,"coverable":22},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","bases","b58.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide base58 convertion tools\n\npub use base58::ToBase58;\n\nuse crate::bases::BaseConvertionError;\nuse base58::{FromBase58, FromBase58Error};\n\n/// Create an array of 32 bytes from a Base58 string.\npub fn str_base58_to_32bytes(base58_data: \u0026str) -\u003e Result\u003c[u8; 32], BaseConvertionError\u003e {\n match base58_data.from_base58() {\n Ok(result) =\u003e {\n if result.len() == 32 {\n let mut u8_array = [0; 32];\n\n u8_array[..32].clone_from_slice(\u0026result[..32]);\n\n Ok(u8_array)\n } else {\n Err(BaseConvertionError::InvalidLength {\n expected: 32,\n found: result.len(),\n })\n }\n }\n Err(FromBase58Error::InvalidBase58Character(character, offset)) =\u003e {\n Err(BaseConvertionError::InvalidCharacter { character, offset })\n }\n Err(FromBase58Error::InvalidBase58Length) =\u003e {\n Err(BaseConvertionError::InvalidBaseConverterLength)\n }\n }\n}\n\n/// Create an array of 64bytes from a Base58 string.\npub fn str_base58_to_64bytes(base58_data: \u0026str) -\u003e Result\u003c[u8; 64], BaseConvertionError\u003e {\n match base58_data.from_base58() {\n Ok(result) =\u003e {\n if result.len() == 64 {\n let mut u8_array = [0; 64];\n\n u8_array[..64].clone_from_slice(\u0026result[..64]);\n\n Ok(u8_array)\n } else {\n Err(BaseConvertionError::InvalidLength {\n expected: 64,\n found: result.len(),\n })\n }\n }\n Err(FromBase58Error::InvalidBase58Character(character, offset)) =\u003e {\n Err(BaseConvertionError::InvalidCharacter { character, offset })\n }\n Err(FromBase58Error::InvalidBase58Length) =\u003e {\n Err(BaseConvertionError::InvalidBaseConverterLength)\n }\n }\n}\n","traces":[{"line":24,"address":4501008,"length":1,"stats":{"Line":7}},{"line":25,"address":4501034,"length":1,"stats":{"Line":7}},{"line":26,"address":4501118,"length":1,"stats":{"Line":7}},{"line":27,"address":4501446,"length":1,"stats":{"Line":7}},{"line":28,"address":4501530,"length":1,"stats":{"Line":7}},{"line":30,"address":4501549,"length":1,"stats":{"Line":7}},{"line":32,"address":4501703,"length":1,"stats":{"Line":7}},{"line":34,"address":4501849,"length":1,"stats":{"Line":1}},{"line":36,"address":4501825,"length":1,"stats":{"Line":1}},{"line":40,"address":4501167,"length":1,"stats":{"Line":1}},{"line":41,"address":4501197,"length":1,"stats":{"Line":1}},{"line":44,"address":4501284,"length":1,"stats":{"Line":1}},{"line":50,"address":4502192,"length":1,"stats":{"Line":3}},{"line":51,"address":4502218,"length":1,"stats":{"Line":3}},{"line":52,"address":4502308,"length":1,"stats":{"Line":3}},{"line":53,"address":4502642,"length":1,"stats":{"Line":3}},{"line":54,"address":4502726,"length":1,"stats":{"Line":3}},{"line":56,"address":4502761,"length":1,"stats":{"Line":3}},{"line":58,"address":4502915,"length":1,"stats":{"Line":3}},{"line":60,"address":4503033,"length":1,"stats":{"Line":1}},{"line":62,"address":4503009,"length":1,"stats":{"Line":1}},{"line":66,"address":4502357,"length":1,"stats":{"Line":1}},{"line":67,"address":4502387,"length":1,"stats":{"Line":1}},{"line":70,"address":4502477,"length":1,"stats":{"Line":1}}],"covered":24,"coverable":24},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","bases","b64.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide base64 convertion tools\n\nuse crate::bases::BaseConvertionError;\n\n/// Create an array of 64 bytes from a Base64 string.\npub fn str_base64_to64bytes(base64_data: \u0026str) -\u003e Result\u003c[u8; 64], BaseConvertionError\u003e {\n let result = base64::decode(base64_data)?;\n\n if result.len() == 64 {\n let mut u8_array = [0; 64];\n u8_array[..64].clone_from_slice(\u0026base64::decode(base64_data)?[..64]);\n\n Ok(u8_array)\n } else {\n Err(BaseConvertionError::InvalidLength {\n found: result.len(),\n expected: 64,\n })\n }\n}\n","traces":[{"line":21,"address":4262464,"length":1,"stats":{"Line":6}},{"line":22,"address":4262490,"length":1,"stats":{"Line":6}},{"line":24,"address":4262821,"length":1,"stats":{"Line":6}},{"line":25,"address":4262878,"length":1,"stats":{"Line":6}},{"line":26,"address":4262913,"length":1,"stats":{"Line":6}},{"line":28,"address":4263427,"length":1,"stats":{"Line":6}},{"line":30,"address":4263538,"length":1,"stats":{"Line":1}},{"line":31,"address":4263515,"length":1,"stats":{"Line":1}}],"covered":8,"coverable":8},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","bases","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide base convertion tools\n\n/// Base16 conversion tools\npub mod b16;\n\n/// Base58 conversion tools\npub mod b58;\n\n/// Base64 conversion tools\npub mod b64;\n\n/// Errors enumeration for Base58/64 strings convertion.\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Fail)]\npub enum BaseConvertionError {\n #[fail(\n display = \"Data have invalid key length : expected {}, found {}.\",\n expected, found\n )]\n /// Data have invalid length.\n InvalidLength {\n /// Expected length\n expected: usize,\n /// Actual length\n found: usize,\n },\n #[fail(display = \"Invalid character '{}' at offset {}.\", character, offset)]\n /// Base58/64 have an invalid character.\n InvalidCharacter {\n /// Character\n character: char,\n /// Offset (=position)\n offset: usize,\n },\n #[fail(display = \"Invalid base converter length.\")]\n /// Base58/64 have invalid lendth\n InvalidBaseConverterLength,\n #[fail(display = \"Invalid last symbol '{}' at offset {}.\", symbol, offset)]\n /// Base64 have invalid last symbol (symbol, offset)\n InvalidLastSymbol {\n /// Symbol\n symbol: u8,\n /// Offset (=position)\n offset: usize,\n },\n}\n\nimpl From\u003cbase64::DecodeError\u003e for BaseConvertionError {\n fn from(err: base64::DecodeError) -\u003e Self {\n match err {\n base64::DecodeError::InvalidByte(offset, byte) =\u003e {\n BaseConvertionError::InvalidCharacter {\n character: byte as char,\n offset,\n }\n }\n base64::DecodeError::InvalidLength =\u003e BaseConvertionError::InvalidBaseConverterLength,\n base64::DecodeError::InvalidLastSymbol(offset, symbol) =\u003e {\n BaseConvertionError::InvalidLastSymbol { symbol, offset }\n }\n }\n }\n}\n","traces":[{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":67,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":0}}],"covered":5,"coverable":6},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","hashs","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide wrappers for cryptographic hashs\n\nuse crate::bases::*;\nuse rand::{thread_rng, Rng};\nuse std::fmt::{Debug, Display, Error, Formatter};\n\n#[cfg(not(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\"))))]\nuse crypto::digest::Digest;\n#[cfg(not(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\"))))]\nuse crypto::sha2::Sha256;\n#[cfg(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\")))]\nuse sha2::{Digest, Sha256};\n\n/// A hash wrapper.\n///\n/// A hash is often provided as string composed of 64 hexadecimal character (0 to 9 then A to F).\n#[derive(Copy, Clone, Deserialize, Eq, Ord, PartialEq, PartialOrd, Hash, Serialize)]\npub struct Hash(pub [u8; 32]);\n\nimpl Display for Hash {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.to_hex())\n }\n}\n\nimpl Debug for Hash {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"Hash({})\", self)\n }\n}\n\nimpl Default for Hash {\n fn default() -\u003e Hash {\n Hash([0; 32])\n }\n}\n\nimpl Hash {\n /// Hash size (in bytes).\n pub const SIZE_IN_BYTES: usize = 32;\n\n /// Generate a random Hash\n pub fn random() -\u003e Self {\n let mut rng = thread_rng();\n let mut hash_bytes = Vec::with_capacity(32);\n for _ in 0..32 {\n hash_bytes.push(rng.gen::\u003cu8\u003e());\n }\n let mut hash_bytes_arr = [0; 32];\n hash_bytes_arr.copy_from_slice(\u0026hash_bytes);\n Hash(hash_bytes_arr)\n }\n\n #[cfg(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n /// Compute hash of any binary datas\n pub fn compute(datas: \u0026[u8]) -\u003e Hash {\n let hasher = Sha256::new();\n let mut hash = Hash::default();\n hash.0\n .copy_from_slice(hasher.chain(datas).result().as_slice());\n hash\n }\n #[cfg(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\")))]\n #[inline]\n /// Compute hash of a string\n pub fn compute_str(str_datas: \u0026str) -\u003e Hash {\n Hash::compute(str_datas.as_bytes())\n }\n #[cfg(not(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\"))))]\n #[cfg_attr(tarpaulin, skip)]\n /// Compute hash of any binary datas\n pub fn compute(datas: \u0026[u8]) -\u003e Hash {\n let mut sha = Sha256::new();\n sha.input(datas);\n let mut hash_buffer = [0u8; 32];\n sha.result(\u0026mut hash_buffer);\n Hash(hash_buffer)\n }\n #[cfg(not(all(unix, any(target_arch = \"x86\", target_arch = \"x86_64\"))))]\n #[cfg_attr(tarpaulin, skip)]\n /// Compute hash of a string\n pub fn compute_str(str_datas: \u0026str) -\u003e Hash {\n let mut sha256 = Sha256::new();\n sha256.input_str(\u0026str_datas);\n Hash::from_hex(\u0026sha256.result_str()).expect(\"Sha256 result must be an hexa string !\")\n }\n\n /// Convert Hash into bytes vector\n pub fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e {\n self.0.to_vec()\n }\n\n /// Convert a `Hash` to an hex string.\n pub fn to_hex(\u0026self) -\u003e String {\n let strings: Vec\u003cString\u003e = self.0.iter().map(|b| format!(\"{:02X}\", b)).collect();\n\n strings.join(\"\")\n }\n\n /// Convert a hex string in a `Hash`.\n ///\n /// The hex string must only contains hex characters\n /// and produce a 32 bytes value.\n #[inline]\n pub fn from_hex(text: \u0026str) -\u003e Result\u003cHash, BaseConvertionError\u003e {\n Ok(Hash(b16::str_hex_to_32bytes(text)?))\n }\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n\n #[test]\n fn test_hash_random() {\n let hash1 = Hash::random();\n let hash2 = Hash::random();\n assert_ne!(hash1, hash2);\n }\n\n #[test]\n fn test_hash_debug() {\n assert_eq!(\n \"Hash(0000000000000000000000000000000000000000000000000000000000000000)\".to_owned(),\n format!(\"{:?}\", Hash::default()),\n );\n }\n\n #[test]\n fn test_hash_to_bytes() {\n assert_eq!(\n vec![\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0\n ],\n Hash::default().to_bytes_vector(),\n );\n }\n\n #[test]\n fn test_hash_computation() {\n assert_eq!(\n Hash::from_hex(\"2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824\")\n .expect(\"dev err\"),\n Hash::compute(b\"hello\"),\n );\n\n assert_eq!(\n Hash::from_hex(\"2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824\")\n .expect(\"dev err\"),\n Hash::compute_str(\"hello\"),\n );\n }\n\n #[test]\n fn test_hash_from_hex() {\n assert_eq!(\n Ok(Hash::default()),\n Hash::from_hex(\"0000000000000000000000000000000000000000000000000000000000000000\")\n );\n assert_eq!(\n Err(BaseConvertionError::InvalidLength {\n expected: 64,\n found: 65,\n }),\n Hash::from_hex(\"00000000000000000000000000000000000000000000000000000000000000000\")\n );\n assert_eq!(\n Err(BaseConvertionError::InvalidCharacter {\n character: '_',\n offset: 0,\n }),\n Hash::from_hex(\"_000000000000000000000000000000000000000000000000000000000000000\")\n );\n assert_eq!(\n Err(BaseConvertionError::InvalidCharacter {\n character: '_',\n offset: 1,\n }),\n Hash::from_hex(\"0_00000000000000000000000000000000000000000000000000000000000000\")\n );\n }\n}\n","traces":[{"line":36,"address":null,"length":0,"stats":{"Line":2}},{"line":37,"address":null,"length":0,"stats":{"Line":2}},{"line":42,"address":null,"length":0,"stats":{"Line":1}},{"line":43,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":5}},{"line":49,"address":null,"length":0,"stats":{"Line":5}},{"line":58,"address":null,"length":0,"stats":{"Line":3}},{"line":59,"address":null,"length":0,"stats":{"Line":3}},{"line":60,"address":null,"length":0,"stats":{"Line":3}},{"line":61,"address":null,"length":0,"stats":{"Line":3}},{"line":62,"address":null,"length":0,"stats":{"Line":3}},{"line":64,"address":null,"length":0,"stats":{"Line":3}},{"line":65,"address":null,"length":0,"stats":{"Line":3}},{"line":66,"address":null,"length":0,"stats":{"Line":3}},{"line":71,"address":null,"length":0,"stats":{"Line":4}},{"line":72,"address":null,"length":0,"stats":{"Line":4}},{"line":73,"address":null,"length":0,"stats":{"Line":4}},{"line":74,"address":null,"length":0,"stats":{"Line":4}},{"line":75,"address":null,"length":0,"stats":{"Line":4}},{"line":76,"address":null,"length":0,"stats":{"Line":4}},{"line":81,"address":null,"length":0,"stats":{"Line":3}},{"line":82,"address":null,"length":0,"stats":{"Line":3}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":1}},{"line":109,"address":null,"length":0,"stats":{"Line":5}},{"line":110,"address":null,"length":0,"stats":{"Line":10}},{"line":112,"address":null,"length":0,"stats":{"Line":5}},{"line":120,"address":null,"length":0,"stats":{"Line":10}},{"line":121,"address":null,"length":0,"stats":{"Line":10}},{"line":131,"address":4438576,"length":1,"stats":{"Line":2}},{"line":132,"address":4438583,"length":1,"stats":{"Line":1}},{"line":133,"address":4438593,"length":1,"stats":{"Line":1}},{"line":134,"address":4438603,"length":1,"stats":{"Line":1}},{"line":138,"address":4438976,"length":1,"stats":{"Line":2}},{"line":139,"address":4439104,"length":1,"stats":{"Line":0}},{"line":140,"address":4438990,"length":1,"stats":{"Line":1}},{"line":141,"address":4439033,"length":1,"stats":{"Line":1}},{"line":146,"address":4439712,"length":1,"stats":{"Line":2}},{"line":147,"address":4439909,"length":1,"stats":{"Line":0}},{"line":148,"address":4439719,"length":1,"stats":{"Line":1}},{"line":152,"address":4439902,"length":1,"stats":{"Line":1}},{"line":157,"address":4440416,"length":1,"stats":{"Line":2}},{"line":158,"address":4440512,"length":1,"stats":{"Line":1}},{"line":159,"address":4440430,"length":1,"stats":{"Line":1}},{"line":161,"address":4440491,"length":1,"stats":{"Line":1}},{"line":164,"address":4440972,"length":1,"stats":{"Line":1}},{"line":165,"address":4440887,"length":1,"stats":{"Line":1}},{"line":167,"address":4440951,"length":1,"stats":{"Line":1}},{"line":172,"address":4441376,"length":1,"stats":{"Line":2}},{"line":173,"address":4441496,"length":1,"stats":{"Line":1}},{"line":174,"address":4441383,"length":1,"stats":{"Line":1}},{"line":175,"address":4441475,"length":1,"stats":{"Line":1}},{"line":177,"address":4441938,"length":1,"stats":{"Line":1}},{"line":182,"address":4441910,"length":1,"stats":{"Line":1}},{"line":184,"address":4442348,"length":1,"stats":{"Line":1}},{"line":189,"address":4442320,"length":1,"stats":{"Line":1}},{"line":191,"address":4442758,"length":1,"stats":{"Line":1}},{"line":196,"address":4442730,"length":1,"stats":{"Line":1}}],"covered":56,"coverable":58},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","keys","bin_signable.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Generic code for signing data in binary format\n\nuse super::*;\nuse bincode;\nuse serde::{Deserialize, Serialize};\n\n/// Signatureable in binary format\npub trait BinSignable\u003c'de\u003e: Serialize + Deserialize\u003c'de\u003e {\n /// Return entity issuer pubkey\n fn issuer_pubkey(\u0026self) -\u003e PubKey;\n /// Return signature\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e;\n /// Change signature\n fn set_signature(\u0026mut self, _signature: Sig);\n /// Get binary datas without signature\n #[inline]\n fn get_bin_without_sig(\u0026self) -\u003e Result\u003cVec\u003cu8\u003e, bincode::Error\u003e {\n let mut bin_msg = bincode::serialize(\u0026self)?;\n let sig_size = bincode::serialized_size(\u0026self.signature())?;\n let bin_msg_len = bin_msg.len();\n bin_msg.truncate(bin_msg_len - (sig_size as usize));\n Ok(bin_msg)\n }\n /// Sign entity with a private key\n fn sign(\u0026mut self, priv_key: PrivKey) -\u003e Result\u003cVec\u003cu8\u003e, SignError\u003e {\n if self.signature().is_some() {\n return Err(SignError::AlreadySign);\n }\n match self.issuer_pubkey() {\n PubKey::Ed25519(_) =\u003e match priv_key {\n PrivKey::Ed25519(priv_key) =\u003e {\n let mut bin_msg = bincode::serialize(\u0026self)\n .map_err(|e| SignError::SerdeError(format!(\"{}\", e)))?;\n bin_msg.pop(); // Remove sig field (1 byte: None)\n let sig = Sig::Ed25519(priv_key.sign(\u0026bin_msg));\n self.set_signature(sig);\n bin_msg.extend_from_slice(\n \u0026bincode::serialize(\u0026Some(sig)).expect(\"Fail to binarize sig !\"),\n );\n Ok(bin_msg)\n }\n _ =\u003e Err(SignError::WrongAlgo),\n },\n _ =\u003e Err(SignError::WrongAlgo),\n }\n }\n /// Check signature of entity\n fn verify(\u0026self) -\u003e Result\u003c(), SigError\u003e {\n if let Some(signature) = self.signature() {\n match self.issuer_pubkey() {\n PubKey::Ed25519(pubkey) =\u003e match signature {\n Sig::Ed25519(sig) =\u003e {\n let signed_part: Vec\u003cu8\u003e = self\n .get_bin_without_sig()\n .map_err(|e| SigError::SerdeError(format!(\"{}\", e)))?;\n pubkey.verify(\u0026signed_part, \u0026sig)\n /*\n if pubkey.verify(\u0026signed_part, \u0026sig) {\n Ok(())\n } else {\n Err(SigError::InvalidSig())\n }\n */\n }\n _ =\u003e Err(SigError::NotSameAlgo),\n },\n _ =\u003e Err(SigError::NotSameAlgo),\n }\n } else {\n Err(SigError::NotSig)\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n\n #[derive(Deserialize, Serialize)]\n struct BinSignableTestImpl {\n datas: Vec\u003cu8\u003e,\n issuer: PubKey,\n sig: Option\u003cSig\u003e,\n }\n\n impl BinSignable\u003c'_\u003e for BinSignableTestImpl {\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n self.issuer\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n self.sig\n }\n fn set_signature(\u0026mut self, new_signature: Sig) {\n self.sig = Some(new_signature);\n }\n }\n\n #[test]\n fn name() {\n let key_pair = ed25519::KeyPairFromSeedGenerator::generate(\u0026[\n 0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\n 10, 11, 12, 13, 14, 15,\n ]);\n\n let mut bin_signable_datas = BinSignableTestImpl {\n datas: vec![0, 1, 2, 3],\n issuer: PubKey::Ed25519(key_pair.pubkey),\n sig: None,\n };\n\n assert_eq!(Err(SigError::NotSig), bin_signable_datas.verify());\n\n let _bin_msg = bin_signable_datas\n .sign(PrivKey::Ed25519(key_pair.privkey))\n .expect(\"Fail to sign datas !\");\n\n assert_eq!(\n Err(SignError::AlreadySign),\n bin_signable_datas.sign(PrivKey::Ed25519(key_pair.privkey))\n );\n\n assert_eq!(Ok(()), bin_signable_datas.verify())\n }\n}\n","traces":[{"line":32,"address":null,"length":0,"stats":{"Line":3}},{"line":33,"address":null,"length":0,"stats":{"Line":3}},{"line":34,"address":null,"length":0,"stats":{"Line":3}},{"line":35,"address":null,"length":0,"stats":{"Line":3}},{"line":36,"address":null,"length":0,"stats":{"Line":3}},{"line":37,"address":null,"length":0,"stats":{"Line":3}},{"line":40,"address":null,"length":0,"stats":{"Line":4}},{"line":41,"address":null,"length":0,"stats":{"Line":4}},{"line":42,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":4}},{"line":45,"address":null,"length":0,"stats":{"Line":4}},{"line":46,"address":null,"length":0,"stats":{"Line":4}},{"line":47,"address":null,"length":0,"stats":{"Line":4}},{"line":48,"address":null,"length":0,"stats":{"Line":4}},{"line":49,"address":4299265,"length":1,"stats":{"Line":4}},{"line":50,"address":null,"length":0,"stats":{"Line":4}},{"line":51,"address":null,"length":0,"stats":{"Line":4}},{"line":52,"address":null,"length":0,"stats":{"Line":3}},{"line":53,"address":null,"length":0,"stats":{"Line":3}},{"line":55,"address":null,"length":0,"stats":{"Line":3}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":3}},{"line":64,"address":null,"length":0,"stats":{"Line":3}},{"line":65,"address":null,"length":0,"stats":{"Line":3}},{"line":66,"address":null,"length":0,"stats":{"Line":3}},{"line":67,"address":null,"length":0,"stats":{"Line":3}},{"line":68,"address":null,"length":0,"stats":{"Line":3}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":3}},{"line":71,"address":null,"length":0,"stats":{"Line":3}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":1}},{"line":109,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":1}},{"line":115,"address":4316448,"length":1,"stats":{"Line":2}},{"line":116,"address":4626391,"length":1,"stats":{"Line":1}},{"line":121,"address":4626560,"length":1,"stats":{"Line":1}},{"line":122,"address":4626428,"length":1,"stats":{"Line":1}},{"line":123,"address":4626480,"length":1,"stats":{"Line":1}},{"line":124,"address":4626552,"length":1,"stats":{"Line":1}},{"line":127,"address":4626732,"length":1,"stats":{"Line":1}},{"line":129,"address":4627393,"length":1,"stats":{"Line":1}},{"line":130,"address":4627233,"length":1,"stats":{"Line":1}},{"line":133,"address":4627620,"length":1,"stats":{"Line":1}},{"line":135,"address":4627435,"length":1,"stats":{"Line":1}},{"line":138,"address":4628076,"length":1,"stats":{"Line":1}}],"covered":47,"coverable":53},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","keys","ed25519.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide wrappers around ed25519 keys and signatures\n//!\n//! Key pairs can be generated with [`KeyPairGenerator`].\n//!\n//! [`KeyPairGenerator`]: struct.KeyPairGenerator.html\n\nuse super::{PrivateKey as PrivateKeyMethods, PublicKey as PublicKeyMethods};\nuse crate::bases::*;\nuse base58::ToBase58;\nuse base64;\nuse crypto;\nuse serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};\nuse serde::ser::{Serialize, SerializeTuple, Serializer};\nuse std::fmt;\nuse std::fmt::{Debug, Display, Formatter};\nuse std::hash::{Hash, Hasher};\nuse std::marker::PhantomData;\n\n/// Size of a public key in bytes\npub static PUBKEY_SIZE_IN_BYTES: \u0026'static usize = \u002632;\n/// Size of a signature in bytes\npub static SIG_SIZE_IN_BYTES: \u0026'static usize = \u002664;\n\n/// Store a ed25519 signature.\n#[derive(Clone, Copy)]\npub struct Signature(pub [u8; 64]);\n\nimpl Hash for Signature {\n fn hash\u003cH: Hasher\u003e(\u0026self, state: \u0026mut H) {\n self.0.hash(state);\n }\n}\n\nimpl Serialize for Signature {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n let mut seq = serializer.serialize_tuple(self.0.len())?;\n for elem in \u0026self.0[..] {\n seq.serialize_element(elem)?;\n }\n seq.end()\n }\n}\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for Signature {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSignature, D::Error\u003e\n where\n D: Deserializer\u003c'de\u003e,\n {\n struct ArrayVisitor {\n element: PhantomData\u003cu8\u003e,\n }\n\n impl\u003c'de\u003e Visitor\u003c'de\u003e for ArrayVisitor {\n type Value = Signature;\n\n #[cfg_attr(tarpaulin, skip)]\n fn expecting(\u0026self, formatter: \u0026mut Formatter) -\u003e fmt::Result {\n formatter.write_str(concat!(\"an array of length \", 64))\n }\n\n fn visit_seq\u003cA\u003e(self, mut seq: A) -\u003e Result\u003cSignature, A::Error\u003e\n where\n A: SeqAccess\u003c'de\u003e,\n {\n let mut arr = [0u8; 64];\n for (i, byte) in arr.iter_mut().take(64).enumerate() {\n *byte = seq\n .next_element()?\n .ok_or_else(|| Error::invalid_length(i, \u0026self))?;\n }\n Ok(Signature(arr))\n }\n }\n\n let visitor: ArrayVisitor = ArrayVisitor {\n element: PhantomData,\n };\n deserializer.deserialize_tuple(64, visitor)\n }\n}\n\nimpl super::Signature for Signature {\n #[inline]\n fn from_base64(base64_data: \u0026str) -\u003e Result\u003cSignature, BaseConvertionError\u003e {\n Ok(Signature(b64::str_base64_to64bytes(base64_data)?))\n }\n\n fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e {\n self.0.to_vec()\n }\n\n fn to_base64(\u0026self) -\u003e String {\n base64::encode(\u0026self.0[..]) // need to take a slice for required trait `AsRef\u003c[u8]\u003e`\n }\n}\n\nimpl Display for Signature {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), fmt::Error\u003e {\n use super::Signature;\n\n write!(f, \"{}\", self.to_base64())\n }\n}\n\nimpl Debug for Signature {\n // Signature { 1eubHHb... }\n fn fmt(\u0026self, f: \u0026mut ::std::fmt::Formatter) -\u003e ::std::fmt::Result {\n write!(f, \"Signature {{ {} }}\", self)\n }\n}\n\nimpl PartialEq\u003cSignature\u003e for Signature {\n fn eq(\u0026self, other: \u0026Signature) -\u003e bool {\n // No PartialEq for [u8;64], need to use 2 [u8;32]\n self.0[0..32] == other.0[0..32] \u0026\u0026 self.0[32..64] == other.0[32..64]\n }\n}\n\nimpl Eq for Signature {}\n\n/// Store a Ed25519 public key.\n///\n/// Can be generated with [`KeyPairGenerator`].\n///\n/// [`KeyPairGenerator`]: struct.KeyPairGenerator.html\n#[derive(Copy, Clone, Deserialize, PartialEq, Eq, Hash, Serialize)]\npub struct PublicKey(pub [u8; 32]);\n\nimpl ToBase58 for PublicKey {\n fn to_base58(\u0026self) -\u003e String {\n self.0.to_base58()\n }\n}\n\nimpl Display for PublicKey {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), fmt::Error\u003e {\n write!(f, \"{}\", self.to_base58())\n }\n}\n\nimpl Debug for PublicKey {\n // PublicKey { DNann1L... }\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), fmt::Error\u003e {\n write!(f, \"PublicKey {{ {} }}\", self)\n }\n}\n\nuse crate::keys::SigError;\n\nimpl super::PublicKey for PublicKey {\n type Signature = Signature;\n\n #[inline]\n fn from_base58(base58_data: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e {\n Ok(PublicKey(b58::str_base58_to_32bytes(base58_data)?))\n }\n\n fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e {\n self.0.to_vec()\n }\n\n fn verify(\u0026self, message: \u0026[u8], signature: \u0026Self::Signature) -\u003e Result\u003c(), SigError\u003e {\n if crypto::ed25519::verify(message, \u0026self.0, \u0026signature.0) {\n Ok(())\n } else {\n Err(SigError::InvalidSig)\n }\n }\n}\n\n/// Store a Ed25519 private key.\n///\n/// Can be generated with [`KeyPairGenerator`].\n///\n/// [`KeyPairGenerator`]: struct.KeyPairGenerator.html\n#[derive(Copy, Clone)]\npub struct PrivateKey(pub [u8; 64]);\n\nimpl ToBase58 for PrivateKey {\n fn to_base58(\u0026self) -\u003e String {\n self.0.to_base58()\n }\n}\n\nimpl Display for PrivateKey {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), fmt::Error\u003e {\n write!(f, \"{}\", self.to_base58())\n }\n}\n\nimpl Debug for PrivateKey {\n // PrivateKey { 468Q1XtT... }\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), fmt::Error\u003e {\n write!(f, \"PrivateKey {{ {} }}\", self)\n }\n}\n\nimpl PartialEq\u003cPrivateKey\u003e for PrivateKey {\n fn eq(\u0026self, other: \u0026PrivateKey) -\u003e bool {\n // No PartialEq for [u8;64], need to use 2 [u8;32]\n self.0[0..32] == other.0[0..32] \u0026\u0026 self.0[32..64] == other.0[32..64]\n }\n}\n\nimpl Eq for PrivateKey {}\n\nimpl super::PrivateKey for PrivateKey {\n type Signature = Signature;\n\n #[inline]\n fn from_base58(base58_data: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e {\n Ok(PrivateKey(b58::str_base58_to_64bytes(base58_data)?))\n }\n\n /// Sign a message with this private key.\n fn sign(\u0026self, message: \u0026[u8]) -\u003e Self::Signature {\n Signature(crypto::ed25519::signature(message, \u0026self.0))\n }\n}\n\n/// Store a ed25519 cryptographic key pair (`PublicKey` + `PrivateKey`)\n#[derive(Debug, Copy, Clone, Eq)]\npub struct KeyPair {\n /// Store a Ed25519 public key.\n pub pubkey: PublicKey,\n /// Store a Ed25519 private key.\n pub privkey: PrivateKey,\n}\n\nimpl Display for KeyPair {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), fmt::Error\u003e {\n write!(f, \"({}, hidden)\", self.pubkey.to_base58())\n }\n}\n\nimpl PartialEq\u003cKeyPair\u003e for KeyPair {\n fn eq(\u0026self, other: \u0026KeyPair) -\u003e bool {\n self.pubkey.eq(\u0026other.pubkey) \u0026\u0026 self.privkey.eq(\u0026other.privkey)\n }\n}\n\nimpl super::KeyPair for KeyPair {\n type Signature = Signature;\n type PublicKey = PublicKey;\n type PrivateKey = PrivateKey;\n\n fn public_key(\u0026self) -\u003e PublicKey {\n self.pubkey\n }\n\n fn private_key(\u0026self) -\u003e PrivateKey {\n self.privkey\n }\n\n fn sign(\u0026self, message: \u0026[u8]) -\u003e Signature {\n self.private_key().sign(message)\n }\n\n fn verify(\u0026self, message: \u0026[u8], signature: \u0026Self::Signature) -\u003e Result\u003c(), SigError\u003e {\n self.public_key().verify(message, signature)\n }\n}\n\n/// Keypair generator with seed\n#[derive(Debug, Copy, Clone)]\npub struct KeyPairFromSeedGenerator {}\n\nimpl KeyPairFromSeedGenerator {\n /// Create a keypair based on a given seed.\n ///\n /// The [`PublicKey`](struct.PublicKey.html) will be able to verify messaged signed with\n /// the [`PrivateKey`](struct.PrivateKey.html).\n pub fn generate(seed: \u0026[u8; 32]) -\u003e KeyPair {\n let (private, public) = crypto::ed25519::keypair(seed);\n KeyPair {\n pubkey: PublicKey(public),\n privkey: PrivateKey(private),\n }\n }\n}\n\n/// Keypair generator with given parameters for `scrypt` keypair function.\n#[derive(Debug, Copy, Clone)]\npub struct KeyPairFromSaltedPasswordGenerator {\n /// The log2 of the Scrypt parameter `N`.\n log_n: u8,\n /// The Scrypt parameter `r`\n r: u32,\n /// The Scrypt parameter `p`\n p: u32,\n}\n\nimpl KeyPairFromSaltedPasswordGenerator {\n /// Create a `KeyPairGenerator` with default arguments `(log_n: 12, r: 16, p: 1)`\n pub fn with_default_parameters() -\u003e KeyPairFromSaltedPasswordGenerator {\n KeyPairFromSaltedPasswordGenerator {\n log_n: 12,\n r: 16,\n p: 1,\n }\n }\n\n /// Create a `KeyPairFromSaltedPasswordGenerator` with given arguments.\n ///\n /// # Arguments\n ///\n /// - log_n - The log2 of the Scrypt parameter N\n /// - r - The Scrypt parameter r\n /// - p - The Scrypt parameter p\n pub fn with_parameters(log_n: u8, r: u32, p: u32) -\u003e KeyPairFromSaltedPasswordGenerator {\n KeyPairFromSaltedPasswordGenerator { log_n, r, p }\n }\n\n /// Create a seed based on a given password and salt.\n pub fn generate_seed(\u0026self, password: \u0026[u8], salt: \u0026[u8]) -\u003e [u8; 32] {\n let mut seed = [0u8; 32];\n\n crypto::scrypt::scrypt(\n salt,\n password,\n \u0026crypto::scrypt::ScryptParams::new(self.log_n, self.r, self.p),\n \u0026mut seed,\n );\n\n seed\n }\n\n /// Create a keypair based on a given password and salt.\n ///\n /// The [`PublicKey`](struct.PublicKey.html) will be able to verify messaged signed with\n /// the [`PrivateKey`](struct.PrivateKey.html).\n pub fn generate(\u0026self, password: \u0026[u8], salt: \u0026[u8]) -\u003e KeyPair {\n let seed: [u8; 32] = self.generate_seed(password, salt);\n KeyPairFromSeedGenerator::generate(\u0026seed)\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::keys::{KeyPair, Sig, Signature};\n use base58::FromBase58;\n use bincode;\n use std::collections::hash_map::DefaultHasher;\n\n #[test]\n fn base58_private_key() {\n let private58 =\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9r\\\n qnXuW3iAfZACm7\";\n let private_key = super::PrivateKey::from_base58(private58).unwrap();\n\n // Test base58 encoding/decoding (loop for every bytes)\n assert_eq!(private_key.to_base58(), private58);\n let private_raw = private58.from_base58().unwrap();\n for (key, raw) in private_key.0.iter().zip(private_raw.iter()) {\n assert_eq!(key, raw);\n }\n\n // Test privkey display and debug\n assert_eq!(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7\".to_owned(),\n format!(\"{}\", private_key)\n );\n assert_eq!(\n \"PrivateKey { 468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7 }\".to_owned(),\n format!(\"{:?}\", private_key)\n );\n\n // Test privkey equality\n let same_private_key = private_key.clone();\n let other_private_key = PrivateKey([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0,\n ]);\n assert!(private_key.eq(\u0026same_private_key));\n assert!(!private_key.eq(\u0026other_private_key));\n\n // Test privkey parsing\n assert_eq!(\n super::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iA\\\n fZACm7djh\",\n ).unwrap_err(),\n BaseConvertionError::InvalidLength { found: 67, expected: 64 }\n );\n assert_eq!(\n super::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9\",\n )\n .unwrap_err(),\n BaseConvertionError::InvalidLength {\n found: 53,\n expected: 64\n }\n );\n assert_eq!(\n super::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9\u003c\u003c\",\n )\n .unwrap_err(),\n BaseConvertionError::InvalidCharacter {\n character: '\u003c',\n offset: 73\n }\n );\n assert_eq!(\n super::PrivateKey::from_base58(\n \"\\\n 468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9\\\n 468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9\\\n 468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9\\\n \",\n )\n .unwrap_err(),\n BaseConvertionError::InvalidBaseConverterLength,\n );\n }\n\n #[test]\n fn base58_public_key() {\n let public58 = \"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\";\n let public_key = super::PublicKey::from_base58(public58).unwrap();\n\n // Test base58 encoding/decoding (loop for every bytes)\n assert_eq!(public_key.to_base58(), public58);\n let public_raw = public58.from_base58().unwrap();\n assert_eq!(public_raw, public_key.to_bytes_vector());\n for (key, raw) in public_key.0.iter().zip(public_raw.iter()) {\n assert_eq!(key, raw);\n }\n\n // Test pubkey debug\n assert_eq!(\n \"PublicKey { DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV }\".to_owned(),\n format!(\"{:?}\", public_key)\n );\n\n assert_eq!(\n super::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLVdjq\")\n .unwrap_err(),\n BaseConvertionError::InvalidLength {\n found: 35,\n expected: 32\n }\n );\n assert_eq!(\n super::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd\")\n .unwrap_err(),\n BaseConvertionError::InvalidLength {\n found: 31,\n expected: 32\n }\n );\n assert_eq!(\n super::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQd\u003c\u003c\")\n .unwrap_err(),\n BaseConvertionError::InvalidCharacter {\n character: '\u003c',\n offset: 42\n }\n );\n assert_eq!(\n super::PublicKey::from_base58(\n \"\\\n DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\\\n DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\\\n DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\\\n DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\\\n DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\\\n \"\n )\n .unwrap_err(),\n BaseConvertionError::InvalidBaseConverterLength\n );\n }\n\n #[test]\n fn base64_signature() {\n let signature64 = \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FG\\\n MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\";\n let signature = super::Signature::from_base64(signature64).unwrap();\n\n // Test signature base64 encoding/decoding (loop for every bytes)\n assert_eq!(signature.to_base64(), signature64);\n let signature_raw = base64::decode(signature64).unwrap();\n for (sig, raw) in signature.0.iter().zip(signature_raw.iter()) {\n assert_eq!(sig, raw);\n }\n\n // Test signature display and debug\n assert_eq!(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\".to_owned(),\n format!(\"{}\", signature)\n );\n assert_eq!(\n \"Signature { 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg== }\".to_owned(),\n format!(\"{:?}\", signature)\n );\n\n // Test signature hash\n let mut hasher = DefaultHasher::new();\n signature.hash(\u0026mut hasher);\n let hash1 = hasher.finish();\n let mut hasher = DefaultHasher::new();\n let signature_copy = signature.clone();\n signature_copy.hash(\u0026mut hasher);\n let hash2 = hasher.finish();\n assert_eq!(hash1, hash2);\n\n // Test signature serialization/deserialization\n let mut bin_sig = bincode::serialize(\u0026signature).expect(\"Fail to serialize signature !\");\n assert_eq!(*SIG_SIZE_IN_BYTES, bin_sig.len());\n assert_eq!(signature.to_bytes_vector(), bin_sig);\n assert_eq!(\n signature,\n bincode::deserialize(\u0026bin_sig).expect(\"Fail to deserialize signature !\"),\n );\n bin_sig.push(0); // add on byte to simulate invalid length\n bincode::deserialize::\u003cSig\u003e(\u0026bin_sig)\n .expect_err(\"Deserialization must be fail because length is invalid !\");\n\n // Test signature parsing\n assert_eq!(\n super::Signature::from_base64(\"YmhlaW9iaHNlcGlvaGVvaXNlcGl2ZXBvdm5pc2U=\").unwrap_err(),\n BaseConvertionError::InvalidLength {\n found: 29,\n expected: 64\n }\n );\n assert_eq!(\n super::Signature::from_base64(\n \"YmhlaW9iaHNlcGlvaGVvaXNlcGl2ZXBvdm5pc2V2c2JlaW9idmVpb3Zqc\\\n 2V2Z3BpaHNlamVwZ25qZXNqb2dwZWpnaW9zZXNkdnNic3JicmJyZGJyZGI=\",\n )\n .unwrap_err(),\n BaseConvertionError::InvalidLength {\n found: 86,\n expected: 64\n }\n );\n assert_eq!(\n super::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\\\n mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAgdha\u003c\u003c\",\n )\n .unwrap_err(),\n BaseConvertionError::InvalidCharacter {\n character: '\u003c',\n offset: 89\n }\n );\n assert_eq!(\n super::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FG\\\n MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg===\",\n )\n .unwrap_err(),\n BaseConvertionError::InvalidBaseConverterLength,\n );\n }\n\n #[test]\n fn message_sign_verify() {\n let pubkey =\n super::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\").unwrap();\n\n let prikey = super::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt\\\n 5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7\",\n )\n .unwrap();\n\n let expected_signature = super::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FG\\\n MMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\",\n )\n .unwrap();\n\n let message = \"Version: 10\nType: Identity\nCurrency: duniter_unit_test_currency\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nUniqueID: tic\nTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n\";\n\n let sig = prikey.sign(message.as_bytes());\n let wrong_sig = Signature([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0,\n ]);\n\n assert_eq!(sig, expected_signature);\n assert_eq!(Ok(()), pubkey.verify(message.as_bytes(), \u0026sig));\n assert_eq!(\n Err(SigError::InvalidSig),\n pubkey.verify(message.as_bytes(), \u0026wrong_sig)\n );\n }\n\n #[test]\n fn keypair_generate() {\n let key_pair1 = KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV\".as_bytes(),\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_\".as_bytes(),\n );\n\n assert_eq!(\n key_pair1.pubkey.to_string(),\n \"7iMV3b6j2hSj5WtrfchfvxivS9swN3opDgxudeHq64fb\"\n );\n\n let key_pair2 = KeyPairFromSaltedPasswordGenerator::with_parameters(12u8, 16, 1)\n .generate(b\"toto\", b\"toto\");\n\n // Test signature display and debug\n assert_eq!(\n \"(EA7Dsw39ShZg4SpURsrgMaMqrweJPUFPYHwZA8e92e3D, hidden)\".to_owned(),\n format!(\"{}\", key_pair2)\n );\n\n // Test key_pair equality\n let same_key_pair = key_pair2.clone();\n let other_key_pair = KeyPairFromSeedGenerator::generate(\u0026[\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0,\n ]);\n assert!(key_pair2.eq(\u0026same_key_pair));\n assert!(!key_pair2.eq(\u0026other_key_pair));\n }\n\n #[test]\n fn keypair_generate_sign_and_verify() {\n let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters()\n .generate(\"password\".as_bytes(), \"salt\".as_bytes());\n\n let message = \"Version: 10\nType: Identity\nCurrency: duniter_unit_test_currency\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nUniqueID: tic\nTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n\";\n\n let sig = keypair.sign(message.as_bytes());\n assert!(keypair.verify(message.as_bytes(), \u0026sig).is_ok());\n }\n}\n","traces":[{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":50,"address":4491984,"length":1,"stats":{"Line":10}},{"line":54,"address":null,"length":0,"stats":{"Line":10}},{"line":55,"address":null,"length":0,"stats":{"Line":10}},{"line":56,"address":null,"length":0,"stats":{"Line":10}},{"line":58,"address":null,"length":0,"stats":{"Line":10}},{"line":63,"address":4512464,"length":1,"stats":{"Line":3}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":3}},{"line":83,"address":null,"length":0,"stats":{"Line":3}},{"line":84,"address":null,"length":0,"stats":{"Line":3}},{"line":85,"address":null,"length":0,"stats":{"Line":3}},{"line":86,"address":null,"length":0,"stats":{"Line":3}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":3}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":3}},{"line":102,"address":null,"length":0,"stats":{"Line":7}},{"line":103,"address":null,"length":0,"stats":{"Line":7}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":5}},{"line":111,"address":4596540,"length":1,"stats":{"Line":5}},{"line":116,"address":null,"length":0,"stats":{"Line":2}},{"line":119,"address":null,"length":0,"stats":{"Line":2}},{"line":125,"address":null,"length":0,"stats":{"Line":2}},{"line":126,"address":null,"length":0,"stats":{"Line":2}},{"line":131,"address":null,"length":0,"stats":{"Line":4}},{"line":133,"address":null,"length":0,"stats":{"Line":4}},{"line":148,"address":null,"length":0,"stats":{"Line":6}},{"line":149,"address":null,"length":0,"stats":{"Line":6}},{"line":154,"address":null,"length":0,"stats":{"Line":2}},{"line":155,"address":null,"length":0,"stats":{"Line":2}},{"line":161,"address":null,"length":0,"stats":{"Line":2}},{"line":162,"address":null,"length":0,"stats":{"Line":2}},{"line":172,"address":null,"length":0,"stats":{"Line":10}},{"line":173,"address":null,"length":0,"stats":{"Line":10}},{"line":176,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":7}},{"line":181,"address":null,"length":0,"stats":{"Line":7}},{"line":182,"address":null,"length":0,"stats":{"Line":7}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":2}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":199,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":211,"address":null,"length":0,"stats":{"Line":1}},{"line":212,"address":null,"length":0,"stats":{"Line":1}},{"line":217,"address":null,"length":0,"stats":{"Line":2}},{"line":219,"address":null,"length":0,"stats":{"Line":2}},{"line":229,"address":null,"length":0,"stats":{"Line":3}},{"line":230,"address":null,"length":0,"stats":{"Line":3}},{"line":234,"address":null,"length":0,"stats":{"Line":6}},{"line":235,"address":null,"length":0,"stats":{"Line":6}},{"line":249,"address":null,"length":0,"stats":{"Line":1}},{"line":250,"address":null,"length":0,"stats":{"Line":1}},{"line":255,"address":null,"length":0,"stats":{"Line":1}},{"line":256,"address":null,"length":0,"stats":{"Line":1}},{"line":265,"address":null,"length":0,"stats":{"Line":5}},{"line":266,"address":null,"length":0,"stats":{"Line":5}},{"line":269,"address":null,"length":0,"stats":{"Line":6}},{"line":270,"address":null,"length":0,"stats":{"Line":6}},{"line":273,"address":null,"length":0,"stats":{"Line":1}},{"line":274,"address":null,"length":0,"stats":{"Line":1}},{"line":277,"address":null,"length":0,"stats":{"Line":1}},{"line":278,"address":null,"length":0,"stats":{"Line":1}},{"line":291,"address":null,"length":0,"stats":{"Line":5}},{"line":292,"address":null,"length":0,"stats":{"Line":5}},{"line":294,"address":null,"length":0,"stats":{"Line":5}},{"line":295,"address":null,"length":0,"stats":{"Line":5}},{"line":313,"address":null,"length":0,"stats":{"Line":5}},{"line":328,"address":null,"length":0,"stats":{"Line":1}},{"line":333,"address":null,"length":0,"stats":{"Line":5}},{"line":334,"address":null,"length":0,"stats":{"Line":5}},{"line":337,"address":null,"length":0,"stats":{"Line":5}},{"line":338,"address":null,"length":0,"stats":{"Line":5}},{"line":339,"address":null,"length":0,"stats":{"Line":5}},{"line":340,"address":null,"length":0,"stats":{"Line":5}},{"line":343,"address":null,"length":0,"stats":{"Line":5}},{"line":350,"address":null,"length":0,"stats":{"Line":5}},{"line":351,"address":null,"length":0,"stats":{"Line":5}},{"line":352,"address":null,"length":0,"stats":{"Line":5}},{"line":365,"address":4319840,"length":1,"stats":{"Line":2}},{"line":367,"address":4319854,"length":1,"stats":{"Line":1}},{"line":369,"address":4319874,"length":1,"stats":{"Line":1}},{"line":372,"address":4319941,"length":1,"stats":{"Line":1}},{"line":373,"address":4320447,"length":1,"stats":{"Line":1}},{"line":374,"address":4320498,"length":1,"stats":{"Line":1}},{"line":375,"address":4321002,"length":1,"stats":{"Line":1}},{"line":379,"address":4321632,"length":1,"stats":{"Line":0}},{"line":380,"address":4321088,"length":1,"stats":{"Line":1}},{"line":381,"address":4321487,"length":1,"stats":{"Line":1}},{"line":383,"address":4322355,"length":1,"stats":{"Line":0}},{"line":384,"address":4322175,"length":1,"stats":{"Line":1}},{"line":385,"address":4322210,"length":1,"stats":{"Line":1}},{"line":389,"address":4322914,"length":1,"stats":{"Line":1}},{"line":390,"address":4322921,"length":1,"stats":{"Line":1}},{"line":395,"address":4323513,"length":1,"stats":{"Line":1}},{"line":396,"address":4323591,"length":1,"stats":{"Line":1}},{"line":399,"address":4323713,"length":1,"stats":{"Line":1}},{"line":400,"address":4323655,"length":1,"stats":{"Line":1}},{"line":406,"address":4324212,"length":1,"stats":{"Line":1}},{"line":407,"address":4324154,"length":1,"stats":{"Line":1}},{"line":416,"address":4324705,"length":1,"stats":{"Line":1}},{"line":417,"address":4324647,"length":1,"stats":{"Line":1}},{"line":426,"address":4325162,"length":1,"stats":{"Line":1}},{"line":427,"address":4325104,"length":1,"stats":{"Line":1}},{"line":440,"address":4325728,"length":1,"stats":{"Line":2}},{"line":441,"address":4325742,"length":1,"stats":{"Line":1}},{"line":442,"address":4325762,"length":1,"stats":{"Line":1}},{"line":445,"address":4325829,"length":1,"stats":{"Line":1}},{"line":446,"address":4326335,"length":1,"stats":{"Line":1}},{"line":447,"address":4326402,"length":1,"stats":{"Line":1}},{"line":448,"address":4326907,"length":1,"stats":{"Line":1}},{"line":449,"address":4327393,"length":1,"stats":{"Line":1}},{"line":453,"address":4328023,"length":1,"stats":{"Line":0}},{"line":454,"address":4327479,"length":1,"stats":{"Line":1}},{"line":455,"address":4327878,"length":1,"stats":{"Line":1}},{"line":458,"address":4328624,"length":1,"stats":{"Line":1}},{"line":459,"address":4328566,"length":1,"stats":{"Line":1}},{"line":466,"address":4329123,"length":1,"stats":{"Line":1}},{"line":467,"address":4329065,"length":1,"stats":{"Line":1}},{"line":474,"address":4329610,"length":1,"stats":{"Line":1}},{"line":475,"address":4329552,"length":1,"stats":{"Line":1}},{"line":482,"address":4330067,"length":1,"stats":{"Line":1}},{"line":483,"address":4330009,"length":1,"stats":{"Line":1}},{"line":498,"address":4330608,"length":1,"stats":{"Line":2}},{"line":499,"address":4330628,"length":1,"stats":{"Line":1}},{"line":501,"address":4330648,"length":1,"stats":{"Line":1}},{"line":504,"address":4330715,"length":1,"stats":{"Line":1}},{"line":505,"address":4331221,"length":1,"stats":{"Line":1}},{"line":506,"address":4331271,"length":1,"stats":{"Line":1}},{"line":507,"address":4331775,"length":1,"stats":{"Line":1}},{"line":511,"address":4332405,"length":1,"stats":{"Line":0}},{"line":512,"address":4331861,"length":1,"stats":{"Line":1}},{"line":513,"address":4332260,"length":1,"stats":{"Line":1}},{"line":515,"address":4333128,"length":1,"stats":{"Line":0}},{"line":516,"address":4332948,"length":1,"stats":{"Line":1}},{"line":517,"address":4332983,"length":1,"stats":{"Line":1}},{"line":521,"address":4333671,"length":1,"stats":{"Line":1}},{"line":522,"address":4333706,"length":1,"stats":{"Line":1}},{"line":523,"address":4333721,"length":1,"stats":{"Line":1}},{"line":524,"address":4333752,"length":1,"stats":{"Line":1}},{"line":525,"address":4333787,"length":1,"stats":{"Line":1}},{"line":526,"address":4333810,"length":1,"stats":{"Line":1}},{"line":527,"address":4333825,"length":1,"stats":{"Line":1}},{"line":528,"address":4333856,"length":1,"stats":{"Line":1}},{"line":531,"address":4334111,"length":1,"stats":{"Line":1}},{"line":532,"address":4334360,"length":1,"stats":{"Line":1}},{"line":533,"address":4334695,"length":1,"stats":{"Line":1}},{"line":534,"address":4335505,"length":1,"stats":{"Line":1}},{"line":536,"address":4335396,"length":1,"stats":{"Line":1}},{"line":538,"address":4335947,"length":1,"stats":{"Line":1}},{"line":539,"address":4335968,"length":1,"stats":{"Line":1}},{"line":540,"address":4336093,"length":1,"stats":{"Line":1}},{"line":543,"address":4336158,"length":1,"stats":{"Line":1}},{"line":544,"address":4336100,"length":1,"stats":{"Line":1}},{"line":550,"address":4336657,"length":1,"stats":{"Line":1}},{"line":551,"address":4336599,"length":1,"stats":{"Line":1}},{"line":561,"address":4337150,"length":1,"stats":{"Line":1}},{"line":562,"address":4337092,"length":1,"stats":{"Line":1}},{"line":572,"address":4337607,"length":1,"stats":{"Line":1}},{"line":573,"address":4337549,"length":1,"stats":{"Line":1}},{"line":583,"address":4338224,"length":1,"stats":{"Line":2}},{"line":585,"address":4338238,"length":1,"stats":{"Line":1}},{"line":587,"address":4338304,"length":1,"stats":{"Line":1}},{"line":593,"address":4338353,"length":1,"stats":{"Line":1}},{"line":599,"address":4338395,"length":1,"stats":{"Line":1}},{"line":607,"address":4338422,"length":1,"stats":{"Line":1}},{"line":608,"address":4338555,"length":1,"stats":{"Line":1}},{"line":614,"address":4339099,"length":1,"stats":{"Line":1}},{"line":615,"address":4339512,"length":1,"stats":{"Line":1}},{"line":616,"address":4340226,"length":1,"stats":{"Line":1}},{"line":618,"address":4340097,"length":1,"stats":{"Line":1}},{"line":623,"address":4340720,"length":1,"stats":{"Line":2}},{"line":624,"address":4340727,"length":1,"stats":{"Line":1}},{"line":629,"address":4341024,"length":1,"stats":{"Line":1}},{"line":630,"address":4340992,"length":1,"stats":{"Line":1}},{"line":634,"address":4341482,"length":1,"stats":{"Line":1}},{"line":638,"address":4341722,"length":1,"stats":{"Line":0}},{"line":639,"address":4341566,"length":1,"stats":{"Line":1}},{"line":640,"address":4341595,"length":1,"stats":{"Line":1}},{"line":644,"address":4342221,"length":1,"stats":{"Line":1}},{"line":645,"address":4342242,"length":1,"stats":{"Line":1}},{"line":649,"address":4342262,"length":1,"stats":{"Line":1}},{"line":650,"address":4342329,"length":1,"stats":{"Line":1}},{"line":654,"address":4342480,"length":1,"stats":{"Line":2}},{"line":655,"address":4342487,"length":1,"stats":{"Line":1}},{"line":658,"address":4342725,"length":1,"stats":{"Line":1}},{"line":666,"address":4342752,"length":1,"stats":{"Line":1}},{"line":667,"address":4342870,"length":1,"stats":{"Line":1}}],"covered":184,"coverable":196},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","keys","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide wrappers around public keys, private keys and signatures.\n//!\n//! - Keys can be converted to/from Base58 string format.\n//! - Signatures can be converted to/from Base64 string format.\n//!\n//! # Usage\n//!\n//! ```\n//! use dup_crypto::keys::{Signature, PublicKey, PrivateKey, KeyPair};\n//! use dup_crypto::keys::ed25519::KeyPairFromSaltedPasswordGenerator;\n//!\n//! let generator = KeyPairFromSaltedPasswordGenerator::with_default_parameters();\n//!\n//! let keypair = generator.generate(\n//! b\"password\",\n//! b\"salt\"\n//! );\n//!\n//! let message = \"Hello, world!\";\n//!\n//! let signature = keypair.sign(\u0026message.as_bytes());\n//!\n//! assert!(keypair.pubkey.verify(\u0026message.as_bytes(), \u0026signature).is_ok());\n//! ```\n//!\n//! # Format\n//!\n//! - Base58 use the following alphabet :\n//! `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`\n//! - Base64 use the following alphabet :\n//! `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`\n//! with `=` as padding character.\n\nuse crate::bases::BaseConvertionError;\nuse base58::ToBase58;\nuse bincode;\nuse durs_common_tools::fatal_error;\nuse failure::Fail;\nuse std::fmt::Debug;\nuse std::fmt::Display;\nuse std::fmt::Error;\nuse std::fmt::Formatter;\nuse std::hash::Hash;\nuse std::str::FromStr;\n\npub mod bin_signable;\npub mod ed25519;\npub mod text_signable;\n\n/// Cryptographic keys algorithms list\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]\npub enum KeysAlgo {\n /// Ed25519 algorithm\n Ed25519 = 0,\n /// Schnorr algorithm\n Schnorr = 1,\n}\n\n/// Get the cryptographic algorithm.\npub trait GetKeysAlgo: Clone + Debug + PartialEq + Eq {\n /// Get the cryptographic algorithm.\n fn algo(\u0026self) -\u003e KeysAlgo;\n}\n\n/// Errors enumeration for signature verification.\n#[derive(Debug, Eq, Fail, PartialEq)]\npub enum SigError {\n /// Signature and pubkey are not the same algo\n #[fail(display = \"Signature and pubkey are not the same algo.\")]\n NotSameAlgo,\n /// Invalid signature\n #[fail(display = \"Invalid signature.\")]\n InvalidSig,\n /// Absence of signature\n #[fail(display = \"Absence of signature.\")]\n NotSig,\n /// Serialization error\n #[fail(display = \"Serialization error: {}\", _0)]\n SerdeError(String),\n}\n\n/// SignError\n#[derive(Debug, Eq, Fail, PartialEq)]\npub enum SignError {\n /// WrongAlgo\n #[fail(display = \"Wrong algo.\")]\n WrongAlgo,\n /// WrongPrivkey\n #[fail(display = \"Wrong private key.\")]\n WrongPrivkey,\n /// AlreadySign\n #[fail(display = \"Already signed.\")]\n AlreadySign,\n /// Serialization error\n #[fail(display = \"Serialization error: {}\", _0)]\n SerdeError(String),\n}\n\n/// Define the operations that can be performed on a cryptographic signature.\n///\n/// A signature can be converted from/to Base64 format.\n/// When converted back and forth the value should be the same.\n///\n/// A signature can be made with a [`PrivateKey`]\n/// and a message, and verified with the associated [`PublicKey`].\n///\n/// [`PrivateKey`]: trait.PrivateKey.html\n/// [`PublicKey`]: trait.PublicKey.html\npub trait Signature: Clone + Display + Debug + PartialEq + Eq + Hash {\n /// Create a `Signature` from a Base64 string.\n ///\n /// The Base64 string should contains only valid Base64 characters\n /// and have a correct length (64 bytes when converted). If it's not the case,\n /// a [`BaseConvertionError`] is returned with the corresponding variant.\n ///\n /// [`BaseConvertionError`]: enum.BaseConvertionError.html\n fn from_base64(base64_string: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e;\n\n /// Convert Signature into butes vector\n fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e;\n\n /// Encode the signature into Base64 string format.\n fn to_base64(\u0026self) -\u003e String;\n}\n\n/// Store a cryptographic signature.\n#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]\npub enum Sig {\n /// Store a ed25519 Signature\n Ed25519(ed25519::Signature),\n /// Store a Schnorr Signature\n Schnorr(),\n}\n\nimpl Sig {\n /// Get Sig size in bytes\n pub fn size_in_bytes(\u0026self) -\u003e usize {\n match *self {\n Sig::Ed25519(_) =\u003e *ed25519::SIG_SIZE_IN_BYTES + 2,\n Sig::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\nimpl Display for Sig {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.to_base64())\n }\n}\n\nimpl GetKeysAlgo for Sig {\n fn algo(\u0026self) -\u003e KeysAlgo {\n match *self {\n Sig::Ed25519(_) =\u003e KeysAlgo::Ed25519,\n Sig::Schnorr() =\u003e KeysAlgo::Schnorr,\n }\n }\n}\n\nimpl Signature for Sig {\n #[cfg_attr(tarpaulin, skip)]\n fn from_base64(_base64_string: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e {\n unimplemented!()\n }\n fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e {\n match *self {\n Sig::Ed25519(ed25519_sig) =\u003e ed25519_sig.to_bytes_vector(),\n Sig::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n fn to_base64(\u0026self) -\u003e String {\n match *self {\n Sig::Ed25519(ed25519_sig) =\u003e ed25519_sig.to_base64(),\n Sig::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\n/// Define the operations that can be performed on a cryptographic public key.\n///\n/// A `PublicKey` can be converted from/to Base64 format.\n/// When converted back and forth the value should be the same.\n///\n/// A `PublicKey` is used to verify the signature of a message\n/// with the associated [`PrivateKey`].\n///\n/// [`PrivateKey`]: trait.PrivateKey.html\npub trait PublicKey: Clone + Display + Debug + PartialEq + Eq + Hash + ToBase58 {\n /// Signature type of associated cryptosystem.\n type Signature: Signature;\n\n /// Create a PublicKey from a Base58 string.\n ///\n /// The Base58 string should contains only valid Base58 characters\n /// and have a correct length. If it's not the case,\n /// a [`BaseConvertionError`] is returned with the corresponding variant.\n ///\n /// [`BaseConvertionError`]: enum.BaseConvertionError.html\n fn from_base58(base58_string: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e;\n\n /// Convert into bytes vector\n fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e;\n\n /// Verify a signature with this public key.\n fn verify(\u0026self, message: \u0026[u8], signature: \u0026Self::Signature) -\u003e Result\u003c(), SigError\u003e;\n}\n\n/// Store a cryptographic public key.\n#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]\npub enum PubKey {\n /// Store a ed25519 public key.\n Ed25519(ed25519::PublicKey),\n /// Store a Schnorr public key.\n Schnorr(),\n}\n\nimpl PubKey {\n /// Compute PubKey size in bytes\n pub fn size_in_bytes(\u0026self) -\u003e usize {\n match *self {\n PubKey::Ed25519(_) =\u003e ed25519::PUBKEY_SIZE_IN_BYTES + 3,\n PubKey::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\nimpl Default for PubKey {\n fn default() -\u003e Self {\n PubKey::Ed25519(ed25519::PublicKey([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0,\n ]))\n }\n}\n\nimpl GetKeysAlgo for PubKey {\n fn algo(\u0026self) -\u003e KeysAlgo {\n match *self {\n PubKey::Ed25519(_) =\u003e KeysAlgo::Ed25519,\n PubKey::Schnorr() =\u003e KeysAlgo::Schnorr,\n }\n }\n}\n\nimpl ToBase58 for PubKey {\n fn to_base58(\u0026self) -\u003e String {\n match *self {\n PubKey::Ed25519(ed25519_pub) =\u003e ed25519_pub.to_base58(),\n PubKey::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\nimpl Display for PubKey {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.to_base58())\n }\n}\n\nimpl FromStr for PubKey {\n type Err = BaseConvertionError;\n\n fn from_str(s: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n ed25519::PublicKey::from_base58(s).map(PubKey::Ed25519)\n }\n}\n\nimpl PublicKey for PubKey {\n type Signature = Sig;\n\n #[cfg_attr(tarpaulin, skip)]\n fn from_base58(_base58_string: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e {\n unimplemented!()\n }\n fn to_bytes_vector(\u0026self) -\u003e Vec\u003cu8\u003e {\n match *self {\n PubKey::Ed25519(ed25519_pubkey) =\u003e ed25519_pubkey.to_bytes_vector(),\n PubKey::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n fn verify(\u0026self, message: \u0026[u8], signature: \u0026Self::Signature) -\u003e Result\u003c(), SigError\u003e {\n match *self {\n PubKey::Ed25519(ed25519_pubkey) =\u003e {\n if let Sig::Ed25519(ed25519_sig) = signature {\n ed25519_pubkey.verify(message, ed25519_sig)\n } else {\n fatal_error!(\"Try to verify a signature with public key of a different algorithm !\\nSignature={:?}\\nPublickey={:?}\", signature, self)\n }\n }\n PubKey::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\n/// Define the operations that can be performed on a cryptographic private key.\n///\n/// A `PrivateKey` can be converted from/to Base58 format.\n/// When converted back and forth the value should be the same.\n///\n/// A `PrivateKey` is used to sign a message. The corresponding [`PublicKey`]\n/// will then be used to verify that signature.\n///\n/// [`PublicKey`]: trait.PublicKey.html\npub trait PrivateKey: Clone + Display + Debug + PartialEq + Eq + ToBase58 {\n /// Signature type of associated cryptosystem.\n type Signature: Signature;\n\n /// Create a PrivateKey from a Base58 string.\n ///\n /// The Base58 string should contains only valid Base58 characters\n /// and have a correct length. If it's not the case, a [`BaseConvertionError`]\n /// is returned with the corresponding variant.\n ///\n /// [`BaseConvertionError`]: enum.BaseConvertionError.html\n fn from_base58(base58_string: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e;\n\n /// Sign a message with this private key.\n fn sign(\u0026self, message: \u0026[u8]) -\u003e Self::Signature;\n}\n\n/// Store a cryptographic private key.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum PrivKey {\n /// Store a ed25519 private key.\n Ed25519(ed25519::PrivateKey),\n /// Store a Schnorr private key.\n Schnorr(),\n}\n\nimpl GetKeysAlgo for PrivKey {\n fn algo(\u0026self) -\u003e KeysAlgo {\n match *self {\n PrivKey::Ed25519(_) =\u003e KeysAlgo::Ed25519,\n PrivKey::Schnorr() =\u003e KeysAlgo::Schnorr,\n }\n }\n}\n\nimpl ToBase58 for PrivKey {\n fn to_base58(\u0026self) -\u003e String {\n match *self {\n PrivKey::Ed25519(ed25519_privkey) =\u003e ed25519_privkey.to_base58(),\n PrivKey::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\nimpl Display for PrivKey {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.to_base58())\n }\n}\n\nimpl PrivateKey for PrivKey {\n type Signature = Sig;\n\n #[cfg_attr(tarpaulin, skip)]\n fn from_base58(_base58_string: \u0026str) -\u003e Result\u003cSelf, BaseConvertionError\u003e {\n unimplemented!()\n }\n fn sign(\u0026self, message: \u0026[u8]) -\u003e Self::Signature {\n match *self {\n PrivKey::Ed25519(ed25519_privkey) =\u003e Sig::Ed25519(ed25519_privkey.sign(message)),\n PrivKey::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\n/// Define the operations that can be performed on a cryptographic key pair.\npub trait KeyPair: Clone + Display + Debug + PartialEq + Eq {\n /// Signature type of associated cryptosystem.\n type Signature: Signature;\n /// PublicKey type of associated cryptosystem.\n type PublicKey: PublicKey;\n /// PrivateKey type of associated cryptosystem.\n type PrivateKey: PrivateKey;\n\n /// Get `PublicKey`\n fn public_key(\u0026self) -\u003e Self::PublicKey;\n\n /// Get `PrivateKey`\n fn private_key(\u0026self) -\u003e Self::PrivateKey;\n\n /// Sign a message with private key.\n fn sign(\u0026self, message: \u0026[u8]) -\u003e Self::Signature;\n\n /// Verify a signature with public key.\n fn verify(\u0026self, message: \u0026[u8], signature: \u0026Self::Signature) -\u003e Result\u003c(), SigError\u003e;\n}\n\n/// Store a cryptographic key pair.\n#[derive(Clone, Copy, Debug, Eq, PartialEq)]\npub enum KeyPairEnum {\n /// Store a ed25519 key pair.\n Ed25519(ed25519::KeyPair),\n /// Store a Schnorr key pair.\n Schnorr(),\n}\n\nimpl GetKeysAlgo for KeyPairEnum {\n fn algo(\u0026self) -\u003e KeysAlgo {\n match *self {\n KeyPairEnum::Ed25519(_) =\u003e KeysAlgo::Ed25519,\n KeyPairEnum::Schnorr() =\u003e KeysAlgo::Schnorr,\n }\n }\n}\n\nimpl Display for KeyPairEnum {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n match *self {\n KeyPairEnum::Ed25519(ed25519_keypair) =\u003e {\n write!(f, \"({}, hidden)\", ed25519_keypair.pubkey.to_base58())\n }\n KeyPairEnum::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\nimpl KeyPair for KeyPairEnum {\n type Signature = Sig;\n type PublicKey = PubKey;\n type PrivateKey = PrivKey;\n\n fn public_key(\u0026self) -\u003e Self::PublicKey {\n match *self {\n KeyPairEnum::Ed25519(ed25519_keypair) =\u003e PubKey::Ed25519(ed25519_keypair.public_key()),\n KeyPairEnum::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n fn private_key(\u0026self) -\u003e Self::PrivateKey {\n match *self {\n KeyPairEnum::Ed25519(ed25519_keypair) =\u003e {\n PrivKey::Ed25519(ed25519_keypair.private_key())\n }\n KeyPairEnum::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n fn verify(\u0026self, message: \u0026[u8], signature: \u0026Sig) -\u003e Result\u003c(), SigError\u003e {\n match *self {\n KeyPairEnum::Ed25519(ed25519_keypair) =\u003e {\n if let Sig::Ed25519(ed25519_sig) = signature {\n ed25519_keypair.verify(message, ed25519_sig)\n } else {\n fatal_error!(\"Try to verify a signature with key pair of a different algorithm !\\nSignature={:?}\\nKeyPair={}\", signature, self)\n }\n }\n KeyPairEnum::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n fn sign(\u0026self, message: \u0026[u8]) -\u003e Sig {\n match *self {\n KeyPairEnum::Ed25519(ed25519_keypair) =\u003e Sig::Ed25519(ed25519_keypair.sign(message)),\n KeyPairEnum::Schnorr() =\u003e fatal_error!(\"Schnorr algo not yet supported !\"),\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n\n pub fn valid_key_pair_1() -\u003e KeyPairEnum {\n let kp = KeyPairEnum::Ed25519(ed25519::KeyPair {\n pubkey: ed25519::PublicKey([\n 59u8, 106, 39, 188, 206, 182, 164, 45, 98, 163, 168, 208, 42, 111, 13, 115, 101,\n 50, 21, 119, 29, 226, 67, 166, 58, 192, 72, 161, 139, 89, 218, 41,\n ]),\n privkey: ed25519::PrivateKey([\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 59, 106, 39, 188, 206, 182, 164, 45, 98, 163, 168, 208, 42, 111, 13,\n 115, 101, 50, 21, 119, 29, 226, 67, 166, 58, 192, 72, 161, 139, 89, 218, 41,\n ]),\n });\n println!(\"kp.pub={:?}\", kp.public_key().to_bytes_vector());\n if let PrivKey::Ed25519(ed_pk) = kp.private_key() {\n println!(\"kp.priv={:?}\", ed_pk.0.to_vec());\n }\n kp\n }\n\n #[test]\n fn sig() {\n let sig_bytes = [\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0,\n ];\n let sig_str_b64 = \"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\".to_owned();\n let sig = Sig::Ed25519(ed25519::Signature(sig_bytes));\n\n assert_eq!(sig.size_in_bytes(), *ed25519::SIG_SIZE_IN_BYTES + 2);\n assert_eq!(sig_str_b64, format!(\"{}\", sig));\n\n assert_eq!(KeysAlgo::Ed25519, sig.algo());\n assert_eq!(KeysAlgo::Schnorr, Sig::Schnorr().algo());\n\n assert_eq!(sig_bytes.to_vec(), sig.to_bytes_vector());\n\n assert_eq!(sig_str_b64, sig.to_base64());\n }\n\n #[test]\n fn pubkey() {\n let pubkey_default = PubKey::default();\n let pubkey_bytes = [\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0,\n ];\n let pubkey_str_b58 = \"11111111111111111111111111111111\".to_owned();\n let pubkey = PubKey::Ed25519(ed25519::PublicKey(pubkey_bytes));\n\n assert_eq!(pubkey_default, pubkey);\n assert_eq!(\n pubkey_default,\n PubKey::from_str(\u0026pubkey_str_b58).expect(\"Fail to parse pubkey !\")\n );\n\n assert_eq!(pubkey.size_in_bytes(), *ed25519::PUBKEY_SIZE_IN_BYTES + 3);\n assert_eq!(pubkey_str_b58, format!(\"{}\", pubkey));\n\n assert_eq!(KeysAlgo::Ed25519, pubkey.algo());\n assert_eq!(KeysAlgo::Schnorr, PubKey::Schnorr().algo());\n\n assert_eq!(pubkey_bytes.to_vec(), pubkey.to_bytes_vector());\n\n assert_eq!(pubkey_str_b58, pubkey.to_base58());\n\n assert_eq!(\n Err(SigError::InvalidSig),\n pubkey.verify(\n b\"message\",\n \u0026Sig::Ed25519(ed25519::Signature([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n ]))\n )\n )\n }\n\n #[test]\n fn privkey() {\n let privkey_bytes = [\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0,\n ];\n let privkey_str_b58 =\n \"1111111111111111111111111111111111111111111111111111111111111111\".to_owned();\n let privkey = PrivKey::Ed25519(ed25519::PrivateKey(privkey_bytes));\n\n assert_eq!(privkey_str_b58, format!(\"{}\", privkey));\n\n assert_eq!(KeysAlgo::Ed25519, privkey.algo());\n assert_eq!(KeysAlgo::Schnorr, PrivKey::Schnorr().algo());\n\n assert_eq!(privkey_str_b58, privkey.to_base58());\n\n assert_eq!(\n Sig::Ed25519(ed25519::Signature::from_base64(\"JPurBgnHExHND1woow9nB7xVQjKkdHGs1znQbgv0ttboJXueHKd4SOvxuNWmw4w07F4CT//olYMEBw51Cy0SDA==\").unwrap()),\n privkey.sign(b\"message\")\n );\n }\n\n fn false_key_pair_ed25519() -\u003e ed25519::KeyPair {\n ed25519::KeyPair {\n pubkey: ed25519::PublicKey([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0,\n ]),\n privkey: ed25519::PrivateKey([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0,\n ]),\n }\n }\n\n #[test]\n fn key_pair() {\n let false_key_pair_ed25519 = false_key_pair_ed25519();\n let false_key_pair = KeyPairEnum::Ed25519(false_key_pair_ed25519);\n\n assert_eq!(KeysAlgo::Ed25519, false_key_pair.algo());\n assert_eq!(KeysAlgo::Schnorr, KeyPairEnum::Schnorr().algo());\n assert_eq!(\n \"(11111111111111111111111111111111, hidden)\".to_owned(),\n format!(\"{}\", false_key_pair)\n );\n assert_eq!(\n PubKey::Ed25519(false_key_pair_ed25519.pubkey),\n false_key_pair.public_key()\n );\n assert_eq!(\n PrivKey::Ed25519(false_key_pair_ed25519.privkey),\n false_key_pair.private_key()\n );\n assert_eq!(\n Err(SigError::InvalidSig),\n false_key_pair.verify(\n b\"message\",\n \u0026Sig::Ed25519(ed25519::Signature([\n 0u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\n ]))\n )\n );\n }\n\n #[test]\n #[should_panic(\n expected = \"Try to verify a signature with key pair of a different algorithm !\\n\\\n Signature=Schnorr\\nKeyPair=(11111111111111111111111111111111, hidden)\"\n )]\n fn key_pair_verify_wrong_sig_algo() {\n let false_key_pair_ed25519 = false_key_pair_ed25519();\n let false_key_pair = KeyPairEnum::Ed25519(false_key_pair_ed25519);\n let _ = false_key_pair.verify(b\"message\", \u0026Sig::Schnorr());\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn sig_schnorr_size() {\n let sig = Sig::Schnorr();\n sig.size_in_bytes();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn sig_schnorr_to_bytes() {\n let sig = Sig::Schnorr();\n sig.to_bytes_vector();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn sig_schnorr_to_base64() {\n let sig = Sig::Schnorr();\n sig.to_base64();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn pubkey_schnorr_size() {\n let pubkey = PubKey::Schnorr();\n pubkey.size_in_bytes();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn pubkey_schnorr_base58() {\n let pubkey = PubKey::Schnorr();\n pubkey.to_base58();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn pubkey_schnorr_to_bytes() {\n let pubkey = PubKey::Schnorr();\n pubkey.to_bytes_vector();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn pubkey_schnorr_verify() {\n let pubkey = PubKey::Schnorr();\n let _ = pubkey.verify(b\"message\", \u0026Sig::Schnorr());\n }\n\n #[test]\n #[should_panic(\n expected = \"Try to verify a signature with public key of a different algorithm !\\n\\\n Signature=Schnorr\\nPublickey=Ed25519(PublicKey { 11111111111111111111111111111111 })\"\n )]\n fn pubkey_verify_sig_wrong_algo() {\n let pubkey = PubKey::default();\n let _ = pubkey.verify(b\"message\", \u0026Sig::Schnorr());\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn privkey_schnorr_base58() {\n let privkey = PrivKey::Schnorr();\n privkey.to_base58();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn privkey_schnorr_sign() {\n let privkey = PrivKey::Schnorr();\n privkey.sign(b\"message\");\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn key_pair_schnorr_display() {\n let key_pair = KeyPairEnum::Schnorr();\n format!(\"{}\", key_pair);\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn key_pair_schnorr_get_pubkey() {\n let key_pair = KeyPairEnum::Schnorr();\n key_pair.public_key();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn key_pair_schnorr_get_privkey() {\n let key_pair = KeyPairEnum::Schnorr();\n key_pair.private_key();\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn key_pair_schnorr_verify() {\n let key_pair = KeyPairEnum::Schnorr();\n let _ = key_pair.verify(b\"message\", \u0026Sig::Schnorr());\n }\n\n #[test]\n #[should_panic(expected = \"Schnorr algo not yet supported !\")]\n fn key_pair_schnorr_sign() {\n let key_pair = KeyPairEnum::Schnorr();\n key_pair.sign(b\"message\");\n }\n}\n","traces":[{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":3}},{"line":162,"address":null,"length":0,"stats":{"Line":3}},{"line":167,"address":null,"length":0,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":169,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":3}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":3}},{"line":189,"address":null,"length":0,"stats":{"Line":1}},{"line":234,"address":null,"length":0,"stats":{"Line":1}},{"line":235,"address":null,"length":0,"stats":{"Line":0}},{"line":236,"address":null,"length":0,"stats":{"Line":1}},{"line":237,"address":null,"length":0,"stats":{"Line":1}},{"line":243,"address":null,"length":0,"stats":{"Line":1}},{"line":244,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":0}},{"line":252,"address":null,"length":0,"stats":{"Line":1}},{"line":253,"address":null,"length":0,"stats":{"Line":1}},{"line":254,"address":null,"length":0,"stats":{"Line":1}},{"line":255,"address":null,"length":0,"stats":{"Line":1}},{"line":261,"address":null,"length":0,"stats":{"Line":6}},{"line":262,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":6}},{"line":264,"address":null,"length":0,"stats":{"Line":1}},{"line":270,"address":null,"length":0,"stats":{"Line":5}},{"line":271,"address":null,"length":0,"stats":{"Line":5}},{"line":278,"address":null,"length":0,"stats":{"Line":1}},{"line":279,"address":null,"length":0,"stats":{"Line":1}},{"line":290,"address":null,"length":0,"stats":{"Line":1}},{"line":291,"address":null,"length":0,"stats":{"Line":0}},{"line":292,"address":null,"length":0,"stats":{"Line":1}},{"line":293,"address":null,"length":0,"stats":{"Line":1}},{"line":296,"address":null,"length":0,"stats":{"Line":4}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":4}},{"line":299,"address":null,"length":0,"stats":{"Line":4}},{"line":300,"address":null,"length":0,"stats":{"Line":4}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":1}},{"line":305,"address":null,"length":0,"stats":{"Line":1}},{"line":346,"address":null,"length":0,"stats":{"Line":1}},{"line":347,"address":null,"length":0,"stats":{"Line":1}},{"line":348,"address":null,"length":0,"stats":{"Line":1}},{"line":349,"address":null,"length":0,"stats":{"Line":1}},{"line":355,"address":null,"length":0,"stats":{"Line":1}},{"line":356,"address":null,"length":0,"stats":{"Line":0}},{"line":357,"address":null,"length":0,"stats":{"Line":1}},{"line":358,"address":null,"length":0,"stats":{"Line":1}},{"line":364,"address":null,"length":0,"stats":{"Line":1}},{"line":365,"address":null,"length":0,"stats":{"Line":1}},{"line":376,"address":null,"length":0,"stats":{"Line":2}},{"line":377,"address":null,"length":0,"stats":{"Line":0}},{"line":378,"address":null,"length":0,"stats":{"Line":2}},{"line":379,"address":null,"length":0,"stats":{"Line":1}},{"line":416,"address":null,"length":0,"stats":{"Line":1}},{"line":417,"address":null,"length":0,"stats":{"Line":1}},{"line":418,"address":null,"length":0,"stats":{"Line":1}},{"line":419,"address":null,"length":0,"stats":{"Line":1}},{"line":425,"address":null,"length":0,"stats":{"Line":1}},{"line":426,"address":null,"length":0,"stats":{"Line":0}},{"line":427,"address":null,"length":0,"stats":{"Line":1}},{"line":428,"address":null,"length":0,"stats":{"Line":1}},{"line":430,"address":null,"length":0,"stats":{"Line":1}},{"line":440,"address":null,"length":0,"stats":{"Line":3}},{"line":441,"address":null,"length":0,"stats":{"Line":0}},{"line":442,"address":null,"length":0,"stats":{"Line":3}},{"line":443,"address":null,"length":0,"stats":{"Line":1}},{"line":446,"address":null,"length":0,"stats":{"Line":4}},{"line":447,"address":null,"length":0,"stats":{"Line":0}},{"line":448,"address":null,"length":0,"stats":{"Line":4}},{"line":449,"address":null,"length":0,"stats":{"Line":4}},{"line":451,"address":null,"length":0,"stats":{"Line":1}},{"line":454,"address":null,"length":0,"stats":{"Line":1}},{"line":455,"address":null,"length":0,"stats":{"Line":0}},{"line":456,"address":null,"length":0,"stats":{"Line":1}},{"line":457,"address":null,"length":0,"stats":{"Line":1}},{"line":458,"address":null,"length":0,"stats":{"Line":1}},{"line":459,"address":null,"length":0,"stats":{"Line":0}},{"line":460,"address":null,"length":0,"stats":{"Line":1}},{"line":463,"address":null,"length":0,"stats":{"Line":1}},{"line":466,"address":null,"length":0,"stats":{"Line":1}},{"line":467,"address":null,"length":0,"stats":{"Line":0}},{"line":468,"address":null,"length":0,"stats":{"Line":1}},{"line":469,"address":null,"length":0,"stats":{"Line":1}},{"line":479,"address":4264496,"length":1,"stats":{"Line":1}},{"line":480,"address":4265390,"length":1,"stats":{"Line":1}},{"line":481,"address":4264506,"length":1,"stats":{"Line":1}},{"line":485,"address":4264826,"length":1,"stats":{"Line":1}},{"line":491,"address":4265538,"length":1,"stats":{"Line":1}},{"line":492,"address":4265775,"length":1,"stats":{"Line":1}},{"line":493,"address":4265874,"length":1,"stats":{"Line":1}},{"line":495,"address":4266082,"length":1,"stats":{"Line":1}},{"line":499,"address":4267024,"length":1,"stats":{"Line":2}},{"line":500,"address":4267038,"length":1,"stats":{"Line":1}},{"line":505,"address":4267550,"length":1,"stats":{"Line":1}},{"line":506,"address":4267588,"length":1,"stats":{"Line":1}},{"line":508,"address":4267796,"length":1,"stats":{"Line":1}},{"line":509,"address":4268148,"length":1,"stats":{"Line":1}},{"line":511,"address":4269022,"length":1,"stats":{"Line":1}},{"line":512,"address":4269501,"length":1,"stats":{"Line":1}},{"line":514,"address":4269975,"length":1,"stats":{"Line":1}},{"line":516,"address":4270517,"length":1,"stats":{"Line":1}},{"line":520,"address":4271120,"length":1,"stats":{"Line":2}},{"line":521,"address":4271127,"length":1,"stats":{"Line":1}},{"line":522,"address":4271164,"length":1,"stats":{"Line":1}},{"line":526,"address":4271420,"length":1,"stats":{"Line":1}},{"line":527,"address":4271441,"length":1,"stats":{"Line":1}},{"line":529,"address":4271553,"length":1,"stats":{"Line":1}},{"line":530,"address":4272118,"length":1,"stats":{"Line":1}},{"line":532,"address":4272021,"length":1,"stats":{"Line":1}},{"line":535,"address":4272568,"length":1,"stats":{"Line":1}},{"line":536,"address":4272900,"length":1,"stats":{"Line":1}},{"line":538,"address":4273774,"length":1,"stats":{"Line":1}},{"line":539,"address":4274253,"length":1,"stats":{"Line":1}},{"line":541,"address":4274739,"length":1,"stats":{"Line":1}},{"line":543,"address":4275311,"length":1,"stats":{"Line":1}},{"line":545,"address":4275819,"length":1,"stats":{"Line":1}},{"line":547,"address":4275759,"length":1,"stats":{"Line":1}},{"line":559,"address":4276496,"length":1,"stats":{"Line":2}},{"line":560,"address":4276510,"length":1,"stats":{"Line":1}},{"line":566,"address":4277022,"length":1,"stats":{"Line":1}},{"line":567,"address":4277060,"length":1,"stats":{"Line":1}},{"line":569,"address":4277268,"length":1,"stats":{"Line":1}},{"line":571,"address":4277949,"length":1,"stats":{"Line":1}},{"line":572,"address":4278428,"length":1,"stats":{"Line":1}},{"line":574,"address":4278918,"length":1,"stats":{"Line":1}},{"line":576,"address":4279531,"length":1,"stats":{"Line":1}},{"line":577,"address":4279366,"length":1,"stats":{"Line":1}},{"line":578,"address":4279488,"length":1,"stats":{"Line":1}},{"line":582,"address":4266160,"length":1,"stats":{"Line":1}},{"line":584,"address":4266170,"length":1,"stats":{"Line":1}},{"line":588,"address":4266370,"length":1,"stats":{"Line":1}},{"line":597,"address":4280016,"length":1,"stats":{"Line":2}},{"line":598,"address":4280023,"length":1,"stats":{"Line":1}},{"line":599,"address":4280053,"length":1,"stats":{"Line":1}},{"line":601,"address":4280173,"length":1,"stats":{"Line":1}},{"line":602,"address":4280606,"length":1,"stats":{"Line":1}},{"line":603,"address":4281216,"length":1,"stats":{"Line":0}},{"line":604,"address":4281042,"length":1,"stats":{"Line":1}},{"line":605,"address":4281071,"length":1,"stats":{"Line":1}},{"line":607,"address":4281914,"length":1,"stats":{"Line":1}},{"line":608,"address":4281757,"length":1,"stats":{"Line":1}},{"line":609,"address":4281893,"length":1,"stats":{"Line":1}},{"line":611,"address":4282441,"length":1,"stats":{"Line":1}},{"line":612,"address":4282327,"length":1,"stats":{"Line":1}},{"line":613,"address":4282420,"length":1,"stats":{"Line":1}},{"line":615,"address":4282880,"length":1,"stats":{"Line":1}},{"line":617,"address":4282838,"length":1,"stats":{"Line":1}},{"line":633,"address":4283392,"length":1,"stats":{"Line":2}},{"line":634,"address":4283399,"length":1,"stats":{"Line":1}},{"line":635,"address":4283423,"length":1,"stats":{"Line":1}},{"line":636,"address":4283534,"length":1,"stats":{"Line":1}},{"line":641,"address":4283600,"length":1,"stats":{"Line":2}},{"line":642,"address":4283604,"length":1,"stats":{"Line":1}},{"line":643,"address":4283609,"length":1,"stats":{"Line":1}},{"line":648,"address":4283632,"length":1,"stats":{"Line":2}},{"line":649,"address":4283636,"length":1,"stats":{"Line":1}},{"line":650,"address":4283641,"length":1,"stats":{"Line":1}},{"line":655,"address":4283680,"length":1,"stats":{"Line":2}},{"line":656,"address":4283684,"length":1,"stats":{"Line":1}},{"line":657,"address":4283689,"length":1,"stats":{"Line":1}},{"line":662,"address":4283728,"length":1,"stats":{"Line":2}},{"line":663,"address":4283732,"length":1,"stats":{"Line":1}},{"line":664,"address":4283737,"length":1,"stats":{"Line":1}},{"line":669,"address":4283760,"length":1,"stats":{"Line":2}},{"line":670,"address":4283764,"length":1,"stats":{"Line":1}},{"line":671,"address":4283769,"length":1,"stats":{"Line":1}},{"line":676,"address":4283808,"length":1,"stats":{"Line":2}},{"line":677,"address":4283812,"length":1,"stats":{"Line":1}},{"line":678,"address":4283817,"length":1,"stats":{"Line":1}},{"line":683,"address":4283856,"length":1,"stats":{"Line":2}},{"line":684,"address":4283874,"length":1,"stats":{"Line":1}},{"line":685,"address":4283879,"length":1,"stats":{"Line":1}},{"line":693,"address":4283936,"length":1,"stats":{"Line":2}},{"line":694,"address":4283940,"length":1,"stats":{"Line":1}},{"line":695,"address":4283964,"length":1,"stats":{"Line":1}},{"line":700,"address":4284016,"length":1,"stats":{"Line":2}},{"line":701,"address":4284020,"length":1,"stats":{"Line":1}},{"line":702,"address":4284025,"length":1,"stats":{"Line":1}},{"line":707,"address":4284064,"length":1,"stats":{"Line":2}},{"line":708,"address":4284078,"length":1,"stats":{"Line":1}},{"line":709,"address":4284083,"length":1,"stats":{"Line":1}},{"line":714,"address":4284128,"length":1,"stats":{"Line":2}},{"line":715,"address":4284135,"length":1,"stats":{"Line":1}},{"line":716,"address":4284140,"length":1,"stats":{"Line":1}},{"line":721,"address":4284320,"length":1,"stats":{"Line":2}},{"line":722,"address":4284327,"length":1,"stats":{"Line":1}},{"line":723,"address":4284332,"length":1,"stats":{"Line":1}},{"line":728,"address":4284368,"length":1,"stats":{"Line":2}},{"line":729,"address":4284375,"length":1,"stats":{"Line":1}},{"line":730,"address":4284380,"length":1,"stats":{"Line":1}},{"line":735,"address":4284416,"length":1,"stats":{"Line":2}},{"line":736,"address":4284437,"length":1,"stats":{"Line":1}},{"line":737,"address":4284442,"length":1,"stats":{"Line":1}},{"line":742,"address":4284496,"length":1,"stats":{"Line":2}},{"line":743,"address":4284510,"length":1,"stats":{"Line":1}},{"line":744,"address":4284515,"length":1,"stats":{"Line":1}}],"covered":188,"coverable":207},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","crypto","src","keys","text_signable.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Generic code for signing data in text format\n\nuse super::*;\n\n/// Signatureable in text format\npub trait TextSignable: Debug + Clone {\n /// Return signable text\n fn as_signable_text(\u0026self) -\u003e String;\n /// Return entity issuer pubkey\n fn issuer_pubkey(\u0026self) -\u003e PubKey;\n /// Return entity signature\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e;\n /// Change signature\n fn set_signature(\u0026mut self, _signature: Sig);\n /// Sign entity\n fn sign(\u0026mut self, priv_key: PrivKey) -\u003e Result\u003cString, SignError\u003e {\n if self.signature().is_some() {\n return Err(SignError::AlreadySign);\n }\n match self.issuer_pubkey() {\n PubKey::Ed25519(_) =\u003e match priv_key {\n PrivKey::Ed25519(priv_key) =\u003e {\n let text = self.as_signable_text();\n let sig = priv_key.sign(\u0026text.as_bytes());\n self.set_signature(Sig::Ed25519(sig));\n let str_sig = sig.to_base64();\n Ok(format!(\"{}{}\", text, str_sig))\n }\n _ =\u003e Err(SignError::WrongAlgo),\n },\n _ =\u003e Err(SignError::WrongAlgo),\n }\n }\n /// Check signature of entity\n fn verify(\u0026self) -\u003e Result\u003c(), SigError\u003e {\n if let Some(signature) = self.signature() {\n match self.issuer_pubkey() {\n PubKey::Ed25519(pubkey) =\u003e match signature {\n Sig::Ed25519(sig) =\u003e {\n pubkey.verify(\u0026self.as_signable_text().as_bytes(), \u0026sig)\n /*\n if pubkey.verify(\u0026self.as_signable_text().as_bytes(), \u0026sig) {\n Ok(())\n } else {\n Err(SigError::InvalidSig())\n }\n */\n }\n _ =\u003e Err(SigError::NotSameAlgo),\n },\n _ =\u003e Err(SigError::NotSameAlgo),\n }\n } else {\n Err(SigError::NotSig)\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n\n #[derive(Debug, Clone)]\n struct TextSignableTestImpl {\n issuer: PubKey,\n text: String,\n sig: Option\u003cSig\u003e,\n }\n\n impl TextSignable for TextSignableTestImpl {\n fn as_signable_text(\u0026self) -\u003e String {\n format!(\"{}:{}\", self.issuer, self.text)\n }\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n self.issuer\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n self.sig\n }\n fn set_signature(\u0026mut self, new_signature: Sig) {\n self.sig = Some(new_signature);\n }\n }\n\n #[test]\n fn test_text_signable() {\n let key_pair = super::super::tests::valid_key_pair_1();\n\n let mut text_signable = TextSignableTestImpl {\n issuer: key_pair.public_key(),\n text: \"toto\".to_owned(),\n sig: None,\n };\n\n assert_eq!(Err(SigError::NotSig), text_signable.verify());\n assert_eq!(\n Err(SignError::WrongAlgo),\n text_signable.sign(PrivKey::Schnorr())\n );\n text_signable.issuer = PubKey::Schnorr();\n assert_eq!(\n Err(SignError::WrongAlgo),\n text_signable.sign(key_pair.private_key())\n );\n text_signable.issuer = key_pair.public_key();\n assert_eq!(\n Ok(\"4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS:totoe53bEIYLX3IK7p0IBm60jAZfrQWxrBpinvzJqGxvD3kAzoe+zp2FGIXuxHMobrImu/Au33sB9k/6yOhtFz/YAw==\".to_owned()),\n text_signable.sign(key_pair.private_key())\n );\n assert_eq!(\n Err(SignError::AlreadySign),\n text_signable.sign(key_pair.private_key())\n );\n assert_eq!(Ok(()), text_signable.verify());\n let old_sig = text_signable.sig.replace(Sig::Schnorr());\n assert_eq!(Err(SigError::NotSameAlgo), text_signable.verify());\n text_signable.sig = old_sig;\n text_signable.issuer = PubKey::Schnorr();\n assert_eq!(Err(SigError::NotSameAlgo), text_signable.verify());\n }\n}\n","traces":[{"line":31,"address":null,"length":0,"stats":{"Line":4}},{"line":32,"address":null,"length":0,"stats":{"Line":4}},{"line":33,"address":null,"length":0,"stats":{"Line":1}},{"line":35,"address":null,"length":0,"stats":{"Line":4}},{"line":36,"address":null,"length":0,"stats":{"Line":4}},{"line":37,"address":null,"length":0,"stats":{"Line":4}},{"line":38,"address":null,"length":0,"stats":{"Line":4}},{"line":39,"address":null,"length":0,"stats":{"Line":4}},{"line":40,"address":null,"length":0,"stats":{"Line":4}},{"line":41,"address":null,"length":0,"stats":{"Line":4}},{"line":42,"address":null,"length":0,"stats":{"Line":4}},{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":46,"address":null,"length":0,"stats":{"Line":1}},{"line":50,"address":null,"length":0,"stats":{"Line":3}},{"line":51,"address":null,"length":0,"stats":{"Line":3}},{"line":52,"address":null,"length":0,"stats":{"Line":3}},{"line":53,"address":null,"length":0,"stats":{"Line":3}},{"line":54,"address":null,"length":0,"stats":{"Line":3}},{"line":55,"address":null,"length":0,"stats":{"Line":3}},{"line":64,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":1}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":102,"address":4295600,"length":1,"stats":{"Line":2}},{"line":103,"address":4574375,"length":1,"stats":{"Line":1}},{"line":105,"address":4574462,"length":1,"stats":{"Line":1}},{"line":106,"address":4574405,"length":1,"stats":{"Line":1}},{"line":107,"address":4574433,"length":1,"stats":{"Line":1}},{"line":108,"address":4574454,"length":1,"stats":{"Line":1}},{"line":111,"address":4574634,"length":1,"stats":{"Line":1}},{"line":112,"address":4575180,"length":1,"stats":{"Line":1}},{"line":114,"address":4575141,"length":1,"stats":{"Line":1}},{"line":116,"address":4575662,"length":1,"stats":{"Line":1}},{"line":117,"address":4575724,"length":1,"stats":{"Line":1}},{"line":119,"address":4575686,"length":1,"stats":{"Line":1}},{"line":121,"address":4576222,"length":1,"stats":{"Line":1}},{"line":122,"address":4576369,"length":1,"stats":{"Line":0}},{"line":123,"address":4576275,"length":1,"stats":{"Line":1}},{"line":124,"address":4576362,"length":1,"stats":{"Line":1}},{"line":126,"address":4576970,"length":1,"stats":{"Line":1}},{"line":128,"address":4576932,"length":1,"stats":{"Line":1}},{"line":130,"address":4577468,"length":1,"stats":{"Line":1}},{"line":131,"address":4577951,"length":1,"stats":{"Line":1}},{"line":132,"address":4578006,"length":1,"stats":{"Line":1}},{"line":133,"address":4578453,"length":1,"stats":{"Line":1}},{"line":134,"address":4578609,"length":1,"stats":{"Line":1}},{"line":135,"address":4578633,"length":1,"stats":{"Line":1}}],"covered":53,"coverable":55},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","currency-params","src","currencies_codes.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Implements the Duniter Documents Protocol.\n\nuse crate::CurrencyName;\nuse serde::{Deserialize, Serialize};\nuse std::convert::{TryFrom, TryInto};\n\n/// CURRENCY_NULL\nconst CURRENCY_NULL: u16 = 0x_0000;\n/// CURRENCY_G1\nconst CURRENCY_G1: u16 = 0x_0001;\n/// CURRENCY_G1_TEST\nconst CURRENCY_G1_TEST: u16 = 0x_1000;\n\n/// CurrencyCodeError\n#[derive(Debug)]\npub enum CurrencyCodeError {\n /// UnknowCurrencyCode\n UnknowCurrencyCode,\n /// IoError\n IoError(std::io::Error),\n /// UnknowCurrencyName\n UnknowCurrencyName,\n}\n\nimpl From\u003cstd::io::Error\u003e for CurrencyCodeError {\n fn from(error: std::io::Error) -\u003e Self {\n CurrencyCodeError::IoError(error)\n }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)]\n/// Currency code\npub struct CurrencyCode(u16);\n\nimpl TryFrom\u003cCurrencyName\u003e for CurrencyCode {\n type Error = CurrencyCodeError;\n\n fn try_from(currency_name: CurrencyName) -\u003e Result\u003cSelf, Self::Error\u003e {\n match currency_name.0.as_str() {\n \"g1\" =\u003e Ok(CurrencyCode(CURRENCY_G1)),\n \"g1-test\" =\u003e Ok(CurrencyCode(CURRENCY_G1_TEST)),\n _ =\u003e Err(CurrencyCodeError::UnknowCurrencyName),\n }\n }\n}\n\nimpl TryInto\u003cCurrencyName\u003e for CurrencyCode {\n type Error = CurrencyCodeError;\n\n fn try_into(self) -\u003e Result\u003cCurrencyName, Self::Error\u003e {\n match self.0 {\n CURRENCY_NULL =\u003e Ok(CurrencyName(\"\".to_owned())),\n CURRENCY_G1 =\u003e Ok(CurrencyName(\"g1\".to_owned())),\n CURRENCY_G1_TEST =\u003e Ok(CurrencyName(\"g1-test\".to_owned())),\n _ =\u003e Err(CurrencyCodeError::UnknowCurrencyCode),\n }\n }\n}\n","traces":[{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":13},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","currency-params","src","db.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Duniter protocol currency parameters DB\n\nuse crate::constants::CURRENCY_PARAMS_DB_NAME;\nuse crate::genesis_block_params::GenesisBlockParams;\nuse crate::{CurrencyName, CurrencyParameters};\nuse durs_common_tools::fns::bin_file::{read_bin_file, write_bin_file};\nuse failure::Fail;\nuse std::path::PathBuf;\n\n/// Currency parameters db datas\ntype CurrencyParamsDbDatas = Option\u003c(CurrencyName, GenesisBlockParams)\u003e;\n\n/// Currency params Db error\n#[derive(Debug, Fail)]\npub enum CurrencyParamsDbError {\n /// Serialize/Deserialize error\n #[fail(display = \"SerDe error: {}\", _0)]\n SerDe(bincode::Error),\n /// I/O Error\n #[fail(display = \"I/O error: {}\", _0)]\n Io(std::io::Error),\n}\n\n/// Get currency name\npub fn get_currency_name(\n datas_path: PathBuf,\n) -\u003e Result\u003cOption\u003cCurrencyName\u003e, CurrencyParamsDbError\u003e {\n let db_datas: CurrencyParamsDbDatas = read_currency_params_db(datas_path)?;\n\n if let Some((currency_name, _genesis_block_params)) = db_datas {\n Ok(Some(currency_name))\n } else {\n Ok(None)\n }\n}\n\n/// Get currency parameters\npub fn get_currency_params(\n datas_path: PathBuf,\n) -\u003e Result\u003cOption\u003c(CurrencyName, CurrencyParameters)\u003e, CurrencyParamsDbError\u003e {\n let db_datas: CurrencyParamsDbDatas = read_currency_params_db(datas_path)?;\n\n if let Some((currency_name, genesis_block_params)) = db_datas {\n let currency_params = match genesis_block_params {\n GenesisBlockParams::V10(genesis_block_v10_params) =\u003e {\n CurrencyParameters::from((\u0026currency_name, genesis_block_v10_params))\n }\n };\n Ok(Some((currency_name, currency_params)))\n } else {\n Ok(None)\n }\n}\n\nfn read_currency_params_db(\n mut datas_path: PathBuf,\n) -\u003e Result\u003cCurrencyParamsDbDatas, CurrencyParamsDbError\u003e {\n datas_path.push(CURRENCY_PARAMS_DB_NAME);\n\n if !datas_path.exists() {\n return Ok(None);\n }\n\n let bin_vec = read_bin_file(datas_path.as_path()).map_err(CurrencyParamsDbError::Io)?;\n let db_datas: CurrencyParamsDbDatas =\n bincode::deserialize(\u0026bin_vec).map_err(CurrencyParamsDbError::SerDe)?;\n\n Ok(db_datas)\n}\n\n/// Write currency parameters\npub fn write_currency_params(\n mut datas_path: PathBuf,\n currency_name: CurrencyName,\n genesis_block_params: GenesisBlockParams,\n) -\u003e Result\u003c(), CurrencyParamsDbError\u003e {\n datas_path.push(CURRENCY_PARAMS_DB_NAME);\n\n let db_datas: CurrencyParamsDbDatas = Some((currency_name, genesis_block_params));\n\n Ok(write_bin_file(\n datas_path.as_path(),\n \u0026bincode::serialize(\u0026db_datas).map_err(CurrencyParamsDbError::SerDe)?,\n )\n .map_err(CurrencyParamsDbError::Io)?)\n}\n","traces":[{"line":40,"address":4467968,"length":1,"stats":{"Line":0}},{"line":43,"address":4467978,"length":1,"stats":{"Line":0}},{"line":45,"address":4468400,"length":1,"stats":{"Line":0}},{"line":46,"address":4468480,"length":1,"stats":{"Line":0}},{"line":48,"address":4468626,"length":1,"stats":{"Line":0}},{"line":53,"address":4468960,"length":1,"stats":{"Line":0}},{"line":56,"address":4468970,"length":1,"stats":{"Line":0}},{"line":58,"address":4469455,"length":1,"stats":{"Line":0}},{"line":60,"address":4469551,"length":1,"stats":{"Line":0}},{"line":61,"address":4469590,"length":1,"stats":{"Line":0}},{"line":64,"address":4469695,"length":1,"stats":{"Line":0}},{"line":66,"address":4469991,"length":1,"stats":{"Line":0}},{"line":70,"address":4470400,"length":1,"stats":{"Line":0}},{"line":73,"address":4470410,"length":1,"stats":{"Line":0}},{"line":75,"address":4470474,"length":1,"stats":{"Line":0}},{"line":76,"address":4470573,"length":1,"stats":{"Line":0}},{"line":79,"address":4470669,"length":1,"stats":{"Line":0}},{"line":81,"address":4471106,"length":1,"stats":{"Line":0}},{"line":83,"address":4471674,"length":1,"stats":{"Line":0}},{"line":87,"address":4471952,"length":1,"stats":{"Line":0}},{"line":92,"address":4471962,"length":1,"stats":{"Line":0}},{"line":94,"address":4472086,"length":1,"stats":{"Line":0}},{"line":96,"address":4472866,"length":1,"stats":{"Line":0}},{"line":97,"address":4472294,"length":1,"stats":{"Line":0}},{"line":98,"address":4472382,"length":1,"stats":{"Line":0}},{"line":100,"address":4472959,"length":1,"stats":{"Line":0}}],"covered":0,"coverable":26},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","currency-params","src","genesis_block_params","v10.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Duniter protocol currency parameters in genesis block v10\n\nuse crate::genesis_block_params::ParseParamsError;\nuse serde::{Deserialize, Serialize};\n\n/// Currency parameters\n#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq)]\npub struct BlockV10Parameters {\n /// UD target growth rate (see Relative Theorie of Money)\n pub c: f64,\n /// Duration between the creation of two UD (in seconds)\n pub dt: u64,\n /// Amount of the initial UD\n pub ud0: usize,\n /// Minimum duration between the writing of 2 certifications from the same issuer (in seconds)\n pub sig_period: u64,\n /// Maximum number of active certifications at the same time (for the same issuer)\n pub sig_stock: usize,\n /// Maximum retention period of a pending certification\n pub sig_window: u64,\n /// Time to expiry of written certification\n pub sig_validity: u64,\n /// Minimum number of certifications required to become a member\n pub sig_qty: usize,\n /// Maximum retention period of a pending identity\n pub idty_window: u64,\n /// Maximum retention period of a pending membership\n pub ms_window: u64,\n /// Percentage of referring members who must be within step_max steps of each member\n pub x_percent: f64,\n /// Time to expiry of written membership\n pub ms_validity: u64,\n /// For a member to respect the distance rule,\n /// there must exist for more than x_percent % of the referring members\n /// a path of less than step_max steps from the referring member to the evaluated member.\n pub step_max: usize,\n /// Number of blocks used for calculating median time.\n pub median_time_blocks: usize,\n /// The average time for writing 1 block (wished time)\n pub avg_gen_time: u64,\n /// The number of blocks required to evaluate again PoWMin value\n pub dt_diff_eval: usize,\n /// The percent of previous issuers to reach for personalized difficulty\n pub percent_rot: f64,\n /// Time of first UD.\n pub ud_time0: u64,\n /// Time of first reevaluation of the UD.\n pub ud_reeval_time0: u64,\n /// Time period between two re-evaluation of the UD.\n pub dt_reeval: u64,\n}\n\nimpl Default for BlockV10Parameters {\n fn default() -\u003e BlockV10Parameters {\n BlockV10Parameters {\n c: 0.0488,\n dt: 86_400,\n ud0: 1_000,\n sig_period: 432_000,\n sig_stock: 100,\n sig_window: 5_259_600,\n sig_validity: 63_115_200,\n sig_qty: 5,\n idty_window: 5_259_600,\n ms_window: 5_259_600,\n x_percent: 0.8,\n ms_validity: 31_557_600,\n step_max: 5,\n median_time_blocks: 24,\n avg_gen_time: 300,\n dt_diff_eval: 12,\n percent_rot: 0.67,\n ud_time0: 1_488_970_800,\n ud_reeval_time0: 1_490_094_000,\n dt_reeval: 15_778_800,\n }\n }\n}\n\nimpl ::std::str::FromStr for BlockV10Parameters {\n type Err = ParseParamsError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n let params: Vec\u003c\u0026str\u003e = source.split(':').collect();\n Ok(BlockV10Parameters {\n c: params[0].parse()?,\n dt: params[1].parse()?,\n ud0: params[2].parse()?,\n sig_period: params[3].parse()?,\n sig_stock: params[4].parse()?,\n sig_window: params[5].parse()?,\n sig_validity: params[6].parse()?,\n sig_qty: params[7].parse()?,\n idty_window: params[8].parse()?,\n ms_window: params[9].parse()?,\n x_percent: params[10].parse()?,\n ms_validity: params[11].parse()?,\n step_max: params[12].parse()?,\n median_time_blocks: params[13].parse()?,\n avg_gen_time: params[14].parse()?,\n dt_diff_eval: params[15].parse()?,\n percent_rot: params[16].parse()?,\n ud_time0: params[17].parse()?,\n ud_reeval_time0: params[18].parse()?,\n dt_reeval: params[19].parse()?,\n })\n }\n}\n\nimpl ToString for BlockV10Parameters {\n fn to_string(\u0026self) -\u003e String {\n format!(\n \"{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}:{}\",\n self.c,\n self.dt,\n self.ud0,\n self.sig_period,\n self.sig_stock,\n self.sig_window,\n self.sig_validity,\n self.sig_qty,\n self.idty_window,\n self.ms_window,\n self.x_percent,\n self.ms_validity,\n self.step_max,\n self.median_time_blocks,\n self.avg_gen_time,\n self.dt_diff_eval,\n self.percent_rot,\n self.ud_time0,\n self.ud_reeval_time0,\n self.dt_reeval,\n )\n }\n}\n\nimpl Eq for BlockV10Parameters {}\n","traces":[{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":46},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","currency-params","src","genesis_block_params.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Duniter protocol currency parameters in genesis block\n\npub mod v10;\n\nuse failure::Fail;\nuse serde::{Deserialize, Serialize};\nuse v10::BlockV10Parameters;\n\n#[derive(Copy, Clone, Debug, Deserialize, Serialize)]\n/// Currency parameters in genesis block\npub enum GenesisBlockParams {\n /// Currency parameters in genesis block v10\n V10(BlockV10Parameters),\n}\n\n#[derive(Debug, Clone, Fail)]\n/// Store error in block parameters parsing\npub enum ParseParamsError {\n /// ParseIntError\n #[fail(display = \"Fail to parse params :ParseIntError !\")]\n ParseIntError(::std::num::ParseIntError),\n /// ParseFloatError\n #[fail(display = \"Fail to parse params :ParseFloatError !\")]\n ParseFloatError(::std::num::ParseFloatError),\n}\n\nimpl From\u003c::std::num::ParseIntError\u003e for ParseParamsError {\n fn from(err: ::std::num::ParseIntError) -\u003e ParseParamsError {\n ParseParamsError::ParseIntError(err)\n }\n}\n\nimpl From\u003c::std::num::ParseFloatError\u003e for ParseParamsError {\n fn from(err: ::std::num::ParseFloatError) -\u003e ParseParamsError {\n ParseParamsError::ParseFloatError(err)\n }\n}\n","traces":[{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":4},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","currency-params","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Duniter protocol currency parameters\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces\n)]\n\npub mod constants;\npub mod currencies_codes;\npub mod db;\npub mod genesis_block_params;\n\nuse crate::constants::*;\nuse genesis_block_params::v10::BlockV10Parameters;\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{Display, Error, Formatter};\n\n/// Currency name\n#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)]\npub struct CurrencyName(pub String);\n\nimpl Display for CurrencyName {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.0)\n }\n}\n\nimpl From\u003c\u0026str\u003e for CurrencyName {\n fn from(s: \u0026str) -\u003e Self {\n CurrencyName(s.to_owned())\n }\n}\n\n#[derive(Debug, Copy, Clone)]\n/// Currency parameters\npub struct CurrencyParameters {\n /// Protocol version\n pub protocol_version: usize,\n /// UD target growth rate (see Relative Theorie of Money)\n pub c: f64,\n /// Duration between the creation of two UD (in seconds)\n pub dt: u64,\n /// Amount of the initial UD\n pub ud0: usize,\n /// Minimum duration between the writing of 2 certifications from the same issuer (in seconds)\n pub sig_period: u64,\n /// Minimum duration between two renewals of the same certification\n pub sig_renew_period: u64,\n /// Maximum number of active certifications at the same time (for the same issuer)\n pub sig_stock: usize,\n /// Maximum retention period of a pending certification\n pub sig_window: u64,\n /// Time to expiry of written certification\n pub sig_validity: u64,\n /// Minimum number of certifications required to become a member\n pub sig_qty: usize,\n /// Maximum retention period of a pending identity\n pub idty_window: u64,\n /// Maximum retention period of a pending membership\n pub ms_window: u64,\n /// Maximum retention period of a pending transaction\n pub tx_window: u64,\n /// Percentage of referring members who must be within step_max steps of each member\n pub x_percent: f64,\n /// Time to expiry of written membership\n pub ms_validity: u64,\n /// Minimum duration between the writing of 2 memberships from the same issuer (in seconds)\n pub ms_period: u64,\n /// For a member to respect the distance rule,\n /// there must exist for more than x_percent % of the referring members\n /// a path of less than step_max steps from the referring member to the evaluated member.\n pub step_max: usize,\n /// Number of blocks used for calculating median time.\n pub median_time_blocks: usize,\n /// The average time for writing 1 block (wished time)\n pub avg_gen_time: u64,\n /// The number of blocks required to evaluate again PoWMin value\n pub dt_diff_eval: usize,\n /// The percent of previous issuers to reach for personalized difficulty\n pub percent_rot: f64,\n /// Time of first UD.\n pub ud_time0: u64,\n /// Time of first reevaluation of the UD.\n pub ud_reeval_time0: u64,\n /// Time period between two re-evaluation of the UD.\n pub dt_reeval: u64,\n /// Maximum roolback length\n pub fork_window_size: usize,\n}\n\nimpl From\u003c(\u0026CurrencyName, BlockV10Parameters)\u003e for CurrencyParameters {\n fn from(source: (\u0026CurrencyName, BlockV10Parameters)) -\u003e CurrencyParameters {\n let (currency_name, block_params) = source;\n let sig_renew_period = match currency_name.0.as_str() {\n DEFAULT_CURRENCY =\u003e *DEFAULT_SIG_RENEW_PERIOD,\n \"g1\" =\u003e 5_259_600,\n \"g1-test\" =\u003e 5_259_600 / 5,\n _ =\u003e *DEFAULT_SIG_RENEW_PERIOD,\n };\n let ms_period = match currency_name.0.as_str() {\n DEFAULT_CURRENCY =\u003e *DEFAULT_MS_PERIOD,\n \"g1\" =\u003e 5_259_600,\n \"g1-test\" =\u003e 5_259_600 / 5,\n _ =\u003e *DEFAULT_MS_PERIOD,\n };\n let tx_window = match currency_name.0.as_str() {\n DEFAULT_CURRENCY =\u003e *DEFAULT_TX_WINDOW,\n \"g1\" =\u003e 604_800,\n \"g1-test\" =\u003e 604_800,\n _ =\u003e *DEFAULT_TX_WINDOW,\n };\n let fork_window_size = match currency_name.0.as_str() {\n DEFAULT_CURRENCY =\u003e *DEFAULT_FORK_WINDOW_SIZE,\n \"g1\" =\u003e 200,\n \"g1-test\" =\u003e 200,\n _ =\u003e *DEFAULT_FORK_WINDOW_SIZE,\n };\n CurrencyParameters {\n protocol_version: 10,\n c: block_params.c,\n dt: block_params.dt,\n ud0: block_params.ud0,\n sig_period: block_params.sig_period,\n sig_renew_period,\n sig_stock: block_params.sig_stock,\n sig_window: block_params.sig_window,\n sig_validity: block_params.sig_validity,\n sig_qty: block_params.sig_qty,\n idty_window: block_params.idty_window,\n ms_window: block_params.ms_window,\n tx_window,\n x_percent: block_params.x_percent,\n ms_validity: block_params.ms_validity,\n ms_period,\n step_max: block_params.step_max,\n median_time_blocks: block_params.median_time_blocks,\n avg_gen_time: block_params.avg_gen_time,\n dt_diff_eval: block_params.dt_diff_eval,\n percent_rot: block_params.percent_rot,\n ud_time0: block_params.ud_time0,\n ud_reeval_time0: block_params.ud_reeval_time0,\n dt_reeval: block_params.dt_reeval,\n fork_window_size,\n }\n }\n}\n\nimpl CurrencyParameters {\n /// Get max value of connectivity (=1/x_percent)\n pub fn max_connectivity(\u0026self) -\u003e f64 {\n 1.0 / self.x_percent\n }\n}\n","traces":[{"line":44,"address":null,"length":0,"stats":{"Line":2}},{"line":45,"address":null,"length":0,"stats":{"Line":2}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}}],"covered":2,"coverable":48},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","blockstamp.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrapper for blockstamp\n\nuse crate::*;\n\n/// Type of errors for [`BlockUId`] parsing.\n///\n/// [`BlockUId`]: struct.BlockUId.html\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Fail)]\npub enum BlockstampParseError {\n /// Given string have invalid format\n #[fail(display = \"Given string have invalid format\")]\n InvalidFormat(),\n /// [`BlockNumber`](struct.BlockHash.html) part is not a valid number.\n #[fail(display = \"BlockNumber part is not a valid number.\")]\n InvalidBlockNumber(),\n /// [`BlockHash`](struct.BlockHash.html) part is not a valid hex number.\n #[fail(display = \"BlockHash part is not a valid hex number.\")]\n InvalidBlockHash(),\n}\n\n/// A blockstamp (Unique ID).\n///\n/// It's composed of the [`BlockNumber`] and\n/// the [`BlockHash`] of the block.\n///\n/// Thanks to blockchain immutability and frequent block production, it can\n/// be used to date information.\n///\n/// [`BlockNumber`]: struct.BlockNumber.html\n/// [`BlockHash`]: struct.BlockHash.html\n\n#[derive(Copy, Clone, Deserialize, PartialEq, Eq, Hash, Serialize)]\npub struct Blockstamp {\n /// Block Id.\n pub id: BlockNumber,\n /// Block hash.\n pub hash: BlockHash,\n}\n\n/// Previous blockstamp (BlockNumber-1, previous_hash)\npub type PreviousBlockstamp = Blockstamp;\n\nimpl Blockstamp {\n /// Blockstamp size (in bytes).\n pub const SIZE_IN_BYTES: usize = 36;\n}\n\nimpl Display for Blockstamp {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}-{}\", self.id, self.hash)\n }\n}\n\nimpl Debug for Blockstamp {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"BlockUId({})\", self)\n }\n}\n\nimpl Default for Blockstamp {\n fn default() -\u003e Blockstamp {\n Blockstamp {\n id: BlockNumber(0),\n hash: BlockHash(Hash::default()),\n }\n }\n}\n\nimpl PartialOrd for Blockstamp {\n fn partial_cmp(\u0026self, other: \u0026Blockstamp) -\u003e Option\u003cOrdering\u003e {\n Some(self.cmp(other))\n }\n}\n\nimpl Ord for Blockstamp {\n fn cmp(\u0026self, other: \u0026Blockstamp) -\u003e Ordering {\n if self.id == other.id {\n self.hash.cmp(\u0026other.hash)\n } else {\n self.id.cmp(\u0026other.id)\n }\n }\n}\n\n#[derive(Debug)]\n/// Error when converting a byte vector to Blockstamp\npub enum ReadBytesBlockstampError {\n /// Bytes vector is too short\n TooShort(),\n /// Bytes vector is too long\n TooLong(),\n /// IoError\n IoError(::std::io::Error),\n}\n\nimpl From\u003c::std::io::Error\u003e for ReadBytesBlockstampError {\n fn from(e: ::std::io::Error) -\u003e Self {\n ReadBytesBlockstampError::IoError(e)\n }\n}\n\nimpl Blockstamp {\n /// Create a `BlockUId` from a text.\n pub fn from_string(src: \u0026str) -\u003e Result\u003cBlockstamp, BlockstampParseError\u003e {\n let mut split = src.split('-');\n\n if split.clone().count() != 2 {\n Err(BlockstampParseError::InvalidFormat())\n } else {\n let id = split.next().unwrap().parse::\u003cu32\u003e();\n let hash = Hash::from_hex(split.next().unwrap());\n\n if id.is_err() {\n Err(BlockstampParseError::InvalidBlockNumber())\n } else if hash.is_err() {\n Err(BlockstampParseError::InvalidBlockHash())\n } else {\n Ok(Blockstamp {\n id: BlockNumber(id.unwrap()),\n hash: BlockHash(\n hash.expect(\"Try to get hash of an uncompleted or reduce block !\"),\n ),\n })\n }\n }\n }\n\n /// Convert a `BlockUId` to its text format.\n pub fn to_string(\u0026self) -\u003e String {\n format!(\"{}\", self)\n }\n}\n","traces":[{"line":64,"address":null,"length":0,"stats":{"Line":4}},{"line":65,"address":null,"length":0,"stats":{"Line":4}},{"line":70,"address":null,"length":0,"stats":{"Line":2}},{"line":71,"address":null,"length":0,"stats":{"Line":2}},{"line":76,"address":null,"length":0,"stats":{"Line":2}},{"line":78,"address":null,"length":0,"stats":{"Line":2}},{"line":79,"address":null,"length":0,"stats":{"Line":2}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":5}},{"line":120,"address":null,"length":0,"stats":{"Line":5}},{"line":122,"address":null,"length":0,"stats":{"Line":5}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":5}},{"line":126,"address":null,"length":0,"stats":{"Line":5}},{"line":128,"address":null,"length":0,"stats":{"Line":5}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":5}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":5}},{"line":134,"address":null,"length":0,"stats":{"Line":5}},{"line":135,"address":null,"length":0,"stats":{"Line":5}},{"line":136,"address":null,"length":0,"stats":{"Line":5}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}}],"covered":20,"coverable":34},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","block","v10.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Block document V10.\n\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\nuse dup_currency_params::genesis_block_params::v10::BlockV10Parameters;\nuse dup_currency_params::CurrencyName;\nuse durs_common_tools::fatal_error;\nuse std::ops::Deref;\nuse unwrap::unwrap;\n\nuse super::{BlockDocumentTrait, VerifyBlockHashError};\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::certification::v10::CompactCertificationDocumentV10Stringified;\nuse crate::documents::identity::IdentityDocumentV10;\nuse crate::documents::membership::v10::{MembershipDocumentV10, MembershipDocumentV10Stringified};\nuse crate::documents::revocation::v10::CompactRevocationDocumentV10Stringified;\nuse crate::documents::revocation::RevocationDocumentV10;\nuse crate::documents::transaction::TransactionDocument;\nuse crate::documents::*;\nuse crate::text_document_traits::*;\n\n/// Store a transaction document or just its hash.\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub enum TxDocOrTxHash {\n /// Transaction document\n TxDoc(Box\u003cTransactionDocument\u003e),\n /// transaction hash\n TxHash(Hash),\n}\n\nimpl TxDocOrTxHash {\n /// Lightens the TxDocOrTxHash (for example to store it while minimizing the space required)\n /// lightening consists in transforming the document by its hash.\n pub fn reduce(\u0026self) -\u003e TxDocOrTxHash {\n if let TxDocOrTxHash::TxDoc(ref tx_doc) = self {\n let tx_doc = tx_doc.deref();\n if let Some(ref hash) = tx_doc.get_hash_opt() {\n TxDocOrTxHash::TxHash(*hash)\n } else {\n TxDocOrTxHash::TxHash(tx_doc.clone().compute_hash())\n }\n } else {\n self.clone()\n }\n }\n /// Get TxDoc variant\n pub fn unwrap_doc(\u0026self) -\u003e TransactionDocument {\n if let TxDocOrTxHash::TxDoc(ref tx_doc) = self {\n tx_doc.deref().clone()\n } else {\n fatal_error!(\"Try to unwrap_doc() in a TxHash() variant of TxDocOrTxHash !\")\n }\n }\n}\n\n/// Wrap a Block document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]\npub struct BlockDocumentV10 {\n /// Version\n pub version: u32,\n /// Nonce\n pub nonce: u64,\n /// number\n pub number: BlockNumber,\n /// Minimal proof of work difficulty\n pub pow_min: usize,\n /// Local time of the block issuer\n pub time: u64,\n /// Average time\n pub median_time: u64,\n /// Members count\n pub members_count: usize,\n /// Monetary mass\n pub monetary_mass: usize,\n /// Unit base (power of ten)\n pub unit_base: usize,\n /// Number of compute members in the current frame\n pub issuers_count: usize,\n /// Current frame size (in blocks)\n pub issuers_frame: usize,\n /// Current frame variation buffer\n pub issuers_frame_var: isize,\n /// Currency.\n pub currency: CurrencyName,\n /// Document issuer (there should be only one).\n pub issuers: Vec\u003cPubKey\u003e,\n /// Document signature (there should be only one).\n /// This vector is empty, when the block is generated but the proof of work has not yet started\n pub signatures: Vec\u003cSig\u003e,\n /// The hash is None, when the block is generated but the proof of work has not yet started\n pub hash: Option\u003cBlockHash\u003e,\n /// Currency parameters (only in genesis block)\n pub parameters: Option\u003cBlockV10Parameters\u003e,\n /// Hash of the previous block\n pub previous_hash: Option\u003cHash\u003e,\n /// Issuer of the previous block\n pub previous_issuer: Option\u003cPubKey\u003e,\n /// Hash of the deterministic content of the block\n pub inner_hash: Option\u003cHash\u003e,\n /// Amount of new dividend created at this block, None if no dividend is created at this block\n pub dividend: Option\u003cusize\u003e,\n /// Identities\n pub identities: Vec\u003cIdentityDocumentV10\u003e,\n /// joiners\n pub joiners: Vec\u003cMembershipDocumentV10\u003e,\n /// Actives (=renewals)\n pub actives: Vec\u003cMembershipDocumentV10\u003e,\n /// Leavers\n pub leavers: Vec\u003cMembershipDocumentV10\u003e,\n /// Revokeds\n pub revoked: Vec\u003cTextDocumentFormat\u003cRevocationDocumentV10\u003e\u003e,\n /// Excludeds\n pub excluded: Vec\u003cPubKey\u003e,\n /// Certifications\n pub certifications: Vec\u003cTextDocumentFormat\u003cCertificationDocumentV10\u003e\u003e,\n /// Transactions\n pub transactions: Vec\u003cTxDocOrTxHash\u003e,\n}\n\nimpl BlockDocumentTrait for BlockDocumentV10 {\n fn common_time(\u0026self) -\u003e u64 {\n self.median_time\n }\n fn compute_hash(\u0026self) -\u003e BlockHash {\n BlockHash(Hash::compute_str(\u0026self.compute_will_hashed_string()))\n }\n fn compute_will_hashed_string(\u0026self) -\u003e String {\n format!(\n \"{}{}\\n\",\n self.compute_will_signed_string(),\n self.signatures[0]\n )\n }\n fn compute_will_signed_string(\u0026self) -\u003e String {\n format!(\n \"InnerHash: {}\\nNonce: {}\\n\",\n self.inner_hash\n .expect(\"compute_will_signed_string(): Try to get inner_hash of an uncompleted or reduce block !\")\n .to_hex(),\n self.nonce\n )\n }\n fn current_frame_size(\u0026self) -\u003e usize {\n self.issuers_frame\n }\n fn generate_compact_inner_text(\u0026self) -\u003e String {\n let mut identities_str = String::from(\"\");\n for identity in self.identities.clone() {\n identities_str.push_str(\"\\n\");\n identities_str.push_str(\u0026identity.generate_compact_text());\n }\n let mut joiners_str = String::from(\"\");\n for joiner in self.joiners.clone() {\n joiners_str.push_str(\"\\n\");\n joiners_str.push_str(\u0026joiner.generate_compact_text());\n }\n let mut actives_str = String::from(\"\");\n for active in self.actives.clone() {\n actives_str.push_str(\"\\n\");\n actives_str.push_str(\u0026active.generate_compact_text());\n }\n let mut leavers_str = String::from(\"\");\n for leaver in self.leavers.clone() {\n leavers_str.push_str(\"\\n\");\n leavers_str.push_str(\u0026leaver.generate_compact_text());\n }\n let mut identities_str = String::from(\"\");\n for identity in self.identities.clone() {\n identities_str.push_str(\"\\n\");\n identities_str.push_str(\u0026identity.generate_compact_text());\n }\n let mut revokeds_str = String::from(\"\");\n for revocation in self.revoked.clone() {\n revokeds_str.push_str(\"\\n\");\n revokeds_str.push_str(\u0026revocation.as_compact_text());\n }\n let mut excludeds_str = String::from(\"\");\n for exclusion in self.excluded.clone() {\n excludeds_str.push_str(\"\\n\");\n excludeds_str.push_str(\u0026exclusion.to_string());\n }\n let mut certifications_str = String::from(\"\");\n for certification in self.certifications.clone() {\n certifications_str.push_str(\"\\n\");\n certifications_str.push_str(\u0026certification.as_compact_text());\n }\n let mut transactions_str = String::from(\"\");\n for transaction in self.transactions.clone() {\n if let TxDocOrTxHash::TxDoc(transaction) = transaction {\n transactions_str.push_str(\"\\n\");\n transactions_str.push_str(\u0026transaction.deref().generate_compact_text());\n }\n }\n let mut dividend_str = String::from(\"\");\n if let Some(dividend) = self.dividend {\n if dividend \u003e 0 {\n dividend_str.push_str(\"UniversalDividend: \");\n dividend_str.push_str(\u0026dividend.to_string());\n dividend_str.push_str(\"\\n\");\n }\n }\n let mut parameters_str = String::from(\"\");\n if let Some(params) = self.parameters {\n parameters_str.push_str(\"Parameters: \");\n parameters_str.push_str(\u0026params.to_string());\n parameters_str.push_str(\"\\n\");\n }\n let mut previous_hash_str = String::from(\"\");\n if self.number.0 \u003e 0 {\n previous_hash_str.push_str(\"PreviousHash: \");\n previous_hash_str.push_str(\u0026unwrap!(self.previous_hash).to_string());\n previous_hash_str.push_str(\"\\n\");\n }\n let mut previous_issuer_str = String::from(\"\");\n if self.number.0 \u003e 0 {\n previous_issuer_str.push_str(\"PreviousIssuer: \");\n previous_issuer_str.push_str(\n \u0026self\n .previous_issuer\n .expect(\"No genesis block must have previous issuer\")\n .to_string(),\n );\n previous_issuer_str.push_str(\"\\n\");\n }\n format!(\n \"Version: {version}\nType: Block\nCurrency: {currency}\nNumber: {block_number}\nPoWMin: {pow_min}\nTime: {time}\nMedianTime: {median_time}\n{dividend}UnitBase: {unit_base}\nIssuer: {issuer}\nIssuersFrame: {issuers_frame}\nIssuersFrameVar: {issuers_frame_var}\nDifferentIssuersCount: {issuers_count}\n{parameters}{previous_hash}{previous_issuer}MembersCount: {members_count}\nIdentities:{identities}\nJoiners:{joiners}\nActives:{actives}\nLeavers:{leavers}\nRevoked:{revoked}\nExcluded:{excluded}\nCertifications:{certifications}\nTransactions:{transactions}\n\",\n version = self.version,\n currency = self.currency,\n block_number = self.number,\n pow_min = self.pow_min,\n time = self.time,\n median_time = self.median_time,\n dividend = dividend_str,\n unit_base = self.unit_base,\n issuer = self.issuers[0],\n issuers_frame = self.issuers_frame,\n issuers_frame_var = self.issuers_frame_var,\n issuers_count = self.issuers_count,\n parameters = parameters_str,\n previous_hash = previous_hash_str,\n previous_issuer = previous_issuer_str,\n members_count = self.members_count,\n identities = identities_str,\n joiners = joiners_str,\n actives = actives_str,\n leavers = leavers_str,\n revoked = revokeds_str,\n excluded = excludeds_str,\n certifications = certifications_str,\n transactions = transactions_str,\n )\n }\n fn generate_hash(\u0026mut self) {\n self.hash = Some(self.compute_hash());\n }\n fn generate_inner_hash(\u0026mut self) {\n self.inner_hash = Some(self.compute_inner_hash());\n }\n fn hash(\u0026self) -\u003e Option\u003cBlockHash\u003e {\n self.hash\n }\n fn increment_nonce(\u0026mut self) {\n self.nonce += 1;\n }\n fn inner_hash(\u0026self) -\u003e Option\u003cHash\u003e {\n self.inner_hash\n }\n fn issuers_count(\u0026self) -\u003e usize {\n self.issuers_count\n }\n fn number(\u0026self) -\u003e BlockNumber {\n self.number\n }\n fn previous_blockstamp(\u0026self) -\u003e Blockstamp {\n if self.number.0 \u003e 0 {\n Blockstamp {\n id: BlockNumber(self.number.0 - 1),\n hash: BlockHash(unwrap!(self.previous_hash)),\n }\n } else {\n Blockstamp::default()\n }\n }\n fn previous_hash(\u0026self) -\u003e Option\u003cHash\u003e {\n self.previous_hash\n }\n fn reduce(\u0026mut self) {\n //self.hash = None;\n self.inner_hash = None;\n for i in \u0026mut self.identities {\n i.reduce();\n }\n for i in \u0026mut self.joiners {\n i.reduce();\n }\n for i in \u0026mut self.actives {\n i.reduce();\n }\n for i in \u0026mut self.leavers {\n i.reduce();\n }\n for i in \u0026mut self.transactions {\n i.reduce();\n }\n }\n fn sign(\u0026mut self, privkey: PrivKey) {\n self.signatures = vec![privkey.sign(self.compute_will_signed_string().as_bytes())];\n }\n fn verify_inner_hash(\u0026self) -\u003e Result\u003c(), VerifyBlockHashError\u003e {\n match self.inner_hash {\n Some(inner_hash) =\u003e {\n let computed_hash = self.compute_inner_hash();\n if inner_hash == computed_hash {\n Ok(())\n } else {\n Err(VerifyBlockHashError::InvalidHash {\n block_number: self.number,\n expected_hash: computed_hash,\n actual_hash: inner_hash,\n })\n }\n }\n None =\u003e Err(VerifyBlockHashError::MissingHash {\n block_number: self.number,\n }),\n }\n }\n fn verify_hash(\u0026self) -\u003e Result\u003c(), VerifyBlockHashError\u003e {\n match self.hash {\n Some(actual_hash) =\u003e {\n let expected_hash = self.compute_hash();\n if actual_hash == expected_hash {\n Ok(())\n } else {\n warn!(\n \"Block #{} have invalid hash (expected='{}', actual='{}', datas='{}').\",\n self.number.0,\n expected_hash,\n actual_hash,\n self.compute_will_hashed_string()\n );\n Err(VerifyBlockHashError::InvalidHash {\n block_number: self.number,\n expected_hash: expected_hash.0,\n actual_hash: actual_hash.0,\n })\n }\n }\n None =\u003e Err(VerifyBlockHashError::MissingHash {\n block_number: self.number,\n }),\n }\n }\n}\n\nimpl Document for BlockDocumentV10 {\n type PublicKey = PubKey;\n\n #[inline]\n fn version(\u0026self) -\u003e u16 {\n 10\n }\n\n #[inline]\n fn currency(\u0026self) -\u003e \u0026str {\n \u0026self.currency.0\n }\n\n #[inline]\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n Blockstamp {\n id: self.number,\n hash: self\n .hash\n .expect(\"Fatal error : try to get blockstamp of an uncomplete or reduce block !\"),\n }\n }\n\n #[inline]\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n #[inline]\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n #[inline]\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n fatal_error!(\"as_bytes() must not be call for BlockDocumentV10 !\")\n }\n\n #[inline]\n fn no_as_bytes(\u0026self) -\u003e bool {\n true\n }\n\n #[inline]\n fn to_bytes(\u0026self) -\u003e Vec\u003cu8\u003e {\n self.compute_will_signed_string().as_bytes().to_vec()\n }\n}\n\nimpl CompactTextDocument for BlockDocumentV10 {\n fn as_compact_text(\u0026self) -\u003e String {\n let compact_inner_text = self.generate_compact_inner_text();\n format!(\n \"{}InnerHash: {}\\nNonce: \",\n compact_inner_text,\n self.inner_hash\n .expect(\n \"as_compact_text(): Try to get inner_hash of an uncompleted or reduce block !\"\n )\n .to_hex()\n )\n }\n}\n\nimpl TextDocument for BlockDocumentV10 {\n type CompactTextDocument_ = BlockDocumentV10;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n fatal_error!(\n \"Dev error: function not implemented. Please use to_compact_document() instead\"\n );\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n self.clone()\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub struct BlockDocumentV10Stringified {\n /// Version\n pub version: u64,\n /// Nonce\n pub nonce: u64,\n /// number\n pub number: u64,\n /// Minimal proof of work difficulty\n pub pow_min: u64,\n /// Local time of the block issuer\n pub time: u64,\n /// Average time\n pub median_time: u64,\n /// Members count\n pub members_count: u64,\n /// Monetary mass\n pub monetary_mass: u64,\n /// Unit base (power of ten)\n pub unit_base: u64,\n /// Number of compute members in the current frame\n pub issuers_count: u64,\n /// Current frame size (in blocks)\n pub issuers_frame: i64,\n /// Current frame variation buffer\n pub issuers_frame_var: i64,\n /// Currency.\n pub currency: String,\n /// Document issuer (there should be only one).\n pub issuers: Vec\u003cString\u003e,\n /// Document signature (there should be only one).\n /// This vector is empty, when the block is generated but the proof of work has not yet started\n pub signatures: Vec\u003cString\u003e,\n /// The hash is None, when the block is generated but the proof of work has not yet started\n pub hash: Option\u003cString\u003e,\n /// Currency parameters (only in genesis block)\n pub parameters: Option\u003cString\u003e,\n /// Hash of the previous block\n pub previous_hash: Option\u003cString\u003e,\n /// Issuer of the previous block\n pub previous_issuer: Option\u003cString\u003e,\n /// Hash of the deterministic content of the block\n pub inner_hash: Option\u003cString\u003e,\n /// Amount of new dividend created at this block, None if no dividend is created at this block\n pub dividend: Option\u003cu64\u003e,\n /// Identities\n pub identities: Vec\u003cIdentityDocumentV10Stringified\u003e,\n /// joiners\n pub joiners: Vec\u003cMembershipDocumentV10Stringified\u003e,\n /// Actives (=renewals)\n pub actives: Vec\u003cMembershipDocumentV10Stringified\u003e,\n /// Leavers\n pub leavers: Vec\u003cMembershipDocumentV10Stringified\u003e,\n /// Revokeds\n pub revoked: Vec\u003cCompactRevocationDocumentV10Stringified\u003e,\n /// Excludeds\n pub excluded: Vec\u003cString\u003e,\n /// Certifications\n pub certifications: Vec\u003cCompactCertificationDocumentV10Stringified\u003e,\n /// Transactions\n pub transactions: Vec\u003cTransactionDocumentStringified\u003e,\n}\n\nimpl ToStringObject for BlockDocumentV10 {\n type StringObject = BlockDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e BlockDocumentV10Stringified {\n BlockDocumentV10Stringified {\n version: u64::from(self.version),\n nonce: self.nonce,\n number: u64::from(self.number.0),\n pow_min: self.pow_min as u64,\n time: self.time,\n median_time: self.median_time,\n members_count: self.members_count as u64,\n monetary_mass: self.monetary_mass as u64,\n unit_base: self.unit_base as u64,\n issuers_count: self.issuers_count as u64,\n issuers_frame: self.issuers_frame as i64,\n issuers_frame_var: self.issuers_frame_var as i64,\n currency: self.currency.to_string(),\n issuers: self.issuers.iter().map(ToString::to_string).collect(),\n signatures: self.signatures.iter().map(ToString::to_string).collect(),\n hash: self.hash.map(|hash| hash.to_string()),\n parameters: self.parameters.map(|parameters| parameters.to_string()),\n previous_hash: self.previous_hash.map(|hash| hash.to_string()),\n previous_issuer: self.previous_issuer.map(|p| p.to_string()),\n inner_hash: self.inner_hash.map(|hash| hash.to_string()),\n dividend: self.dividend.map(|dividend| dividend as u64),\n identities: self\n .identities\n .iter()\n .map(ToStringObject::to_string_object)\n .collect(),\n joiners: self\n .joiners\n .iter()\n .map(ToStringObject::to_string_object)\n .collect(),\n actives: self\n .actives\n .iter()\n .map(ToStringObject::to_string_object)\n .collect(),\n leavers: self\n .leavers\n .iter()\n .map(ToStringObject::to_string_object)\n .collect(),\n revoked: self\n .revoked\n .iter()\n .map(|revocation_doc| match revocation_doc {\n TextDocumentFormat::Complete(complete_revoc_doc) =\u003e {\n complete_revoc_doc.to_compact_document().to_string_object()\n }\n TextDocumentFormat::Compact(compact_revoc_doc) =\u003e {\n compact_revoc_doc.to_string_object()\n }\n })\n .collect(),\n excluded: self.excluded.iter().map(ToString::to_string).collect(),\n certifications: self\n .certifications\n .iter()\n .map(|cert_doc| match cert_doc {\n TextDocumentFormat::Complete(complete_cert_doc) =\u003e {\n complete_cert_doc.to_compact_document().to_string_object()\n }\n TextDocumentFormat::Compact(compact_cert_doc) =\u003e {\n compact_cert_doc.to_string_object()\n }\n })\n .collect(),\n transactions: self\n .transactions\n .iter()\n .map(|tx_doc_or_tx_hash| match tx_doc_or_tx_hash {\n TxDocOrTxHash::TxDoc(tx_doc) =\u003e tx_doc.to_string_object(),\n TxDocOrTxHash::TxHash(_) =\u003e {\n fatal_error!(\"Try to stringify block without their tx documents\")\n }\n })\n .collect(),\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::certification::CertificationDocumentParser;\n use super::transaction::TransactionDocumentParser;\n use super::*;\n use crate::Document;\n\n #[test]\n fn generate_and_verify_empty_block() {\n let mut block = BlockDocumentV10 {\n nonce: 100_010_200_000_006_940,\n version: 10,\n number: BlockNumber(174_260),\n pow_min: 68,\n time: 1_525_296_873,\n median_time: 1_525_292_577,\n members_count: 33,\n monetary_mass: 15_633_687,\n unit_base: 0,\n issuers_count: 8,\n issuers_frame: 41,\n issuers_frame_var: 0,\n currency: CurrencyName(String::from(\"g1-test\")),\n issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(\"39Fnossy1GrndwCnAXGDw3K5UYXhNXAFQe7yhYZp8ELP\").unwrap())],\n signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64(\"lqXrNOopjM39oM7hgB7Vq13uIohdCuLlhh/q8RVVEZ5UVASphow/GXikCdhbWID19Bn0XrXzTbt/R7akbE9xAg==\").unwrap())],\n hash: None,\n parameters: None,\n previous_hash: Some(Hash::from_hex(\"0000A7D4361B9EBF4CE974A521149A73E8A5DE9B73907AB3BC918726AED7D40A\").expect(\"fail to parse previous_hash\")),\n previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58(\"EPKuZA1Ek5y8S1AjAmAPtGrVCMFqUGzUEAa7Ei62CY2L\").unwrap())),\n inner_hash: None,\n dividend: None,\n identities: Vec::new(),\n joiners: Vec::new(),\n actives: Vec::new(),\n leavers: Vec::new(),\n revoked: Vec::new(),\n excluded: Vec::new(),\n certifications: Vec::new(),\n transactions: Vec::new(),\n };\n // test inner_hash computation\n block.generate_inner_hash();\n println!(\"{}\", block.generate_compact_text());\n assert_eq!(\n block\n .inner_hash\n .expect(\"tests::generate_and_verify_empty_block: Try to get inner_hash of an uncompleted or reduce block !\")\n .to_hex(),\n \"58E4865A47A46E0DF1449AABC449B5406A12047C413D61B5E17F86BE6641E7B0\"\n );\n // Test signature validity\n assert!(block.verify_signatures().is_ok());\n // Test hash computation\n block.generate_hash();\n assert_eq!(\n block\n .hash\n .expect(\"Try to get hash of an uncompleted or reduce block !\")\n .0\n .to_hex(),\n \"00002EE584F36C15D3EB21AAC78E0896C75EF9070E73B4EC33BFA2C3D561EEB2\"\n );\n }\n\n #[test]\n fn generate_and_verify_block() {\n let cert1 = CertificationDocumentParser::parse(\"Version: 10\nType: Certification\nCurrency: g1\nIssuer: 6TAzLWuNcSqgNDNpAutrKpPXcGJwy1ZEMeVvZSZNs2e3\nIdtyIssuer: CYPsYTdt87Tx6cCiZs9KD4jqPgYxbcVEqVZpRgJ9jjoV\nIdtyUniqueID: PascaleM\nIdtyTimestamp: 97401-0000003821911909F98519CC773D2D3E5CFE3D5DBB39F4F4FF33B96B4D41800E\nIdtySignature: QncUVXxZ2NfARjdJOn6luILvDuG1NuK9qSoaU4CST2Ij8z7oeVtEgryHl+EXOjSe6XniALsCT0gU8wtadcA/Cw==\nCertTimestamp: 106669-000003682E6FE38C44433DCE92E8B2A26C69B6D7867A2BAED231E788DDEF4251\nUmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==\").expect(\"Fail to parse cert1\");\n let cert1 = match cert1 {\n CertificationDocument::V10(cert_v10) =\u003e cert_v10,\n };\n\n let tx1 = TransactionDocumentParser::parse(\"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B\nLocktime: 0\nIssuers:\n8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3\nInputs:\n1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345\nUnlocks:\n0:SIG(0)\nOutputs:\n1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX)\nComment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci\nT0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==\").expect(\"Fail to parse tx1\");\n\n let tx2 = TransactionDocumentParser::parse(\"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B\nLocktime: 0\nIssuers:\n8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3\nInputs:\n1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106614\nUnlocks:\n0:SIG(0)\nOutputs:\n1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8)\nComment: DU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci\na9PHPuSfw7jW8FRQHXFsGi/bnLjbtDnTYvEVgUC9u0WlR7GVofa+Xb+l5iy6NwuEXiwvueAkf08wPVY8xrNcCg==\").expect(\"Fail to parse tx2\");\n\n let mut block = BlockDocumentV10 {\n nonce: 10_300_000_018_323,\n version: 10,\n number: BlockNumber(107_984),\n pow_min: 88,\n time: 1_522_685_861,\n median_time: 1522683184,\n members_count: 896,\n monetary_mass: 140_469_765,\n unit_base: 0,\n issuers_count: 42,\n issuers_frame: 211,\n issuers_frame_var: 0,\n currency: CurrencyName(String::from(\"g1\")),\n issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(\"DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ\").unwrap())],\n signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64(\"92id58VmkhgVNee4LDqBGSm8u/ooHzAD67JM6fhAE/CV8LCz7XrMF1DvRl+eRpmlaVkp6I+Iy8gmZ1WUM5C8BA==\").unwrap())],\n hash: None,\n parameters: None,\n previous_hash: Some(Hash::from_hex(\"000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB\").expect(\"fail to parse previous_hash\")),\n previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58(\"D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH\").unwrap())),\n inner_hash: None,\n dividend: None,\n identities: Vec::new(),\n joiners: Vec::new(),\n actives: Vec::new(),\n leavers: Vec::new(),\n revoked: Vec::new(),\n excluded: Vec::new(),\n certifications: vec![TextDocumentFormat::Complete(cert1)],\n transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))],\n };\n // test inner_hash computation\n block.generate_inner_hash();\n println!(\"{}\", block.generate_compact_text());\n assert_eq!(\n block\n .inner_hash\n .expect(\"tests::generate_and_verify_block: Try to get inner_hash of an uncompleted or reduce block !\")\n .to_hex(),\n \"C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1\"\n );\n // test generate_compact_text()\n assert_eq!(\n block.generate_compact_text(),\n \"Version: 10\nType: Block\nCurrency: g1\nNumber: 107984\nPoWMin: 88\nTime: 1522685861\nMedianTime: 1522683184\nUnitBase: 0\nIssuer: DA4PYtXdvQqk1nCaprXH52iMsK5Ahxs1nRWbWKLhpVkQ\nIssuersFrame: 211\nIssuersFrameVar: 0\nDifferentIssuersCount: 42\nPreviousHash: 000001144968D0C3516BE6225E4662F182E28956AF46DD7FB228E3D0F9413FEB\nPreviousIssuer: D3krfq6J9AmfpKnS3gQVYoy7NzGCc61vokteTS8LJ4YH\nMembersCount: 896\nIdentities:\nJoiners:\nActives:\nLeavers:\nRevoked:\nExcluded:\nCertifications:\n6TAzLWuNcSqgNDNpAutrKpPXcGJwy1ZEMeVvZSZNs2e3:CYPsYTdt87Tx6cCiZs9KD4jqPgYxbcVEqVZpRgJ9jjoV:106669:UmseG2XKNwKcY8RFi6gUCT91udGnnNmSh7se10J1jeRVlwf+O2Tyb2Cccot9Dt7BO4+Kx2P6vFJB3oVGGHMxBA==\nTransactions:\nTX:10:1:1:1:1:1:0\n107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B\n8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3\n1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106345\n0:SIG(0)\n1002:0:SIG(CitdnuQgZ45tNFCagay7Wh12gwwHM8VLej1sWmfHWnQX)\nDU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci\nT0LlCcbIn7xDFws48H8LboN6NxxwNXXTovG4PROLf7tkUAueHFWjfwZFKQXeZEHxfaL1eYs3QspGtLWUHPRVCQ==\nTX:10:1:1:1:1:1:0\n107982-000001242F6DA51C06A915A96C58BAA37AB3D1EB51F6E1C630C707845ACF764B\n8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3\n1002:0:D:8dkCwvAqSczUjKsoVMDPVbQ3i6bBQeBQYawL87kqTSQ3:106614\n0:SIG(0)\n1002:0:SIG(78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8)\nDU symbolique pour demander le codage de nouvelles fonctionnalites cf. https://forum.monnaie-libre.fr/t/creer-de-nouvelles-fonctionnalites-dans-cesium-les-autres-applications/2025 Merci\na9PHPuSfw7jW8FRQHXFsGi/bnLjbtDnTYvEVgUC9u0WlR7GVofa+Xb+l5iy6NwuEXiwvueAkf08wPVY8xrNcCg==\nInnerHash: C8AB69E33ECE2612EADC7AB30D069B1F1A3D8C95EBBFD50DE583AC8E3666CCA1\nNonce: \"\n );\n // Test signature validity\n assert!(block.verify_signatures().is_ok());\n // Test hash computation\n block.generate_hash();\n assert_eq!(\n block\n .hash\n .expect(\"Try to get hash of an uncompleted or reduce block !\")\n .0\n .to_hex(),\n \"000004F8B84A3590243BA562E5F2BA379F55A0B387C5D6FAC1022DFF7FFE6014\"\n );\n }\n\n #[test]\n fn generate_and_verify_block_2() {\n let ms1 = MembershipDocumentParser::parse(\n \"Version: 10\nType: Membership\nCurrency: g1\nIssuer: 4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969\nBlock: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\nMembership: IN\nUserID: piaaf31\nCertTS: 74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6\ngvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==\",\n )\n .expect(\"Fail to parse ms1\");\n let ms1 = match ms1 {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10,\n };\n\n let tx1 = TransactionDocumentParser::parse(\n \"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\nLocktime: 0\nIssuers:\n51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2\nInputs:\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389\nUnlocks:\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\n6:SIG(0)\nOutputs:\n7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant)\n28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2)\nComment: Panier mixte plus 40 pommes merci\n7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==\",\n )\n .expect(\"Fail to parse tx1\");\n\n let tx2 = TransactionDocumentParser::parse(\n \"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\nLocktime: 0\nIssuers:\n3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX\nInputs:\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222\nUnlocks:\n0:SIG(0)\n1:SIG(0)\n2:SIG(0)\n3:SIG(0)\n4:SIG(0)\n5:SIG(0)\nOutputs:\n6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD)\n12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX)\nComment: En reglement de tes bons bocaux de fruits et legumes\nnxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg==\n\",\n )\n .expect(\"Fail to parse tx2\");\n\n let mut block = BlockDocumentV10 {\n nonce: 10_300_000_090_296,\n version: 10,\n number: BlockNumber(165_647),\n pow_min: 90,\n time: 1_540_633_175,\n median_time: 1_540_627_811,\n members_count: 1402,\n monetary_mass: 386_008_811,\n unit_base: 0,\n issuers_count: 37,\n issuers_frame: 186,\n issuers_frame_var: 0,\n currency: CurrencyName(String::from(\"g1\")),\n issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(\"A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq\").unwrap())],\n signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64(\"2Z/+9ADdZvHXs19YR8+qDzgfl8WJlBG5PcbFvBG9TOuUJbjAdxhcgxrFrSRIABGWcCrIgLkB805fZVLP8jOjBA==\").unwrap())],\n hash: None,\n parameters: None,\n previous_hash: Some(Hash::from_hex(\"000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51\").expect(\"fail to parse previous_hash\")),\n previous_issuer: Some(PubKey::Ed25519(ed25519::PublicKey::from_base58(\"8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65\").unwrap())),\n inner_hash: None,//Some(Hash::from_hex(\"3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846\").expect(\"fail to parse inner_hash\")),\n dividend: None,\n identities: vec![],\n joiners: vec![],\n actives: vec![ms1],\n leavers: vec![],\n revoked: vec![],\n excluded: vec![],\n certifications: vec![],\n transactions: vec![TxDocOrTxHash::TxDoc(Box::new(tx1)), TxDocOrTxHash::TxDoc(Box::new(tx2))],\n };\n // test inner_hash computation\n block.generate_inner_hash();\n println!(\"{}\", block.generate_compact_text());\n assert_eq!(\n block\n .inner_hash\n .expect(\"tests::generate_and_verify_block_2: Try to get inner_hash of an uncompleted or reduce block !\")\n .to_hex(),\n \"3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846\"\n );\n // test generate_compact_text()\n let block_compact_text = block.generate_compact_text();\n assert_eq!(\n block_compact_text,\n \"Version: 10\\nType: Block\\nCurrency: g1\\nNumber: 165647\\nPoWMin: 90\\nTime: 1540633175\\nMedianTime: 1540627811\\nUnitBase: 0\\nIssuer: A4pc9Uuk4NXkWG8CibicjjPpEPdiup1mhjMoRWUZsonq\\nIssuersFrame: 186\\nIssuersFrameVar: 0\\nDifferentIssuersCount: 37\\nPreviousHash: 000003E78FA4133F2C13B416F330C8DFB5A41EB87E37190615DB334F2C914A51\\nPreviousIssuer: 8NmGZmGjL1LUgJQRg282yQF7KTdQuRNAg8QfSa2qvd65\\nMembersCount: 1402\\nIdentities:\\nJoiners:\\nActives:\\n4VZkro3N7VonygybESHngKUABA6gSrbW77Ktb94zE969:gvaZ1QnJf8FjjRDJ0cYusgpBgQ8r0NqEz39BooH6DtIrgX+WTeXuLSnjZDl35VCBjokvyjry+v0OkTT8FKpABA==:165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2:74077-0000022816648B2F7801E059F67CCD0C023FF0ED84459D52C70494D74DDCC6F6:piaaf31\\nLeavers:\\nRevoked:\\nExcluded:\\nCertifications:\\nTransactions:\\nTX:10:1:7:7:2:1:0\\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\\n51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:163766\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164040\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164320\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164584\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:164849\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165118\\n1004:0:D:51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2:165389\\n0:SIG(0)\\n1:SIG(0)\\n2:SIG(0)\\n3:SIG(0)\\n4:SIG(0)\\n5:SIG(0)\\n6:SIG(0)\\n7000:0:SIG(98wxzS683Tc1WWm1YxpL5WpxS7wBa1mZBccKSsYpaant)\\n28:0:SIG(51EFVNZwpfmTXU7BSLpeh3PZFgfdmm5hq5MzCDopdH2)\\nPanier mixte plus 40 pommes merci\\n7o/yIh0BNSAv5pNmHz04uUBl8TuP2s4HRFMtKeGFQfXNYJPUyJTP/dj6hdrgKtJkm5dCfbxT4KRy6wJf+dj1Cw==\\nTX:10:1:6:6:2:1:0\\n165645-000002D30130881939961A38D51CA233B3C696AA604439036DB1AAA4ED5046D2\\n3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX\\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:148827\\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149100\\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149370\\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149664\\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:149943\\n1002:0:D:3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX:150222\\n0:SIG(0)\\n1:SIG(0)\\n2:SIG(0)\\n3:SIG(0)\\n4:SIG(0)\\n5:SIG(0)\\n6000:0:SIG(AopwTfXhj8VqZReFJYGGWnoWnXNj3RgaqFcGGywXpZrD)\\n12:0:SIG(3Uwq4qNp2A97P1XQueEBCxmnvgtAKMdfrEq6VB7Ph2qX)\\nEn reglement de tes bons bocaux de fruits et legumes\\nnxr4exGrt16jteN9ZX3XZPP9l+X0OUbZ1o/QjE1hbWQNtVU3HhH9SJoEvNj2iVl3gCRr9u2OA9uj9vCyUDyjAg==\\nInnerHash: 3B49ECC1475549CFD94CA7B399311548A0FD0EC93C8EDD5670DAA5A958A41846\\nNonce: \"\n );\n // Test signature validity\n assert!(block.verify_signatures().is_ok());\n // Test hash computation\n block.generate_hash();\n assert_eq!(\n block\n .hash\n .expect(\"Try to get hash of an uncompleted or reduce block !\")\n .0\n .to_hex(),\n \"000002026E32A3D649B34968AAF9D03C4F19A5954229C54A801BBB1CD216B230\"\n );\n }\n}\n","traces":[{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":1}},{"line":139,"address":null,"length":0,"stats":{"Line":1}},{"line":141,"address":null,"length":0,"stats":{"Line":1}},{"line":142,"address":null,"length":0,"stats":{"Line":1}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":148,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":1}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":165,"address":null,"length":0,"stats":{"Line":1}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":175,"address":null,"length":0,"stats":{"Line":1}},{"line":176,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":1}},{"line":190,"address":null,"length":0,"stats":{"Line":1}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":1}},{"line":195,"address":null,"length":0,"stats":{"Line":1}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":197,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":1}},{"line":200,"address":null,"length":0,"stats":{"Line":1}},{"line":201,"address":null,"length":0,"stats":{"Line":1}},{"line":202,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":206,"address":null,"length":0,"stats":{"Line":1}},{"line":207,"address":null,"length":0,"stats":{"Line":1}},{"line":208,"address":null,"length":0,"stats":{"Line":1}},{"line":211,"address":null,"length":0,"stats":{"Line":1}},{"line":212,"address":null,"length":0,"stats":{"Line":1}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":1}},{"line":220,"address":null,"length":0,"stats":{"Line":1}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":223,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":1}},{"line":226,"address":null,"length":0,"stats":{"Line":1}},{"line":227,"address":null,"length":0,"stats":{"Line":1}},{"line":228,"address":null,"length":0,"stats":{"Line":1}},{"line":229,"address":null,"length":0,"stats":{"Line":1}},{"line":231,"address":null,"length":0,"stats":{"Line":1}},{"line":232,"address":null,"length":0,"stats":{"Line":1}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":234,"address":null,"length":0,"stats":{"Line":1}},{"line":235,"address":null,"length":0,"stats":{"Line":1}},{"line":236,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":0}},{"line":238,"address":null,"length":0,"stats":{"Line":0}},{"line":240,"address":null,"length":0,"stats":{"Line":1}},{"line":242,"address":null,"length":0,"stats":{"Line":1}},{"line":265,"address":null,"length":0,"stats":{"Line":1}},{"line":266,"address":null,"length":0,"stats":{"Line":1}},{"line":267,"address":null,"length":0,"stats":{"Line":1}},{"line":268,"address":null,"length":0,"stats":{"Line":1}},{"line":269,"address":null,"length":0,"stats":{"Line":1}},{"line":270,"address":null,"length":0,"stats":{"Line":1}},{"line":271,"address":null,"length":0,"stats":{"Line":0}},{"line":272,"address":null,"length":0,"stats":{"Line":1}},{"line":273,"address":null,"length":0,"stats":{"Line":1}},{"line":274,"address":null,"length":0,"stats":{"Line":1}},{"line":275,"address":null,"length":0,"stats":{"Line":1}},{"line":276,"address":null,"length":0,"stats":{"Line":1}},{"line":277,"address":null,"length":0,"stats":{"Line":0}},{"line":278,"address":null,"length":0,"stats":{"Line":0}},{"line":279,"address":null,"length":0,"stats":{"Line":0}},{"line":280,"address":null,"length":0,"stats":{"Line":1}},{"line":281,"address":null,"length":0,"stats":{"Line":0}},{"line":282,"address":null,"length":0,"stats":{"Line":0}},{"line":283,"address":null,"length":0,"stats":{"Line":0}},{"line":284,"address":null,"length":0,"stats":{"Line":0}},{"line":285,"address":null,"length":0,"stats":{"Line":0}},{"line":286,"address":null,"length":0,"stats":{"Line":0}},{"line":287,"address":null,"length":0,"stats":{"Line":0}},{"line":288,"address":null,"length":0,"stats":{"Line":0}},{"line":291,"address":null,"length":0,"stats":{"Line":1}},{"line":292,"address":null,"length":0,"stats":{"Line":1}},{"line":294,"address":null,"length":0,"stats":{"Line":1}},{"line":295,"address":null,"length":0,"stats":{"Line":1}},{"line":297,"address":null,"length":0,"stats":{"Line":2}},{"line":298,"address":null,"length":0,"stats":{"Line":2}},{"line":300,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":303,"address":null,"length":0,"stats":{"Line":1}},{"line":304,"address":null,"length":0,"stats":{"Line":1}},{"line":306,"address":null,"length":0,"stats":{"Line":0}},{"line":307,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":1}},{"line":310,"address":null,"length":0,"stats":{"Line":1}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":313,"address":null,"length":0,"stats":{"Line":0}},{"line":315,"address":null,"length":0,"stats":{"Line":0}},{"line":316,"address":null,"length":0,"stats":{"Line":0}},{"line":318,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":1}},{"line":323,"address":null,"length":0,"stats":{"Line":1}},{"line":325,"address":null,"length":0,"stats":{"Line":0}},{"line":327,"address":null,"length":0,"stats":{"Line":0}},{"line":328,"address":null,"length":0,"stats":{"Line":0}},{"line":329,"address":null,"length":0,"stats":{"Line":0}},{"line":331,"address":null,"length":0,"stats":{"Line":0}},{"line":332,"address":null,"length":0,"stats":{"Line":0}},{"line":334,"address":null,"length":0,"stats":{"Line":0}},{"line":335,"address":null,"length":0,"stats":{"Line":0}},{"line":337,"address":null,"length":0,"stats":{"Line":0}},{"line":338,"address":null,"length":0,"stats":{"Line":0}},{"line":340,"address":null,"length":0,"stats":{"Line":0}},{"line":341,"address":null,"length":0,"stats":{"Line":0}},{"line":344,"address":null,"length":0,"stats":{"Line":0}},{"line":345,"address":null,"length":0,"stats":{"Line":0}},{"line":347,"address":null,"length":0,"stats":{"Line":1}},{"line":348,"address":null,"length":0,"stats":{"Line":1}},{"line":349,"address":null,"length":0,"stats":{"Line":1}},{"line":350,"address":null,"length":0,"stats":{"Line":1}},{"line":351,"address":null,"length":0,"stats":{"Line":1}},{"line":352,"address":null,"length":0,"stats":{"Line":1}},{"line":353,"address":null,"length":0,"stats":{"Line":0}},{"line":354,"address":null,"length":0,"stats":{"Line":0}},{"line":355,"address":null,"length":0,"stats":{"Line":0}},{"line":356,"address":null,"length":0,"stats":{"Line":0}},{"line":357,"address":null,"length":0,"stats":{"Line":0}},{"line":361,"address":null,"length":0,"stats":{"Line":0}},{"line":362,"address":null,"length":0,"stats":{"Line":0}},{"line":366,"address":null,"length":0,"stats":{"Line":0}},{"line":367,"address":null,"length":0,"stats":{"Line":0}},{"line":368,"address":null,"length":0,"stats":{"Line":0}},{"line":369,"address":null,"length":0,"stats":{"Line":0}},{"line":370,"address":null,"length":0,"stats":{"Line":0}},{"line":371,"address":null,"length":0,"stats":{"Line":0}},{"line":372,"address":null,"length":0,"stats":{"Line":0}},{"line":373,"address":null,"length":0,"stats":{"Line":0}},{"line":375,"address":null,"length":0,"stats":{"Line":0}},{"line":376,"address":null,"length":0,"stats":{"Line":0}},{"line":377,"address":null,"length":0,"stats":{"Line":0}},{"line":378,"address":null,"length":0,"stats":{"Line":0}},{"line":380,"address":null,"length":0,"stats":{"Line":0}},{"line":381,"address":null,"length":0,"stats":{"Line":0}},{"line":382,"address":null,"length":0,"stats":{"Line":0}},{"line":383,"address":null,"length":0,"stats":{"Line":0}},{"line":387,"address":null,"length":0,"stats":{"Line":0}},{"line":388,"address":null,"length":0,"stats":{"Line":0}},{"line":398,"address":null,"length":0,"stats":{"Line":0}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":403,"address":null,"length":0,"stats":{"Line":0}},{"line":404,"address":null,"length":0,"stats":{"Line":0}},{"line":408,"address":null,"length":0,"stats":{"Line":1}},{"line":410,"address":null,"length":0,"stats":{"Line":1}},{"line":411,"address":null,"length":0,"stats":{"Line":1}},{"line":418,"address":null,"length":0,"stats":{"Line":1}},{"line":419,"address":null,"length":0,"stats":{"Line":1}},{"line":423,"address":null,"length":0,"stats":{"Line":1}},{"line":424,"address":null,"length":0,"stats":{"Line":1}},{"line":428,"address":null,"length":0,"stats":{"Line":0}},{"line":429,"address":null,"length":0,"stats":{"Line":0}},{"line":433,"address":null,"length":0,"stats":{"Line":1}},{"line":434,"address":null,"length":0,"stats":{"Line":0}},{"line":438,"address":null,"length":0,"stats":{"Line":1}},{"line":439,"address":null,"length":0,"stats":{"Line":1}},{"line":444,"address":null,"length":0,"stats":{"Line":1}},{"line":445,"address":null,"length":0,"stats":{"Line":1}},{"line":446,"address":null,"length":0,"stats":{"Line":1}},{"line":448,"address":null,"length":0,"stats":{"Line":0}},{"line":449,"address":null,"length":0,"stats":{"Line":1}},{"line":450,"address":null,"length":0,"stats":{"Line":0}},{"line":451,"address":null,"length":0,"stats":{"Line":0}},{"line":453,"address":null,"length":0,"stats":{"Line":0}},{"line":461,"address":null,"length":0,"stats":{"Line":0}},{"line":462,"address":null,"length":0,"stats":{"Line":0}},{"line":467,"address":null,"length":0,"stats":{"Line":1}},{"line":468,"address":null,"length":0,"stats":{"Line":1}},{"line":538,"address":null,"length":0,"stats":{"Line":0}},{"line":540,"address":null,"length":0,"stats":{"Line":0}},{"line":541,"address":null,"length":0,"stats":{"Line":0}},{"line":542,"address":null,"length":0,"stats":{"Line":0}},{"line":543,"address":null,"length":0,"stats":{"Line":0}},{"line":544,"address":null,"length":0,"stats":{"Line":0}},{"line":545,"address":null,"length":0,"stats":{"Line":0}},{"line":546,"address":null,"length":0,"stats":{"Line":0}},{"line":547,"address":null,"length":0,"stats":{"Line":0}},{"line":548,"address":null,"length":0,"stats":{"Line":0}},{"line":549,"address":null,"length":0,"stats":{"Line":0}},{"line":550,"address":null,"length":0,"stats":{"Line":0}},{"line":551,"address":null,"length":0,"stats":{"Line":0}},{"line":552,"address":null,"length":0,"stats":{"Line":0}},{"line":553,"address":null,"length":0,"stats":{"Line":0}},{"line":554,"address":null,"length":0,"stats":{"Line":0}},{"line":555,"address":null,"length":0,"stats":{"Line":0}},{"line":556,"address":null,"length":0,"stats":{"Line":0}},{"line":557,"address":null,"length":0,"stats":{"Line":0}},{"line":558,"address":null,"length":0,"stats":{"Line":0}},{"line":559,"address":null,"length":0,"stats":{"Line":0}},{"line":560,"address":null,"length":0,"stats":{"Line":0}},{"line":561,"address":null,"length":0,"stats":{"Line":0}},{"line":566,"address":null,"length":0,"stats":{"Line":0}},{"line":571,"address":null,"length":0,"stats":{"Line":0}},{"line":576,"address":null,"length":0,"stats":{"Line":0}},{"line":581,"address":null,"length":0,"stats":{"Line":0}},{"line":593,"address":null,"length":0,"stats":{"Line":0}},{"line":594,"address":null,"length":0,"stats":{"Line":0}},{"line":606,"address":null,"length":0,"stats":{"Line":0}},{"line":628,"address":4223152,"length":1,"stats":{"Line":2}},{"line":629,"address":4248235,"length":1,"stats":{"Line":1}},{"line":632,"address":4247214,"length":1,"stats":{"Line":1}},{"line":642,"address":4247225,"length":1,"stats":{"Line":1}},{"line":643,"address":4247305,"length":1,"stats":{"Line":1}},{"line":644,"address":4247491,"length":1,"stats":{"Line":1}},{"line":645,"address":4247726,"length":1,"stats":{"Line":1}},{"line":646,"address":4247734,"length":1,"stats":{"Line":1}},{"line":647,"address":4247746,"length":1,"stats":{"Line":1}},{"line":648,"address":4247866,"length":1,"stats":{"Line":1}},{"line":649,"address":4248002,"length":1,"stats":{"Line":1}},{"line":650,"address":4248010,"length":1,"stats":{"Line":1}},{"line":651,"address":4248030,"length":1,"stats":{"Line":1}},{"line":652,"address":4248045,"length":1,"stats":{"Line":1}},{"line":653,"address":4248060,"length":1,"stats":{"Line":1}},{"line":654,"address":4248093,"length":1,"stats":{"Line":1}},{"line":655,"address":4248123,"length":1,"stats":{"Line":1}},{"line":656,"address":4248153,"length":1,"stats":{"Line":1}},{"line":657,"address":4248183,"length":1,"stats":{"Line":1}},{"line":658,"address":4248213,"length":1,"stats":{"Line":1}},{"line":661,"address":4248985,"length":1,"stats":{"Line":1}},{"line":662,"address":4249023,"length":1,"stats":{"Line":1}},{"line":663,"address":4249348,"length":1,"stats":{"Line":1}},{"line":664,"address":4249232,"length":1,"stats":{"Line":1}},{"line":671,"address":4249700,"length":1,"stats":{"Line":1}},{"line":673,"address":4249817,"length":1,"stats":{"Line":1}},{"line":674,"address":4249940,"length":1,"stats":{"Line":1}},{"line":675,"address":4249824,"length":1,"stats":{"Line":1}},{"line":685,"address":4223184,"length":1,"stats":{"Line":2}},{"line":686,"address":4250692,"length":1,"stats":{"Line":1}},{"line":697,"address":4250773,"length":1,"stats":{"Line":1}},{"line":700,"address":4250880,"length":1,"stats":{"Line":1}},{"line":714,"address":4250965,"length":1,"stats":{"Line":1}},{"line":716,"address":4250973,"length":1,"stats":{"Line":1}},{"line":730,"address":4251035,"length":1,"stats":{"Line":1}},{"line":732,"address":4252778,"length":1,"stats":{"Line":1}},{"line":735,"address":4251043,"length":1,"stats":{"Line":1}},{"line":745,"address":4251054,"length":1,"stats":{"Line":1}},{"line":746,"address":4251123,"length":1,"stats":{"Line":1}},{"line":747,"address":4251334,"length":1,"stats":{"Line":1}},{"line":748,"address":4251575,"length":1,"stats":{"Line":1}},{"line":749,"address":4251583,"length":1,"stats":{"Line":1}},{"line":750,"address":4251595,"length":1,"stats":{"Line":1}},{"line":751,"address":4251715,"length":1,"stats":{"Line":1}},{"line":752,"address":4251851,"length":1,"stats":{"Line":1}},{"line":753,"address":4251859,"length":1,"stats":{"Line":1}},{"line":754,"address":4251879,"length":1,"stats":{"Line":1}},{"line":755,"address":4251894,"length":1,"stats":{"Line":1}},{"line":756,"address":4251909,"length":1,"stats":{"Line":1}},{"line":757,"address":4251942,"length":1,"stats":{"Line":1}},{"line":758,"address":4251972,"length":1,"stats":{"Line":1}},{"line":759,"address":4252002,"length":1,"stats":{"Line":1}},{"line":760,"address":4252034,"length":1,"stats":{"Line":1}},{"line":761,"address":4252305,"length":1,"stats":{"Line":1}},{"line":764,"address":4253546,"length":1,"stats":{"Line":1}},{"line":765,"address":4253569,"length":1,"stats":{"Line":1}},{"line":766,"address":4253897,"length":1,"stats":{"Line":1}},{"line":767,"address":4253781,"length":1,"stats":{"Line":1}},{"line":774,"address":4254267,"length":1,"stats":{"Line":1}},{"line":775,"address":4254252,"length":1,"stats":{"Line":1}},{"line":820,"address":4254622,"length":1,"stats":{"Line":1}},{"line":822,"address":4254742,"length":1,"stats":{"Line":1}},{"line":823,"address":4254865,"length":1,"stats":{"Line":1}},{"line":824,"address":4254749,"length":1,"stats":{"Line":1}},{"line":834,"address":4223216,"length":1,"stats":{"Line":2}},{"line":835,"address":4255892,"length":1,"stats":{"Line":1}},{"line":848,"address":4255973,"length":1,"stats":{"Line":1}},{"line":851,"address":4256080,"length":1,"stats":{"Line":1}},{"line":881,"address":4256165,"length":1,"stats":{"Line":1}},{"line":883,"address":4256173,"length":1,"stats":{"Line":1}},{"line":912,"address":4256235,"length":1,"stats":{"Line":1}},{"line":914,"address":4258136,"length":1,"stats":{"Line":1}},{"line":917,"address":4256243,"length":1,"stats":{"Line":1}},{"line":927,"address":4256254,"length":1,"stats":{"Line":1}},{"line":928,"address":4256323,"length":1,"stats":{"Line":1}},{"line":929,"address":4256534,"length":1,"stats":{"Line":1}},{"line":930,"address":4256775,"length":1,"stats":{"Line":1}},{"line":931,"address":4256783,"length":1,"stats":{"Line":1}},{"line":932,"address":4256795,"length":1,"stats":{"Line":1}},{"line":933,"address":4256915,"length":1,"stats":{"Line":1}},{"line":934,"address":4257051,"length":1,"stats":{"Line":1}},{"line":935,"address":4257059,"length":1,"stats":{"Line":1}},{"line":936,"address":4257080,"length":1,"stats":{"Line":1}},{"line":937,"address":4257131,"length":1,"stats":{"Line":1}},{"line":938,"address":4257201,"length":1,"stats":{"Line":1}},{"line":939,"address":4257398,"length":1,"stats":{"Line":1}},{"line":940,"address":4257464,"length":1,"stats":{"Line":1}},{"line":941,"address":4257530,"length":1,"stats":{"Line":1}},{"line":942,"address":4257596,"length":1,"stats":{"Line":1}},{"line":943,"address":4257663,"length":1,"stats":{"Line":1}},{"line":946,"address":4258904,"length":1,"stats":{"Line":1}},{"line":947,"address":4258927,"length":1,"stats":{"Line":1}},{"line":948,"address":4259255,"length":1,"stats":{"Line":1}},{"line":949,"address":4259139,"length":1,"stats":{"Line":1}},{"line":956,"address":4259610,"length":1,"stats":{"Line":1}},{"line":957,"address":4259625,"length":1,"stats":{"Line":1}},{"line":962,"address":4259965,"length":1,"stats":{"Line":1}},{"line":964,"address":4260085,"length":1,"stats":{"Line":1}},{"line":965,"address":4260208,"length":1,"stats":{"Line":1}},{"line":966,"address":4260092,"length":1,"stats":{"Line":1}}],"covered":200,"coverable":343},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","block.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Block document.\n\npub mod v10;\n\npub use v10::{BlockDocumentV10, BlockDocumentV10Stringified};\n\nuse crate::blockstamp::Blockstamp;\nuse crate::{BlockHash, BlockNumber, Document, ToStringObject};\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::{PrivKey, PubKey, PublicKey};\n\n/// Wrap a Block document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]\npub enum BlockDocument {\n V10(BlockDocumentV10),\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\n/// Error when verifying a hash of a block\npub enum VerifyBlockHashError {\n /// The hash is missing\n MissingHash { block_number: BlockNumber },\n /// Hash is invalid\n InvalidHash {\n block_number: BlockNumber,\n expected_hash: Hash,\n actual_hash: Hash,\n },\n}\n\npub trait BlockDocumentTrait {\n /// Common time in block (also known as 'blockchain time')\n fn common_time(\u0026self) -\u003e u64;\n /// Compute hash\n fn compute_hash(\u0026self) -\u003e BlockHash;\n /// Compute inner hash\n fn compute_inner_hash(\u0026self) -\u003e Hash {\n Hash::compute_str(\u0026self.generate_compact_inner_text())\n }\n /// Compute the character string that will be hashed\n fn compute_will_hashed_string(\u0026self) -\u003e String;\n /// Compute the character string that will be signed\n fn compute_will_signed_string(\u0026self) -\u003e String;\n /// Get current frame size (in blocks)\n fn current_frame_size(\u0026self) -\u003e usize;\n /// Generate compact inner text (for compute inner_hash)\n fn generate_compact_inner_text(\u0026self) -\u003e String;\n /// Compute hash and save it in document\n fn generate_hash(\u0026mut self);\n /// Compute inner hash and save it in document\n fn generate_inner_hash(\u0026mut self);\n /// Get block hash\n fn hash(\u0026self) -\u003e Option\u003cBlockHash\u003e;\n /// Increment nonce\n fn increment_nonce(\u0026mut self);\n /// Get block inner hash\n fn inner_hash(\u0026self) -\u003e Option\u003cHash\u003e;\n /// Get number of compute members in the current frame\n fn issuers_count(\u0026self) -\u003e usize;\n /// Get block number\n fn number(\u0026self) -\u003e BlockNumber;\n /// Get previous hash\n fn previous_hash(\u0026self) -\u003e Option\u003cHash\u003e;\n /// Get previous blockstamp\n fn previous_blockstamp(\u0026self) -\u003e Blockstamp;\n /// Lightens the block (for example to store it while minimizing the space required)\n fn reduce(\u0026mut self);\n /// Verify inner hash\n fn verify_inner_hash(\u0026self) -\u003e Result\u003c(), VerifyBlockHashError\u003e;\n /// Verify block hash\n fn verify_hash(\u0026self) -\u003e Result\u003c(), VerifyBlockHashError\u003e;\n /// Sign block\n fn sign(\u0026mut self, privkey: PrivKey);\n}\n\nimpl BlockDocumentTrait for BlockDocument {\n #[inline]\n fn compute_hash(\u0026self) -\u003e BlockHash {\n match self {\n BlockDocument::V10(block) =\u003e block.compute_hash(),\n }\n }\n #[inline]\n fn compute_will_hashed_string(\u0026self) -\u003e String {\n match self {\n BlockDocument::V10(block) =\u003e block.compute_will_hashed_string(),\n }\n }\n #[inline]\n fn compute_will_signed_string(\u0026self) -\u003e String {\n match self {\n BlockDocument::V10(block) =\u003e block.compute_will_signed_string(),\n }\n }\n #[inline]\n fn current_frame_size(\u0026self) -\u003e usize {\n match self {\n BlockDocument::V10(block) =\u003e block.current_frame_size(),\n }\n }\n #[inline]\n fn generate_compact_inner_text(\u0026self) -\u003e String {\n match self {\n BlockDocument::V10(block) =\u003e block.generate_compact_inner_text(),\n }\n }\n #[inline]\n fn generate_hash(\u0026mut self) {\n match self {\n BlockDocument::V10(block) =\u003e block.generate_hash(),\n }\n }\n #[inline]\n fn generate_inner_hash(\u0026mut self) {\n match self {\n BlockDocument::V10(block) =\u003e block.generate_inner_hash(),\n }\n }\n #[inline]\n fn hash(\u0026self) -\u003e Option\u003cBlockHash\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.hash(),\n }\n }\n #[inline]\n fn increment_nonce(\u0026mut self) {\n match self {\n BlockDocument::V10(block) =\u003e block.increment_nonce(),\n }\n }\n #[inline]\n fn inner_hash(\u0026self) -\u003e Option\u003cHash\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.inner_hash(),\n }\n }\n #[inline]\n fn issuers_count(\u0026self) -\u003e usize {\n match self {\n BlockDocument::V10(block) =\u003e block.issuers_count(),\n }\n }\n #[inline]\n fn common_time(\u0026self) -\u003e u64 {\n match self {\n BlockDocument::V10(block) =\u003e block.common_time(),\n }\n }\n #[inline]\n fn number(\u0026self) -\u003e BlockNumber {\n match self {\n BlockDocument::V10(block) =\u003e block.number(),\n }\n }\n #[inline]\n fn previous_blockstamp(\u0026self) -\u003e Blockstamp {\n match self {\n BlockDocument::V10(block) =\u003e block.previous_blockstamp(),\n }\n }\n #[inline]\n fn previous_hash(\u0026self) -\u003e Option\u003cHash\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.previous_hash(),\n }\n }\n #[inline]\n fn reduce(\u0026mut self) {\n match self {\n BlockDocument::V10(block) =\u003e block.reduce(),\n }\n }\n #[inline]\n fn verify_inner_hash(\u0026self) -\u003e Result\u003c(), VerifyBlockHashError\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.verify_inner_hash(),\n }\n }\n #[inline]\n fn verify_hash(\u0026self) -\u003e Result\u003c(), VerifyBlockHashError\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.verify_hash(),\n }\n }\n #[inline]\n fn sign(\u0026mut self, privkey: PrivKey) {\n match self {\n BlockDocument::V10(block) =\u003e block.sign(privkey),\n }\n }\n}\n\nimpl Document for BlockDocument {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n match self {\n BlockDocument::V10(_) =\u003e 10u16,\n }\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n match self {\n BlockDocument::V10(block) =\u003e block.currency(),\n }\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n match self {\n BlockDocument::V10(block) =\u003e block.blockstamp(),\n }\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cSelf::PublicKey\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.issuers(),\n }\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003c\u003cSelf::PublicKey as PublicKey\u003e::Signature\u003e {\n match self {\n BlockDocument::V10(block) =\u003e block.signatures(),\n }\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n match self {\n BlockDocument::V10(block) =\u003e block.as_bytes(),\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub enum BlockDocumentStringified {\n V10(BlockDocumentV10Stringified),\n}\n\nimpl ToStringObject for BlockDocument {\n type StringObject = BlockDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match self {\n BlockDocument::V10(block) =\u003e BlockDocumentStringified::V10(block.to_string_object()),\n }\n }\n}\n","traces":[{"line":54,"address":null,"length":0,"stats":{"Line":1}},{"line":55,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":2}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":2}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":167,"address":null,"length":0,"stats":{"Line":2}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":2}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":1}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":1}},{"line":197,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":205,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":1}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":1}},{"line":231,"address":null,"length":0,"stats":{"Line":0}},{"line":232,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":0}},{"line":238,"address":null,"length":0,"stats":{"Line":0}},{"line":239,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":244,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":258,"address":null,"length":0,"stats":{"Line":0}},{"line":259,"address":null,"length":0,"stats":{"Line":0}},{"line":260,"address":null,"length":0,"stats":{"Line":0}}],"covered":16,"coverable":80},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","certification","v10.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Certification documents V10.\n\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\n\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::*;\nuse crate::text_document_traits::*;\n\n#[derive(Copy, Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]\n/// Wrap an Compact Revocation document (in block content)\npub struct CompactCertificationDocumentV10 {\n /// Issuer\n pub issuer: PubKey,\n /// Target\n pub target: PubKey,\n /// Blockstamp\n pub block_number: BlockNumber,\n /// Signature\n pub signature: Sig,\n}\n\nimpl CompactTextDocument for CompactCertificationDocumentV10 {\n fn as_compact_text(\u0026self) -\u003e String {\n format!(\n \"{issuer}:{target}:{block_number}:{signature}\",\n issuer = self.issuer,\n target = self.target,\n block_number = self.block_number.0,\n signature = self.signature,\n )\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// identity document for jsonification\npub struct CompactCertificationDocumentV10Stringified {\n /// Document issuer\n pub issuer: String,\n /// issuer of target identity.\n pub target: String,\n /// Block number\n pub block_number: u64,\n /// Document signature\n pub signature: String,\n}\n\nimpl ToStringObject for CompactCertificationDocumentV10 {\n type StringObject = CompactCertificationDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e CompactCertificationDocumentV10Stringified {\n CompactCertificationDocumentV10Stringified {\n issuer: format!(\"{}\", self.issuer),\n target: format!(\"{}\", self.target),\n block_number: u64::from(self.block_number.0),\n signature: format!(\"{}\", self.signature),\n }\n }\n}\n\n/// Wrap an Certification document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub struct CertificationDocumentV10 {\n /// Document as text.\n ///\n /// Is used to check signatures, and other values mut be extracted from it.\n text: String,\n\n /// Name of the currency.\n currency: String,\n /// Document issuer (there should be only one).\n issuers: Vec\u003cPubKey\u003e,\n /// issuer of target identity.\n target: PubKey,\n /// Username of target identity\n identity_username: String,\n /// Target Identity document blockstamp.\n identity_blockstamp: Blockstamp,\n /// Target Identity document signature.\n identity_sig: Sig,\n /// Blockstamp\n blockstamp: Blockstamp,\n /// Document signature (there should be only one).\n signatures: Vec\u003cSig\u003e,\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// identity document for jsonification\npub struct CertificationDocumentV10Stringified {\n /// Name of the currency.\n currency: String,\n /// Document issuer\n issuer: String,\n /// issuer of target identity.\n target: String,\n /// Username of target identity\n identity_username: String,\n /// Target Identity document blockstamp.\n identity_blockstamp: String,\n /// Target Identity document signature.\n identity_sig: String,\n /// Blockstamp\n blockstamp: String,\n /// Document signature\n signature: String,\n}\n\nimpl ToStringObject for CertificationDocumentV10 {\n type StringObject = CertificationDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e CertificationDocumentV10Stringified {\n CertificationDocumentV10Stringified {\n currency: self.currency.clone(),\n issuer: format!(\"{}\", self.issuers[0]),\n target: format!(\"{}\", self.target),\n identity_username: self.identity_username.clone(),\n identity_blockstamp: format!(\"{}\", self.identity_blockstamp),\n blockstamp: format!(\"{}\", self.blockstamp),\n identity_sig: format!(\"{}\", self.identity_sig),\n signature: format!(\"{}\", self.signatures[0]),\n }\n }\n}\n\nimpl CertificationDocumentV10 {\n /// Username of target identity\n pub fn identity_username(\u0026self) -\u003e \u0026str {\n \u0026self.identity_username\n }\n\n /// Pubkey of source identity\n pub fn source(\u0026self) -\u003e \u0026PubKey {\n \u0026self.issuers[0]\n }\n\n /// Pubkey of target identity\n pub fn target(\u0026self) -\u003e \u0026PubKey {\n \u0026self.target\n }\n // Parse certification document from pest pairs\n pub fn from_pest_pair(\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cCertificationDocumentV10, TextDocumentParseError\u003e {\n let doc = pair.as_str();\n let mut currency = \"\";\n let mut pubkeys = Vec::with_capacity(2);\n let mut uid = \"\";\n let mut sigs = Vec::with_capacity(2);\n let mut blockstamps = Vec::with_capacity(2);\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency = field.as_str(),\n Rule::pubkey =\u003e pubkeys.push(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.\n )),\n Rule::uid =\u003e {\n uid = field.as_str();\n }\n Rule::blockstamp =\u003e {\n let mut inner_rules = field.into_inner(); // { integer ~ \"-\" ~ hash }\n\n let block_id: \u0026str = inner_rules.next().unwrap().as_str();\n let block_hash: \u0026str = inner_rules.next().unwrap().as_str();\n blockstamps.push(Blockstamp {\n id: BlockNumber(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.\n hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.\n });\n }\n Rule::ed25519_sig =\u003e {\n sigs.push(Sig::Ed25519(\n ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.\n ));\n }\n Rule::EOI =\u003e (),\n _ =\u003e fatal_error!(\"unexpected rule\"), // Grammar ensures that we never reach this line\n }\n }\n\n Ok(CertificationDocumentV10 {\n text: doc.to_owned(),\n issuers: vec![pubkeys[0]],\n currency: currency.to_owned(),\n target: pubkeys[1],\n identity_username: uid.to_owned(),\n identity_blockstamp: blockstamps[0],\n identity_sig: sigs[0],\n blockstamp: blockstamps[1],\n signatures: vec![sigs[1]],\n })\n }\n}\n\nimpl Document for CertificationDocumentV10 {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n 10\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n \u0026self.currency\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n self.blockstamp\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n self.as_text_without_signature().as_bytes()\n }\n}\n\nimpl TextDocument for CertificationDocumentV10 {\n type CompactTextDocument_ = CompactCertificationDocumentV10;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n \u0026self.text\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n CompactCertificationDocumentV10 {\n issuer: self.issuers[0],\n target: self.target,\n block_number: self.blockstamp().id,\n signature: self.signatures()[0],\n }\n }\n}\n\n/// Certification document builder.\n#[derive(Debug, Copy, Clone)]\npub struct CertificationDocumentV10Builder\u003c'a\u003e {\n /// Document currency.\n pub currency: \u0026'a str,\n /// Certification issuer (=source).\n pub issuer: \u0026'a PubKey,\n /// Reference blockstamp.\n pub blockstamp: \u0026'a Blockstamp,\n /// Pubkey of target identity.\n pub target: \u0026'a PubKey,\n /// Username of target Identity.\n pub identity_username: \u0026'a str,\n /// Blockstamp of target Identity.\n pub identity_blockstamp: \u0026'a Blockstamp,\n /// Signature of target Identity.\n pub identity_sig: \u0026'a Sig,\n}\n\nimpl\u003c'a\u003e CertificationDocumentV10Builder\u003c'a\u003e {\n fn build_with_text_and_sigs(\n self,\n text: String,\n signatures: Vec\u003cSig\u003e,\n ) -\u003e CertificationDocumentV10 {\n CertificationDocumentV10 {\n text,\n currency: self.currency.to_string(),\n issuers: vec![*self.issuer],\n blockstamp: *self.blockstamp,\n target: *self.target,\n identity_username: self.identity_username.to_string(),\n identity_blockstamp: *self.identity_blockstamp,\n identity_sig: *self.identity_sig,\n signatures,\n }\n }\n}\n\nimpl\u003c'a\u003e DocumentBuilder for CertificationDocumentV10Builder\u003c'a\u003e {\n type Document = CertificationDocumentV10;\n type PrivateKey = PrivKey;\n\n fn build_with_signature(\u0026self, signatures: Vec\u003cSig\u003e) -\u003e CertificationDocumentV10 {\n self.build_with_text_and_sigs(self.generate_text(), signatures)\n }\n\n fn build_and_sign(\u0026self, private_keys: Vec\u003cPrivKey\u003e) -\u003e CertificationDocumentV10 {\n let (text, signatures) = self.build_signed_text(private_keys);\n self.build_with_text_and_sigs(text, signatures)\n }\n}\n\nimpl\u003c'a\u003e TextDocumentBuilder for CertificationDocumentV10Builder\u003c'a\u003e {\n fn generate_text(\u0026self) -\u003e String {\n format!(\n \"Version: 10\nType: Certification\nCurrency: {currency}\nIssuer: {issuer}\nIdtyIssuer: {target}\nIdtyUniqueID: {idty_uid}\nIdtyTimestamp: {idty_blockstamp}\nIdtySignature: {idty_sig}\nCertTimestamp: {blockstamp}\n\",\n currency = self.currency,\n issuer = self.issuer,\n target = self.target,\n idty_uid = self.identity_username,\n idty_blockstamp = self.identity_blockstamp,\n idty_sig = self.identity_sig,\n blockstamp = self.blockstamp,\n )\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use dup_crypto::keys::{PrivateKey, PublicKey, Signature};\n\n #[test]\n fn generate_real_document() {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR\")\n .unwrap(),\n );\n\n let prikey = PrivKey::Ed25519(ed25519::PrivateKey::from_base58(\n \"3XGWuuU1dQ7zaYPzE76ATfY71STzRkbT3t4DE1bSjMhYje81XdJFeXVG9uMPi3oDeRTosT2dmBAFH8VydrAUWXRZ\",\n ).unwrap());\n\n let sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"qfR6zqT1oJbqIsppOi64gC9yTtxb6g6XA9RYpulkq9ehMvqg2VYVigCbR0yVpqKFsnYiQTrnjgFuFRSJCJDfCw==\",\n ).unwrap());\n\n let target = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let identity_blockstamp = Blockstamp::from_string(\n \"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\",\n )\n .unwrap();\n\n let identity_sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\",\n ).unwrap());\n\n let blockstamp = Blockstamp::from_string(\n \"36-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B865\",\n )\n .unwrap();\n\n let builder = CertificationDocumentV10Builder {\n currency: \"duniter_unit_test_currency\",\n issuer: \u0026pubkey,\n target: \u0026target,\n identity_username: \"tic\",\n identity_blockstamp: \u0026identity_blockstamp,\n identity_sig: \u0026identity_sig,\n blockstamp: \u0026blockstamp,\n };\n\n assert!(builder\n .build_with_signature(vec![sig])\n .verify_signatures()\n .is_ok());\n\n assert!(builder\n .build_and_sign(vec![prikey])\n .verify_signatures()\n .is_ok());\n }\n\n #[test]\n fn certification_document() {\n let doc = \"Version: 10\nType: Certification\nCurrency: g1-test\nIssuer: 5B8iMAzq1dNmFe3ZxFTBQkqhq4fsztg1gZvxHXCk1XYH\nIdtyIssuer: mMPioknj2MQCX9KyKykdw8qMRxYR2w1u3UpdiEJHgXg\nIdtyUniqueID: mmpio\nIdtyTimestamp: 7543-000044410C5370DE8DBA911A358F318096B7A269CFC2BB93272E397CC513EA0A\nIdtySignature: SmSweUD4lEMwiZfY8ux9maBjrQQDkC85oMNsin6oSQCPdXG8sFCZ4FisUaWqKsfOlZVb/HNa+TKzD2t0Yte+DA==\nCertTimestamp: 167884-0001DFCA28002A8C96575E53B8CEF8317453A7B0BA255542CCF0EC8AB5E99038\nwqZxPEGxLrHGv8VdEIfUGvUcf+tDdNTMXjLzVRCQ4UhlhDRahOMjfcbP7byNYr5OfIl83S1MBxF7VJgu8YasCA==\";\n\n let doc = CertificationDocumentParser::parse(doc).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n /*assert_eq!(\n doc.generate_compact_text(),\n \"2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT:\\\n 7jzkd8GiFnpys4X7mP78w2Y3y3kwdK6fVSLEaojd3aH9:99956:\\\n Hkps1QU4HxIcNXKT8YmprYTVByBhPP1U2tIM7Z8wENzLKIWAvQClkAvBE7pW9dnVa18sJIJhVZUcRrPAZfmjBA==\"\n );*/\n }\n}\n","traces":[{"line":39,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":1}},{"line":42,"address":null,"length":0,"stats":{"Line":1}},{"line":43,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":165,"address":null,"length":0,"stats":{"Line":1}},{"line":166,"address":null,"length":0,"stats":{"Line":1}},{"line":167,"address":null,"length":0,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":169,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":5411144,"length":1,"stats":{"Line":1}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":5411381,"length":1,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":5411782,"length":1,"stats":{"Line":1}},{"line":183,"address":5411889,"length":1,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":188,"address":5412076,"length":1,"stats":{"Line":1}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":5412273,"length":1,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":1}},{"line":197,"address":null,"length":0,"stats":{"Line":1}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":199,"address":null,"length":0,"stats":{"Line":1}},{"line":200,"address":null,"length":0,"stats":{"Line":1}},{"line":201,"address":null,"length":0,"stats":{"Line":1}},{"line":202,"address":null,"length":0,"stats":{"Line":1}},{"line":203,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":1}},{"line":222,"address":null,"length":0,"stats":{"Line":1}},{"line":225,"address":null,"length":0,"stats":{"Line":1}},{"line":226,"address":null,"length":0,"stats":{"Line":1}},{"line":229,"address":null,"length":0,"stats":{"Line":1}},{"line":230,"address":null,"length":0,"stats":{"Line":1}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":234,"address":null,"length":0,"stats":{"Line":1}},{"line":241,"address":null,"length":0,"stats":{"Line":1}},{"line":242,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":1}},{"line":247,"address":null,"length":0,"stats":{"Line":1}},{"line":248,"address":null,"length":0,"stats":{"Line":1}},{"line":249,"address":null,"length":0,"stats":{"Line":1}},{"line":250,"address":null,"length":0,"stats":{"Line":1}},{"line":275,"address":null,"length":0,"stats":{"Line":1}},{"line":282,"address":null,"length":0,"stats":{"Line":1}},{"line":283,"address":null,"length":0,"stats":{"Line":1}},{"line":284,"address":null,"length":0,"stats":{"Line":1}},{"line":285,"address":null,"length":0,"stats":{"Line":1}},{"line":286,"address":null,"length":0,"stats":{"Line":1}},{"line":287,"address":null,"length":0,"stats":{"Line":1}},{"line":288,"address":null,"length":0,"stats":{"Line":1}},{"line":298,"address":null,"length":0,"stats":{"Line":1}},{"line":299,"address":null,"length":0,"stats":{"Line":1}},{"line":302,"address":null,"length":0,"stats":{"Line":1}},{"line":303,"address":null,"length":0,"stats":{"Line":1}},{"line":304,"address":null,"length":0,"stats":{"Line":1}},{"line":309,"address":null,"length":0,"stats":{"Line":1}},{"line":310,"address":null,"length":0,"stats":{"Line":1}},{"line":321,"address":null,"length":0,"stats":{"Line":1}},{"line":322,"address":null,"length":0,"stats":{"Line":1}},{"line":323,"address":null,"length":0,"stats":{"Line":1}},{"line":324,"address":null,"length":0,"stats":{"Line":1}},{"line":325,"address":null,"length":0,"stats":{"Line":1}},{"line":326,"address":null,"length":0,"stats":{"Line":1}},{"line":327,"address":null,"length":0,"stats":{"Line":1}},{"line":338,"address":4265344,"length":1,"stats":{"Line":2}},{"line":339,"address":4787680,"length":1,"stats":{"Line":1}},{"line":340,"address":4787614,"length":1,"stats":{"Line":1}},{"line":344,"address":4787737,"length":1,"stats":{"Line":1}},{"line":348,"address":4787835,"length":1,"stats":{"Line":1}},{"line":352,"address":4787986,"length":1,"stats":{"Line":1}},{"line":353,"address":4787935,"length":1,"stats":{"Line":1}},{"line":357,"address":4788058,"length":1,"stats":{"Line":1}},{"line":362,"address":4788107,"length":1,"stats":{"Line":1}},{"line":366,"address":4788205,"length":1,"stats":{"Line":1}},{"line":371,"address":4788263,"length":1,"stats":{"Line":1}},{"line":381,"address":4788504,"length":1,"stats":{"Line":1}},{"line":382,"address":4788380,"length":1,"stats":{"Line":1}},{"line":386,"address":4788803,"length":1,"stats":{"Line":1}},{"line":387,"address":4788686,"length":1,"stats":{"Line":1}},{"line":393,"address":4265376,"length":1,"stats":{"Line":2}},{"line":394,"address":4789102,"length":1,"stats":{"Line":1}},{"line":405,"address":4789116,"length":1,"stats":{"Line":1}},{"line":406,"address":4789179,"length":1,"stats":{"Line":1}},{"line":407,"address":4789349,"length":1,"stats":{"Line":1}}],"covered":94,"coverable":123},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","certification.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Certification documents.\n\npub mod v10;\n\npub use v10::{\n CertificationDocumentV10, CertificationDocumentV10Stringified, CompactCertificationDocumentV10,\n};\n\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::*;\n\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\nuse pest::Parser;\n\n/// Wrap an Certification document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub enum CertificationDocument {\n /// Certification document v10\n V10(CertificationDocumentV10),\n}\n\nimpl Document for CertificationDocument {\n type PublicKey = PubKey;\n\n #[inline]\n fn version(\u0026self) -\u003e u16 {\n match self {\n CertificationDocument::V10(_) =\u003e 10u16,\n }\n }\n\n #[inline]\n fn currency(\u0026self) -\u003e \u0026str {\n match self {\n CertificationDocument::V10(cert_v10) =\u003e cert_v10.currency(),\n }\n }\n\n #[inline]\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n match self {\n CertificationDocument::V10(cert_v10) =\u003e cert_v10.blockstamp(),\n }\n }\n\n #[inline]\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n match self {\n CertificationDocument::V10(cert_v10) =\u003e cert_v10.issuers(),\n }\n }\n\n #[inline]\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n match self {\n CertificationDocument::V10(cert_v10) =\u003e cert_v10.signatures(),\n }\n }\n\n #[inline]\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n match self {\n CertificationDocument::V10(cert_v10) =\u003e cert_v10.as_bytes(),\n }\n }\n}\n\n/// Certification document parser\n#[derive(Debug, Clone, Copy)]\npub struct CertificationDocumentParser;\n\nimpl TextDocumentParser\u003cRule\u003e for CertificationDocumentParser {\n type DocumentType = CertificationDocument;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match DocumentsParser::parse(Rule::cert, doc) {\n Ok(mut cert_pairs) =\u003e {\n let cert_pair = cert_pairs.next().unwrap(); // get and unwrap the `cert` rule; never fails\n Self::from_pest_pair(cert_pair)\n }\n Err(pest_error) =\u003e fatal_error!(\"{}\", pest_error), //Err(TextDocumentParseError::PestError()),\n }\n }\n fn from_pest_pair(cert_pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let cert_vx_pair = cert_pair.into_inner().next().unwrap(); // get and unwrap the `cert_vX` rule; never fails\n\n match cert_vx_pair.as_rule() {\n Rule::cert_v10 =\u003e Ok(CertificationDocumentParser::from_versioned_pest_pair(\n 10,\n cert_vx_pair,\n )?),\n _ =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"{:#?}\",\n cert_vx_pair.as_rule()\n ))),\n }\n }\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 10 =\u003e Ok(CertificationDocument::V10(\n CertificationDocumentV10::from_pest_pair(pair)?,\n )),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub enum CertificationDocumentStringified {\n V10(CertificationDocumentV10Stringified),\n}\n\nimpl ToStringObject for CertificationDocument {\n type StringObject = CertificationDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match self {\n CertificationDocument::V10(idty) =\u003e {\n CertificationDocumentStringified::V10(idty.to_string_object())\n }\n }\n }\n}\n","traces":[{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":79,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":4542777,"length":1,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":4542940,"length":1,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":4544794,"length":1,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":1}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}}],"covered":20,"coverable":44},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","identity","v10.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Identity documents V10.\n\nuse durs_common_tools::fatal_error;\n\nuse crate::documents::*;\nuse crate::text_document_traits::*;\nuse crate::Blockstamp;\n\n/// Wrap an Identity document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\npub struct IdentityDocumentV10 {\n /// Document as text.\n ///\n /// Is used to check signatures, and other values\n /// must be extracted from it.\n text: Option\u003cString\u003e,\n\n /// Currency.\n currency: String,\n /// Unique ID\n username: String,\n /// Blockstamp\n blockstamp: Blockstamp,\n /// Document issuer (there should be only one).\n issuers: Vec\u003cPubKey\u003e,\n /// Document signature (there should be only one).\n signatures: Vec\u003cSig\u003e,\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// identity document for jsonification\npub struct IdentityDocumentV10Stringified {\n /// Currency.\n pub currency: String,\n /// Unique ID\n pub username: String,\n /// Blockstamp\n pub blockstamp: String,\n /// Document issuer\n pub issuer: String,\n /// Document signature\n pub signature: String,\n}\n\nimpl ToStringObject for IdentityDocumentV10 {\n type StringObject = IdentityDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e IdentityDocumentV10Stringified {\n IdentityDocumentV10Stringified {\n currency: self.currency.clone(),\n username: self.username.clone(),\n blockstamp: format!(\"{}\", self.blockstamp),\n issuer: format!(\"{}\", self.issuers[0]),\n signature: format!(\"{}\", self.signatures[0]),\n }\n }\n}\n\nimpl IdentityDocumentV10 {\n /// Unique ID\n pub fn username(\u0026self) -\u003e \u0026str {\n \u0026self.username\n }\n\n /// Lightens the membership (for example to store it while minimizing the space required)\n pub fn reduce(\u0026mut self) {\n self.text = None;\n }\n /// From pest parser pair\n pub fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cIdentityDocumentV10, TextDocumentParseError\u003e {\n let doc = pair.as_str();\n let mut currency = \"\";\n let mut pubkey_str = \"\";\n let mut uid = \"\";\n let mut blockstamp = Blockstamp::default();\n let mut sig_str = \"\";\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency = field.as_str(),\n Rule::pubkey =\u003e pubkey_str = field.as_str(),\n Rule::uid =\u003e uid = field.as_str(),\n Rule::blockstamp =\u003e {\n let mut inner_rules = field.into_inner(); // { integer ~ \"-\" ~ hash }\n\n let block_id: \u0026str = inner_rules.next().unwrap().as_str();\n let block_hash: \u0026str = inner_rules.next().unwrap().as_str();\n blockstamp = Blockstamp {\n id: BlockNumber(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.\n hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.\n };\n }\n Rule::ed25519_sig =\u003e sig_str = field.as_str(),\n Rule::EOI =\u003e (),\n _ =\u003e fatal_error!(\"unexpected rule\"), // Grammar ensures that we never reach this line\n }\n }\n\n Ok(IdentityDocumentV10 {\n text: Some(doc.to_owned()),\n currency: currency.to_owned(),\n username: uid.to_owned(),\n blockstamp,\n issuers: vec![PubKey::Ed25519(\n ed25519::PublicKey::from_base58(pubkey_str).unwrap(),\n )], // Grammar ensures that we have a base58 string.\n signatures: vec![Sig::Ed25519(\n ed25519::Signature::from_base64(sig_str).unwrap(),\n )], // Grammar ensures that we have a base64 string.\n })\n }\n}\n\nimpl Document for IdentityDocumentV10 {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n 10\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n \u0026self.currency\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n self.blockstamp\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n self.as_text_without_signature().as_bytes()\n }\n}\n\n/// CompactIdentityDocumentV10\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\npub struct CompactIdentityDocumentV10 {\n /// Unique ID\n username: String,\n /// Blockstamp\n blockstamp: Blockstamp,\n /// Document issuer\n pubkey: PubKey,\n /// Document signature\n signature: Sig,\n}\n\nimpl CompactTextDocument for CompactIdentityDocumentV10 {\n fn as_compact_text(\u0026self) -\u003e String {\n format!(\n \"{issuer}:{signature}:{blockstamp}:{username}\",\n issuer = self.pubkey,\n signature = self.signature,\n blockstamp = self.blockstamp,\n username = self.username,\n )\n }\n}\n\nimpl TextDocument for IdentityDocumentV10 {\n type CompactTextDocument_ = CompactIdentityDocumentV10;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n if let Some(ref text) = self.text {\n text\n } else {\n fatal_error!(\"Try to get text of reduce identity !\")\n }\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n CompactIdentityDocumentV10 {\n username: self.username.clone(),\n blockstamp: self.blockstamp,\n pubkey: self.issuers[0],\n signature: self.signatures[0],\n }\n }\n}\n\n/// Identity document builder.\n#[derive(Debug, Copy, Clone)]\npub struct IdentityDocumentV10Builder\u003c'a\u003e {\n /// Document currency.\n pub currency: \u0026'a str,\n /// Identity unique id.\n pub username: \u0026'a str,\n /// Reference blockstamp.\n pub blockstamp: \u0026'a Blockstamp,\n /// Document/identity issuer.\n pub issuer: \u0026'a PubKey,\n}\n\nimpl\u003c'a\u003e IdentityDocumentV10Builder\u003c'a\u003e {\n fn build_with_text_and_sigs(self, text: String, signatures: Vec\u003cSig\u003e) -\u003e IdentityDocumentV10 {\n IdentityDocumentV10 {\n text: Some(text),\n currency: self.currency.to_string(),\n username: self.username.to_string(),\n blockstamp: *self.blockstamp,\n issuers: vec![*self.issuer],\n signatures,\n }\n }\n}\n\nimpl\u003c'a\u003e DocumentBuilder for IdentityDocumentV10Builder\u003c'a\u003e {\n type Document = IdentityDocumentV10;\n type PrivateKey = PrivKey;\n\n fn build_with_signature(\u0026self, signatures: Vec\u003cSig\u003e) -\u003e IdentityDocumentV10 {\n self.build_with_text_and_sigs(self.generate_text(), signatures)\n }\n\n fn build_and_sign(\u0026self, private_keys: Vec\u003cPrivKey\u003e) -\u003e IdentityDocumentV10 {\n let (text, signatures) = self.build_signed_text(private_keys);\n self.build_with_text_and_sigs(text, signatures)\n }\n}\n\nimpl\u003c'a\u003e TextDocumentBuilder for IdentityDocumentV10Builder\u003c'a\u003e {\n fn generate_text(\u0026self) -\u003e String {\n format!(\n \"Version: 10\nType: Identity\nCurrency: {currency}\nIssuer: {issuer}\nUniqueID: {username}\nTimestamp: {blockstamp}\n\",\n currency = self.currency,\n issuer = self.issuer,\n username = self.username,\n blockstamp = self.blockstamp\n )\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::Document;\n use dup_crypto::keys::{PrivateKey, PublicKey, Signature};\n\n #[test]\n fn generate_real_document() {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let prikey = PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\\\n iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7\",\n )\n .unwrap(),\n );\n\n let sig = Sig::Ed25519(\n ed25519::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGM\\\n MmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\",\n )\n .unwrap(),\n );\n\n let block = Blockstamp::from_string(\n \"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\",\n )\n .unwrap();\n\n let builder = IdentityDocumentV10Builder {\n currency: \"duniter_unit_test_currency\",\n username: \"tic\",\n blockstamp: \u0026block,\n issuer: \u0026pubkey,\n };\n\n assert!(builder\n .build_with_signature(vec![sig])\n .verify_signatures()\n .is_ok());\n assert!(builder\n .build_and_sign(vec![prikey])\n .verify_signatures()\n .is_ok());\n }\n\n #[test]\n fn parse_identity_document() {\n let doc = \"Version: 10\nType: Identity\nCurrency: duniter_unit_test_currency\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nUniqueID: tic\nTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\";\n\n let doc = IdentityDocumentParser::parse(doc).expect(\"Fail to parse idty doc !\");\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok())\n }\n}\n","traces":[{"line":65,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":4553995,"length":1,"stats":{"Line":1}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":4554396,"length":1,"stats":{"Line":1}},{"line":106,"address":4554503,"length":1,"stats":{"Line":1}},{"line":109,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":4554735,"length":1,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":1}},{"line":116,"address":null,"length":0,"stats":{"Line":1}},{"line":117,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":1}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":123,"address":null,"length":0,"stats":{"Line":1}},{"line":124,"address":null,"length":0,"stats":{"Line":1}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":1}},{"line":142,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":2}},{"line":146,"address":null,"length":0,"stats":{"Line":2}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":188,"address":null,"length":0,"stats":{"Line":1}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":197,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":2}},{"line":220,"address":null,"length":0,"stats":{"Line":2}},{"line":221,"address":null,"length":0,"stats":{"Line":2}},{"line":222,"address":null,"length":0,"stats":{"Line":2}},{"line":223,"address":null,"length":0,"stats":{"Line":2}},{"line":224,"address":null,"length":0,"stats":{"Line":2}},{"line":234,"address":null,"length":0,"stats":{"Line":2}},{"line":235,"address":null,"length":0,"stats":{"Line":2}},{"line":238,"address":null,"length":0,"stats":{"Line":1}},{"line":239,"address":null,"length":0,"stats":{"Line":1}},{"line":240,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":2}},{"line":246,"address":null,"length":0,"stats":{"Line":2}},{"line":254,"address":null,"length":0,"stats":{"Line":2}},{"line":255,"address":null,"length":0,"stats":{"Line":2}},{"line":256,"address":null,"length":0,"stats":{"Line":2}},{"line":257,"address":null,"length":0,"stats":{"Line":2}},{"line":269,"address":5461504,"length":1,"stats":{"Line":2}},{"line":270,"address":5461584,"length":1,"stats":{"Line":1}},{"line":271,"address":5461518,"length":1,"stats":{"Line":1}},{"line":275,"address":5461690,"length":1,"stats":{"Line":1}},{"line":276,"address":5461641,"length":1,"stats":{"Line":1}},{"line":283,"address":5461790,"length":1,"stats":{"Line":1}},{"line":284,"address":5461739,"length":1,"stats":{"Line":1}},{"line":291,"address":5461839,"length":1,"stats":{"Line":1}},{"line":296,"address":5461897,"length":1,"stats":{"Line":1}},{"line":303,"address":5462090,"length":1,"stats":{"Line":1}},{"line":304,"address":5461966,"length":1,"stats":{"Line":1}},{"line":307,"address":5462389,"length":1,"stats":{"Line":1}},{"line":308,"address":5462272,"length":1,"stats":{"Line":1}},{"line":314,"address":5462672,"length":1,"stats":{"Line":2}},{"line":315,"address":5462686,"length":1,"stats":{"Line":1}},{"line":323,"address":5462700,"length":1,"stats":{"Line":1}},{"line":324,"address":5462778,"length":1,"stats":{"Line":1}},{"line":325,"address":5462948,"length":1,"stats":{"Line":1}}],"covered":74,"coverable":104},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","identity.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Identity documents.\n\npub mod v10;\n\npub use v10::{IdentityDocumentV10, IdentityDocumentV10Stringified};\n\nuse crate::documents::*;\n\n/// Identity document\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\npub enum IdentityDocument {\n /// Identity document V10\n V10(IdentityDocumentV10),\n}\n\nimpl Document for IdentityDocument {\n type PublicKey = PubKey;\n\n #[inline]\n fn version(\u0026self) -\u003e u16 {\n match self {\n IdentityDocument::V10(_) =\u003e 10u16,\n }\n }\n\n #[inline]\n fn currency(\u0026self) -\u003e \u0026str {\n match self {\n IdentityDocument::V10(idty_v10) =\u003e idty_v10.currency(),\n }\n }\n\n #[inline]\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n match self {\n IdentityDocument::V10(idty_v10) =\u003e idty_v10.blockstamp(),\n }\n }\n\n #[inline]\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n match self {\n IdentityDocument::V10(idty_v10) =\u003e idty_v10.issuers(),\n }\n }\n\n #[inline]\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n match self {\n IdentityDocument::V10(idty_v10) =\u003e idty_v10.signatures(),\n }\n }\n\n #[inline]\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n match self {\n IdentityDocument::V10(idty_v10) =\u003e idty_v10.as_bytes(),\n }\n }\n}\n\n/// Identity document parser\n#[derive(Debug, Clone, Copy)]\npub struct IdentityDocumentParser;\n\nimpl TextDocumentParser\u003cRule\u003e for IdentityDocumentParser {\n type DocumentType = IdentityDocument;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let mut doc_pairs = DocumentsParser::parse(Rule::idty, doc)?;\n let idty_pair = doc_pairs.next().unwrap(); // get and unwrap the `idty` rule; never fails\n Self::from_pest_pair(idty_pair)\n }\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let idty_vx_pair = pair.into_inner().next().unwrap(); // get and unwrap the `idty_vx` rule; never fails\n\n match idty_vx_pair.as_rule() {\n Rule::idty_v10 =\u003e Ok(Self::from_versioned_pest_pair(10, idty_vx_pair)?),\n _ =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"{:#?}\",\n idty_vx_pair.as_rule()\n ))),\n }\n }\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 10 =\u003e Ok(IdentityDocument::V10(IdentityDocumentV10::from_pest_pair(\n pair,\n )?)),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub enum IdentityDocumentStringified {\n V10(IdentityDocumentV10Stringified),\n}\n\nimpl ToStringObject for IdentityDocument {\n type StringObject = IdentityDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match self {\n IdentityDocument::V10(idty) =\u003e {\n IdentityDocumentStringified::V10(idty.to_string_object())\n }\n }\n }\n}\n","traces":[{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":36,"address":null,"length":0,"stats":{"Line":0}},{"line":37,"address":null,"length":0,"stats":{"Line":0}},{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":1}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":1}},{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":70,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":84,"address":null,"length":0,"stats":{"Line":1}},{"line":85,"address":null,"length":0,"stats":{"Line":1}},{"line":86,"address":4894531,"length":1,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":4894762,"length":1,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":1}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}}],"covered":18,"coverable":40},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","membership","v10.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Membership documents v10.\n\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\n\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::*;\nuse crate::text_document_traits::*;\n\n/// Type of a Membership.\n#[derive(Debug, Deserialize, Clone, Copy, Hash, Serialize, PartialEq, Eq)]\npub enum MembershipType {\n /// The member wishes to opt-in.\n In(),\n /// The member wishes to opt-out.\n Out(),\n}\n\n/// Wrap an Membership document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]\npub struct MembershipDocumentV10 {\n /// Document as text.\n ///\n /// Is used to check signatures, and other values mut be extracted from it.\n text: Option\u003cString\u003e,\n\n /// Name of the currency.\n currency: String,\n /// Document issuer (there should be only one).\n issuers: Vec\u003cPubKey\u003e,\n /// Blockstamp\n blockstamp: Blockstamp,\n /// Membership message.\n membership: MembershipType,\n /// Identity to use for this public key.\n identity_username: String,\n /// Identity document blockstamp.\n identity_blockstamp: Blockstamp,\n /// Document signature (there should be only one).\n signatures: Vec\u003cSig\u003e,\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// identity document for jsonification\npub struct MembershipDocumentV10Stringified {\n /// Currency.\n pub currency: String,\n /// Document issuer\n pub issuer: String,\n /// Blockstamp\n pub blockstamp: String,\n /// Membership message.\n pub membership: String,\n /// Unique ID\n pub username: String,\n /// Identity document blockstamp.\n pub identity_blockstamp: String,\n /// Document signature\n pub signature: String,\n}\n\nimpl ToStringObject for MembershipDocumentV10 {\n type StringObject = MembershipDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e MembershipDocumentV10Stringified {\n MembershipDocumentV10Stringified {\n currency: self.currency.clone(),\n issuer: format!(\"{}\", self.issuers[0]),\n blockstamp: format!(\"{}\", self.blockstamp),\n membership: match self.membership {\n MembershipType::In() =\u003e \"IN\".to_owned(),\n MembershipType::Out() =\u003e \"OUT\".to_owned(),\n },\n username: self.identity_username.clone(),\n identity_blockstamp: format!(\"{}\", self.identity_blockstamp),\n signature: format!(\"{}\", self.signatures[0]),\n }\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Hash, Deserialize, Serialize)]\n/// Membership event type (blockchain event)\npub enum MembershipEventType {\n /// Newcomer\n Join(),\n /// Renewal\n Renewal(),\n /// Renewal after expire or leave\n Rejoin(),\n /// Expire\n Expire(),\n}\n\n#[derive(Debug, Clone, PartialEq, Hash, Deserialize, Serialize)]\n/// Membership event (blockchain event)\npub struct MembershipEvent {\n /// Blockstamp of block event\n pub blockstamp: Blockstamp,\n /// Membership document\n pub doc: MembershipDocumentV10,\n /// Event type\n pub event_type: MembershipEventType,\n /// Chainable time\n pub chainable_on: u64,\n}\n\nimpl MembershipDocumentV10 {\n /// Membership message.\n pub fn membership(\u0026self) -\u003e MembershipType {\n self.membership\n }\n\n /// Identity to use for this public key.\n pub fn identity_username(\u0026self) -\u003e \u0026str {\n \u0026self.identity_username\n }\n\n /// Lightens the membership (for example to store it while minimizing the space required)\n pub fn reduce(\u0026mut self) {\n self.text = None;\n }\n\n /// From pest parser pair\n pub fn from_pest_pair(\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cMembershipDocumentV10, TextDocumentParseError\u003e {\n let doc = pair.as_str();\n let mut currency = \"\";\n let mut pubkey_str = \"\";\n let mut uid = \"\";\n let mut blockstamps = Vec::with_capacity(2);\n let mut membership = MembershipType::In();\n let mut sig_str = \"\";\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency = field.as_str(),\n Rule::uid =\u003e uid = field.as_str(),\n Rule::pubkey =\u003e pubkey_str = field.as_str(),\n Rule::membership_in =\u003e membership = MembershipType::In(),\n Rule::membership_out =\u003e membership = MembershipType::Out(),\n Rule::blockstamp =\u003e {\n let mut inner_rules = field.into_inner(); // { integer ~ \"-\" ~ hash }\n\n let block_id: \u0026str = inner_rules.next().unwrap().as_str();\n let block_hash: \u0026str = inner_rules.next().unwrap().as_str();\n blockstamps.push(Blockstamp {\n id: BlockNumber(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.\n hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.\n });\n }\n Rule::ed25519_sig =\u003e sig_str = field.as_str(),\n Rule::EOI =\u003e (),\n _ =\u003e fatal_error!(\"unexpected rule\"), // Grammar ensures that we never reach this line\n }\n }\n\n Ok(MembershipDocumentV10 {\n text: Some(doc.to_owned()),\n issuers: vec![PubKey::Ed25519(\n ed25519::PublicKey::from_base58(pubkey_str).unwrap(),\n )], // Grammar ensures that we have a base58 string.\n currency: currency.to_owned(),\n blockstamp: blockstamps[0],\n membership,\n identity_username: uid.to_owned(),\n identity_blockstamp: blockstamps[1],\n signatures: vec![Sig::Ed25519(\n ed25519::Signature::from_base64(sig_str).unwrap(),\n )], // Grammar ensures that we have a base64 string.\n })\n }\n}\n\nimpl Document for MembershipDocumentV10 {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n 10\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n \u0026self.currency\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n self.blockstamp\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n self.as_text_without_signature().as_bytes()\n }\n}\n\nimpl CompactTextDocument for MembershipDocumentV10 {\n fn as_compact_text(\u0026self) -\u003e String {\n format!(\n \"{issuer}:{signature}:{blockstamp}:{idty_blockstamp}:{username}\",\n issuer = self.issuers[0],\n signature = self.signatures[0],\n blockstamp = self.blockstamp,\n idty_blockstamp = self.identity_blockstamp,\n username = self.identity_username,\n )\n }\n}\n\n/// CompactPoolMembershipDoc\n#[derive(Copy, Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\npub struct CompactPoolMembershipDoc {\n /// Document creation blockstamp\n pub blockstamp: Blockstamp,\n /// Signature\n pub signature: Sig,\n}\n\nimpl TextDocument for MembershipDocumentV10 {\n type CompactTextDocument_ = MembershipDocumentV10;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n if let Some(ref text) = self.text {\n text\n } else {\n fatal_error!(\"Try to get text of reduce membership !\")\n }\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n self.clone()\n }\n}\n\n/// Membership document builder.\n#[derive(Debug, Copy, Clone)]\npub struct MembershipDocumentV10Builder\u003c'a\u003e {\n /// Document currency.\n pub currency: \u0026'a str,\n /// Document/identity issuer.\n pub issuer: \u0026'a PubKey,\n /// Reference blockstamp.\n pub blockstamp: \u0026'a Blockstamp,\n /// Membership message.\n pub membership: MembershipType,\n /// Identity username.\n pub identity_username: \u0026'a str,\n /// Identity document blockstamp.\n pub identity_blockstamp: \u0026'a Blockstamp,\n}\n\nimpl\u003c'a\u003e MembershipDocumentV10Builder\u003c'a\u003e {\n fn build_with_text_and_sigs(self, text: String, signatures: Vec\u003cSig\u003e) -\u003e MembershipDocumentV10 {\n MembershipDocumentV10 {\n text: Some(text),\n currency: self.currency.to_string(),\n issuers: vec![*self.issuer],\n blockstamp: *self.blockstamp,\n membership: self.membership,\n identity_username: self.identity_username.to_string(),\n identity_blockstamp: *self.identity_blockstamp,\n signatures,\n }\n }\n}\n\nimpl\u003c'a\u003e DocumentBuilder for MembershipDocumentV10Builder\u003c'a\u003e {\n type Document = MembershipDocumentV10;\n type PrivateKey = PrivKey;\n\n fn build_with_signature(\u0026self, signatures: Vec\u003cSig\u003e) -\u003e MembershipDocumentV10 {\n self.build_with_text_and_sigs(self.generate_text(), signatures)\n }\n\n fn build_and_sign(\u0026self, private_keys: Vec\u003cPrivKey\u003e) -\u003e MembershipDocumentV10 {\n let (text, signatures) = self.build_signed_text(private_keys);\n self.build_with_text_and_sigs(text, signatures)\n }\n}\n\nimpl\u003c'a\u003e TextDocumentBuilder for MembershipDocumentV10Builder\u003c'a\u003e {\n fn generate_text(\u0026self) -\u003e String {\n format!(\n \"Version: 10\nType: Membership\nCurrency: {currency}\nIssuer: {issuer}\nBlock: {blockstamp}\nMembership: {membership}\nUserID: {username}\nCertTS: {ity_blockstamp}\n\",\n currency = self.currency,\n issuer = self.issuer,\n blockstamp = self.blockstamp,\n membership = match self.membership {\n MembershipType::In() =\u003e \"IN\",\n MembershipType::Out() =\u003e \"OUT\",\n },\n username = self.identity_username,\n ity_blockstamp = self.identity_blockstamp,\n )\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use dup_crypto::keys::{PrivateKey, PublicKey, Signature};\n\n #[test]\n fn generate_real_document() {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let prikey = PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\\\n iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7\",\n )\n .unwrap(),\n );\n\n let sig = Sig::Ed25519(\n ed25519::Signature::from_base64(\n \"s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkEl\\\n AyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==\",\n )\n .unwrap(),\n );\n\n let block = Blockstamp::from_string(\n \"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\",\n )\n .unwrap();\n\n let builder = MembershipDocumentV10Builder {\n currency: \"duniter_unit_test_currency\",\n issuer: \u0026pubkey,\n blockstamp: \u0026block,\n membership: MembershipType::In(),\n identity_username: \"tic\",\n identity_blockstamp: \u0026block,\n };\n\n assert!(builder\n .build_with_signature(vec![sig])\n .verify_signatures()\n .is_ok());\n assert!(builder\n .build_and_sign(vec![prikey])\n .verify_signatures()\n .is_ok());\n }\n\n #[test]\n fn membership_identity_document() {\n let doc = \"Version: 10\nType: Membership\nCurrency: duniter_unit_test_currency\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nBlock: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nMembership: IN\nUserID: tic\nCertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\ns2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==\";\n\n let doc = MembershipDocumentParser::parse(doc).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n assert_eq!(\n doc.generate_compact_text(),\n \"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:\\\n s2hUbokkibTAWGEwErw6hyXSWlWFQ2UWs2PWx8d/kkElAyuuWaQq4Tsonuweh1xn4AC1TVWt4yMR3WrDdkhnAw==:\\\n 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\\\n 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:\\\n tic\"\n );\n }\n}\n","traces":[{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":141,"address":null,"length":0,"stats":{"Line":1}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":148,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":5472872,"length":1,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":5473234,"length":1,"stats":{"Line":1}},{"line":165,"address":5473333,"length":1,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":5473576,"length":1,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":175,"address":null,"length":0,"stats":{"Line":1}},{"line":176,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":1}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":206,"address":null,"length":0,"stats":{"Line":1}},{"line":207,"address":null,"length":0,"stats":{"Line":1}},{"line":210,"address":null,"length":0,"stats":{"Line":1}},{"line":211,"address":null,"length":0,"stats":{"Line":1}},{"line":214,"address":null,"length":0,"stats":{"Line":1}},{"line":215,"address":null,"length":0,"stats":{"Line":1}},{"line":220,"address":null,"length":0,"stats":{"Line":1}},{"line":221,"address":null,"length":0,"stats":{"Line":1}},{"line":223,"address":null,"length":0,"stats":{"Line":1}},{"line":224,"address":null,"length":0,"stats":{"Line":1}},{"line":225,"address":null,"length":0,"stats":{"Line":1}},{"line":226,"address":null,"length":0,"stats":{"Line":1}},{"line":227,"address":null,"length":0,"stats":{"Line":1}},{"line":244,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":1}},{"line":246,"address":null,"length":0,"stats":{"Line":1}},{"line":247,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":252,"address":null,"length":0,"stats":{"Line":1}},{"line":253,"address":null,"length":0,"stats":{"Line":1}},{"line":275,"address":null,"length":0,"stats":{"Line":1}},{"line":277,"address":null,"length":0,"stats":{"Line":1}},{"line":278,"address":null,"length":0,"stats":{"Line":1}},{"line":279,"address":null,"length":0,"stats":{"Line":1}},{"line":280,"address":null,"length":0,"stats":{"Line":1}},{"line":281,"address":null,"length":0,"stats":{"Line":1}},{"line":282,"address":null,"length":0,"stats":{"Line":1}},{"line":283,"address":null,"length":0,"stats":{"Line":1}},{"line":293,"address":null,"length":0,"stats":{"Line":1}},{"line":294,"address":null,"length":0,"stats":{"Line":1}},{"line":297,"address":null,"length":0,"stats":{"Line":1}},{"line":298,"address":null,"length":0,"stats":{"Line":1}},{"line":299,"address":null,"length":0,"stats":{"Line":1}},{"line":304,"address":null,"length":0,"stats":{"Line":1}},{"line":305,"address":null,"length":0,"stats":{"Line":1}},{"line":315,"address":null,"length":0,"stats":{"Line":1}},{"line":316,"address":null,"length":0,"stats":{"Line":1}},{"line":317,"address":null,"length":0,"stats":{"Line":1}},{"line":318,"address":null,"length":0,"stats":{"Line":1}},{"line":319,"address":null,"length":0,"stats":{"Line":1}},{"line":320,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":1}},{"line":323,"address":null,"length":0,"stats":{"Line":1}},{"line":334,"address":5788464,"length":1,"stats":{"Line":2}},{"line":335,"address":5805376,"length":1,"stats":{"Line":1}},{"line":336,"address":5805310,"length":1,"stats":{"Line":1}},{"line":340,"address":5805482,"length":1,"stats":{"Line":1}},{"line":341,"address":5805433,"length":1,"stats":{"Line":1}},{"line":348,"address":5805582,"length":1,"stats":{"Line":1}},{"line":349,"address":5805531,"length":1,"stats":{"Line":1}},{"line":356,"address":5805631,"length":1,"stats":{"Line":1}},{"line":361,"address":5805697,"length":1,"stats":{"Line":1}},{"line":365,"address":5805689,"length":1,"stats":{"Line":1}},{"line":370,"address":5805919,"length":1,"stats":{"Line":1}},{"line":371,"address":5805791,"length":1,"stats":{"Line":1}},{"line":374,"address":5806218,"length":1,"stats":{"Line":1}},{"line":375,"address":5806101,"length":1,"stats":{"Line":1}},{"line":381,"address":5788496,"length":1,"stats":{"Line":2}},{"line":382,"address":5806510,"length":1,"stats":{"Line":1}},{"line":392,"address":5806524,"length":1,"stats":{"Line":1}},{"line":393,"address":5806587,"length":1,"stats":{"Line":1}},{"line":394,"address":5806757,"length":1,"stats":{"Line":1}},{"line":395,"address":5806894,"length":1,"stats":{"Line":1}},{"line":396,"address":5806879,"length":1,"stats":{"Line":1}}],"covered":93,"coverable":120},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","membership.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Membership documents.\n\npub mod v10;\n\npub use v10::{MembershipDocumentV10, MembershipDocumentV10Stringified};\n\nuse crate::documents::*;\nuse crate::text_document_traits::{CompactTextDocument, TextDocument};\n\n/// Wrap an Membership document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]\npub enum MembershipDocument {\n V10(MembershipDocumentV10),\n}\n\nimpl Document for MembershipDocument {\n type PublicKey = PubKey;\n\n #[inline]\n fn version(\u0026self) -\u003e u16 {\n match self {\n MembershipDocument::V10(_) =\u003e 10u16,\n }\n }\n\n #[inline]\n fn currency(\u0026self) -\u003e \u0026str {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.currency(),\n }\n }\n\n #[inline]\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.blockstamp(),\n }\n }\n\n #[inline]\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.issuers(),\n }\n }\n\n #[inline]\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.signatures(),\n }\n }\n\n #[inline]\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.as_bytes(),\n }\n }\n}\n\nimpl CompactTextDocument for MembershipDocument {\n fn as_compact_text(\u0026self) -\u003e String {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.as_compact_text(),\n }\n }\n}\n\nimpl TextDocument for MembershipDocument {\n type CompactTextDocument_ = MembershipDocument;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e ms_v10.as_text(),\n }\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n match self {\n MembershipDocument::V10(ms_v10) =\u003e {\n MembershipDocument::V10(ms_v10.to_compact_document())\n }\n }\n }\n}\n\n/// Membership document parser\n#[derive(Debug, Clone, Copy)]\npub struct MembershipDocumentParser;\n\nimpl TextDocumentParser\u003cRule\u003e for MembershipDocumentParser {\n type DocumentType = MembershipDocument;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let mut ms_pairs = DocumentsParser::parse(Rule::membership, doc)?;\n let ms_pair = ms_pairs.next().unwrap(); // get and unwrap the `membership` rule; never fails\n Self::from_pest_pair(ms_pair)\n }\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let ms_vx_pair = pair.into_inner().next().unwrap(); // get and unwrap the `membership_vX` rule; never fails\n\n match ms_vx_pair.as_rule() {\n Rule::membership_v10 =\u003e Ok(Self::from_versioned_pest_pair(10, ms_vx_pair)?),\n _ =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"{:#?}\",\n ms_vx_pair.as_rule()\n ))),\n }\n }\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 10 =\u003e Ok(MembershipDocument::V10(\n MembershipDocumentV10::from_pest_pair(pair)?,\n )),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub enum MembershipDocumentStringified {\n V10(MembershipDocumentV10Stringified),\n}\n\nimpl ToStringObject for MembershipDocument {\n type StringObject = MembershipDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match self {\n MembershipDocument::V10(idty) =\u003e {\n MembershipDocumentStringified::V10(idty.to_string_object())\n }\n }\n }\n}\n","traces":[{"line":37,"address":null,"length":0,"stats":{"Line":0}},{"line":38,"address":null,"length":0,"stats":{"Line":0}},{"line":39,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":1}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":1}},{"line":114,"address":5794083,"length":1,"stats":{"Line":1}},{"line":115,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":1}},{"line":119,"address":5794314,"length":1,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":1}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":1}},{"line":134,"address":null,"length":0,"stats":{"Line":1}},{"line":135,"address":null,"length":0,"stats":{"Line":1}},{"line":136,"address":null,"length":0,"stats":{"Line":1}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}}],"covered":23,"coverable":50},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Implements the Dunitrust blockchain Documents.\n\nuse crate::documents::block::*;\nuse crate::documents::certification::*;\nuse crate::documents::identity::*;\nuse crate::documents::membership::*;\nuse crate::documents::revocation::*;\nuse crate::documents::transaction::*;\nuse crate::Rule;\nuse crate::*;\n\nuse durs_common_tools::fatal_error;\nuse pest::iterators::Pair;\nuse pest::Parser;\n\npub mod block;\npub mod certification;\npub mod identity;\npub mod membership;\npub mod revocation;\npub mod transaction;\n\n/// Document of DUBP (DUniter Blockhain Protocol)\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum DocumentDUBP {\n /// Block document.\n Block(Box\u003cBlockDocument\u003e),\n /// User document of DUBP (DUniter Blockhain Protocol)\n UserDocument(UserDocumentDUBP),\n}\n\n/// User document of DUBP (DUniter Blockhain Protocol)\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum UserDocumentDUBP {\n /// Transaction document.\n Transaction(Box\u003cTransactionDocument\u003e),\n\n /// Identity document.\n Identity(IdentityDocument),\n\n /// Membership document.\n Membership(MembershipDocument),\n\n /// Certification document.\n Certification(Box\u003cCertificationDocument\u003e),\n\n /// Revocation document.\n Revocation(Box\u003cRevocationDocument\u003e),\n}\n\n/// List of stringified document types.\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum DocumentDUBPStr {\n /// Block document (not yet implemented)\n Block(Box\u003cBlockDocumentStringified\u003e),\n /// Stringified user document.\n UserDocument(UserDocumentDUBPStr),\n}\n\n/// List of stringified user document types.\n#[derive(Debug, Clone, Serialize, Deserialize)]\npub enum UserDocumentDUBPStr {\n /// Transaction document.\n Transaction(Box\u003cTransactionDocumentStringified\u003e),\n\n /// Identity document.\n Identity(IdentityDocumentStringified),\n\n /// Membership document.\n Membership(MembershipDocumentStringified),\n\n /// Certification document.\n Certification(Box\u003cCertificationDocumentStringified\u003e),\n\n /// Revocation document.\n Revocation(Box\u003cRevocationDocumentStringified\u003e),\n}\n\nimpl ToStringObject for DocumentDUBP {\n type StringObject = DocumentDUBPStr;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match *self {\n DocumentDUBP::Block(ref doc) =\u003e {\n DocumentDUBPStr::Block(Box::new(doc.to_string_object()))\n }\n DocumentDUBP::UserDocument(ref user_doc) =\u003e {\n DocumentDUBPStr::UserDocument(user_doc.to_string_object())\n }\n }\n }\n}\n\nimpl ToStringObject for UserDocumentDUBP {\n type StringObject = UserDocumentDUBPStr;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match *self {\n UserDocumentDUBP::Identity(ref doc) =\u003e {\n UserDocumentDUBPStr::Identity(doc.to_string_object())\n }\n UserDocumentDUBP::Membership(ref doc) =\u003e {\n UserDocumentDUBPStr::Membership(doc.to_string_object())\n }\n UserDocumentDUBP::Certification(ref doc) =\u003e {\n UserDocumentDUBPStr::Certification(Box::new(doc.to_string_object()))\n }\n UserDocumentDUBP::Revocation(ref doc) =\u003e {\n UserDocumentDUBPStr::Revocation(Box::new(doc.to_string_object()))\n }\n UserDocumentDUBP::Transaction(ref doc) =\u003e {\n UserDocumentDUBPStr::Transaction(Box::new(doc.to_string_object()))\n }\n }\n }\n}\n\nimpl TextDocumentParser\u003cRule\u003e for UserDocumentDUBP {\n type DocumentType = UserDocumentDUBP;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cUserDocumentDUBP, TextDocumentParseError\u003e {\n match DocumentsParser::parse(Rule::document, doc) {\n Ok(mut doc_pairs) =\u003e Ok(UserDocumentDUBP::from_pest_pair(doc_pairs.next().unwrap())?), // get and unwrap the `document` rule; never fails\n Err(pest_error) =\u003e Err(pest_error.into()),\n }\n }\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let doc_vx_pair = pair.into_inner().next().unwrap(); // get and unwrap the `document_vX` rule; never fails\n\n match doc_vx_pair.as_rule() {\n Rule::document_v10 =\u003e Ok(UserDocumentDUBP::from_versioned_pest_pair(10, doc_vx_pair)?),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", doc_vx_pair.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 10 =\u003e Ok(UserDocumentDUBP::from_pest_pair_v10(pair)?),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\nimpl UserDocumentDUBP {\n pub fn from_pest_pair_v10(\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cUserDocumentDUBP, TextDocumentParseError\u003e {\n let doc_type_v10_pair = pair.into_inner().next().unwrap(); // get and unwrap the `{DOC_TYPE}_v10` rule; never fails\n\n match doc_type_v10_pair.as_rule() {\n Rule::idty_v10 =\u003e Ok(UserDocumentDUBP::Identity(IdentityDocument::V10(\n IdentityDocumentV10::from_pest_pair(doc_type_v10_pair)?,\n ))),\n Rule::membership_v10 =\u003e Ok(UserDocumentDUBP::Membership(MembershipDocument::V10(\n MembershipDocumentV10::from_pest_pair(doc_type_v10_pair)?,\n ))),\n Rule::cert_v10 =\u003e Ok(UserDocumentDUBP::Certification(Box::new(\n CertificationDocument::V10(CertificationDocumentV10::from_pest_pair(\n doc_type_v10_pair,\n )?),\n ))),\n Rule::revoc_v10 =\u003e Ok(UserDocumentDUBP::Revocation(Box::new(\n RevocationDocumentParser::from_pest_pair(doc_type_v10_pair)?,\n ))),\n Rule::tx_v10 =\u003e Ok(UserDocumentDUBP::Transaction(Box::new(\n transaction::TransactionDocumentParser::from_pest_pair(doc_type_v10_pair)?,\n ))),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", doc_type_v10_pair.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use crate::blockstamp::Blockstamp;\n use crate::*;\n\n use super::certification::CertificationDocumentParser;\n use super::identity::IdentityDocumentParser;\n use super::membership::MembershipDocumentParser;\n use super::revocation::RevocationDocumentParser;\n use super::transaction::TransactionDocumentParser;\n\n use dup_crypto::keys::*;\n\n // simple text document for signature testing\n #[derive(Debug, Clone, PartialEq, Eq)]\n struct PlainTextDocument {\n pub text: \u0026'static str,\n pub issuers: Vec\u003cPubKey\u003e,\n pub signatures: Vec\u003cSig\u003e,\n }\n\n impl Document for PlainTextDocument {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n unimplemented!()\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n unimplemented!()\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n unimplemented!()\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n self.text.as_bytes()\n }\n }\n\n #[test]\n fn verify_signatures() {\n let text = \"Version: 10\nType: Identity\nCurrency: duniter_unit_test_currency\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nUniqueID: tic\nTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\n\";\n\n // good pair\n let issuer1 = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let sig1 = Sig::Ed25519(\n ed25519::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\\\n mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\",\n )\n .unwrap(),\n );\n\n // incorrect pair\n let issuer2 = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt32bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let sig2 = Sig::Ed25519(\n ed25519::Signature::from_base64(\n \"1eubHHbuNfilHHH0G2bI30iZzebQ2cQ1PC7uPAw08FGMM\\\n mQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\",\n )\n .unwrap(),\n );\n\n {\n let doc = PlainTextDocument {\n text,\n issuers: vec![issuer1],\n signatures: vec![sig1],\n };\n\n if let Err(e) = doc.verify_signatures() {\n panic!(\"DocumentSigsErr: {:?}\", e)\n }\n }\n\n {\n let doc = PlainTextDocument {\n text,\n issuers: vec![issuer1],\n signatures: vec![sig2],\n };\n // todo: gérer l'erreur avec PartialEq\n /*\n assert_eq!(\n doc.verify_signatures(),\n Err(DocumentSigsErr::Invalid(vec![0]))\n );\n */\n assert!(doc.verify_signatures().is_err());\n }\n\n {\n let doc = PlainTextDocument {\n text,\n issuers: vec![issuer1, issuer2],\n signatures: vec![sig1],\n };\n\n // todo: gérer l'erreur avec PartialEq\n /*\n assert_eq!(\n doc.verify_signatures(),\n Err(DocumentSigsErr::IncompletePairs(2, 1))\n );\n */\n assert!(doc.verify_signatures().is_err());\n }\n\n {\n let doc = PlainTextDocument {\n text,\n issuers: vec![issuer1],\n signatures: vec![sig1, sig2],\n };\n\n // todo: gérer l'erreur avec PartialEq\n /*\n assert_eq!(\n doc.verify_signatures(),\n Err(DocumentSigsErr::IncompletePairs(1, 2))\n );\n */\n assert!(doc.verify_signatures().is_err());\n }\n }\n\n #[test]\n fn parse_identity_document() {\n let text = \"Version: 10\nType: Identity\nCurrency: g1\nIssuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\nUniqueID: elois\nTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nYdnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==\";\n\n let doc = IdentityDocumentParser::parse(text).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n }\n\n #[test]\n fn parse_membership_document() {\n let text = \"Version: 10\nType: Membership\nCurrency: g1\nIssuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\nBlock: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nMembership: IN\nUserID: elois\nCertTS: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nFFeyrvYio9uYwY5aMcDGswZPNjGLrl8THn9l3EPKSNySD3SDSHjCljSfFEwb87sroyzJQoVzPwER0sW/cbZMDg==\";\n\n let doc = MembershipDocumentParser::parse(text).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n }\n\n #[test]\n fn parse_certification_document() {\n let text = \"Version: 10\nType: Certification\nCurrency: g1\nIssuer: 2sZF6j2PkxBDNAqUde7Dgo5x3crkerZpQ4rBqqJGn8QT\nIdtyIssuer: 7jzkd8GiFnpys4X7mP78w2Y3y3kwdK6fVSLEaojd3aH9\nIdtyUniqueID: fbarbut\nIdtyTimestamp: 98221-000000575AC04F5164F7A307CDB766139EA47DD249E4A2444F292BC8AAB408B3\nIdtySignature: DjeipIeb/RF0tpVCnVnuw6mH1iLJHIsDfPGLR90Twy3PeoaDz6Yzhc/UjLWqHCi5Y6wYajV0dNg4jQRUneVBCQ==\nCertTimestamp: 99956-00000472758331FDA8388E30E50CA04736CBFD3B7C21F34E74707107794B56DD\nHkps1QU4HxIcNXKT8YmprYTVByBhPP1U2tIM7Z8wENzLKIWAvQClkAvBE7pW9dnVa18sJIJhVZUcRrPAZfmjBA==\";\n\n let doc = CertificationDocumentParser::parse(text).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n }\n\n #[test]\n fn parse_revocation_document() {\n let text = \"Version: 10\nType: Revocation\nCurrency: g1\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nIdtyUniqueID: tic\nIdtyTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nIdtySignature: 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\nXXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==\";\n\n let doc = RevocationDocumentParser::parse(text).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n }\n\n #[test]\n fn parse_transaction_document() {\n let text = \"Version: 10\nType: Transaction\nCurrency: g1\nBlockstamp: 107702-0000017CDBE974DC9A46B89EE7DC2BEE4017C43A005359E0853026C21FB6A084\nLocktime: 0\nIssuers:\nDo6Y6nQ2KTo5fB4MXbSwabXVmXHxYRB9UUAaTPKn1XqC\nInputs:\n1002:0:D:Do6Y6nQ2KTo5fB4MXbSwabXVmXHxYRB9UUAaTPKn1XqC:104937\n1002:0:D:Do6Y6nQ2KTo5fB4MXbSwabXVmXHxYRB9UUAaTPKn1XqC:105214\nUnlocks:\n0:SIG(0)\n1:SIG(0)\nOutputs:\n2004:0:SIG(DTgQ97AuJ8UgVXcxmNtULAs8Fg1kKC1Wr9SAS96Br9NG)\nComment: c est pour 2 mois d adhesion ressourcerie\nlnpuFsIymgz7qhKF/GsZ3n3W8ZauAAfWmT4W0iJQBLKJK2GFkesLWeMj/+GBfjD6kdkjreg9M6VfkwIZH+hCCQ==\";\n\n let doc = TransactionDocumentParser::parse(text).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok());\n }\n}\n","traces":[{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":1}},{"line":137,"address":null,"length":0,"stats":{"Line":1}},{"line":138,"address":4988541,"length":1,"stats":{"Line":1}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":1}},{"line":144,"address":4989850,"length":1,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":148,"address":4990606,"length":1,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":157,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":4993328,"length":1,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":1}},{"line":173,"address":null,"length":0,"stats":{"Line":1}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":4996720,"length":1,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":223,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":231,"address":null,"length":0,"stats":{"Line":1}},{"line":232,"address":null,"length":0,"stats":{"Line":1}},{"line":235,"address":null,"length":0,"stats":{"Line":1}},{"line":236,"address":null,"length":0,"stats":{"Line":1}},{"line":239,"address":null,"length":0,"stats":{"Line":1}},{"line":240,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":4899952,"length":1,"stats":{"Line":2}},{"line":246,"address":4899973,"length":1,"stats":{"Line":1}},{"line":255,"address":4900067,"length":1,"stats":{"Line":1}},{"line":256,"address":4900001,"length":1,"stats":{"Line":1}},{"line":260,"address":4900188,"length":1,"stats":{"Line":1}},{"line":261,"address":4900139,"length":1,"stats":{"Line":1}},{"line":269,"address":4900294,"length":1,"stats":{"Line":1}},{"line":270,"address":4900240,"length":1,"stats":{"Line":1}},{"line":274,"address":4900408,"length":1,"stats":{"Line":1}},{"line":275,"address":4900366,"length":1,"stats":{"Line":1}},{"line":283,"address":4900832,"length":1,"stats":{"Line":1}},{"line":284,"address":4900452,"length":1,"stats":{"Line":1}},{"line":285,"address":4900468,"length":1,"stats":{"Line":1}},{"line":286,"address":4900629,"length":1,"stats":{"Line":1}},{"line":289,"address":4900936,"length":1,"stats":{"Line":1}},{"line":290,"address":4901082,"length":1,"stats":{"Line":0}},{"line":295,"address":4901763,"length":1,"stats":{"Line":1}},{"line":296,"address":4901367,"length":1,"stats":{"Line":1}},{"line":297,"address":4901383,"length":1,"stats":{"Line":1}},{"line":298,"address":4901560,"length":1,"stats":{"Line":1}},{"line":307,"address":4901867,"length":1,"stats":{"Line":1}},{"line":311,"address":4902449,"length":1,"stats":{"Line":1}},{"line":312,"address":4902019,"length":1,"stats":{"Line":1}},{"line":313,"address":4902035,"length":1,"stats":{"Line":1}},{"line":314,"address":4902252,"length":1,"stats":{"Line":1}},{"line":324,"address":4902550,"length":1,"stats":{"Line":1}},{"line":328,"address":4903175,"length":1,"stats":{"Line":1}},{"line":329,"address":4902690,"length":1,"stats":{"Line":1}},{"line":330,"address":4902706,"length":1,"stats":{"Line":1}},{"line":331,"address":4902850,"length":1,"stats":{"Line":1}},{"line":341,"address":4903276,"length":1,"stats":{"Line":1}},{"line":346,"address":4903776,"length":1,"stats":{"Line":2}},{"line":347,"address":4903790,"length":1,"stats":{"Line":1}},{"line":355,"address":4903804,"length":1,"stats":{"Line":1}},{"line":356,"address":4903867,"length":1,"stats":{"Line":1}},{"line":357,"address":4904037,"length":1,"stats":{"Line":1}},{"line":361,"address":4904208,"length":1,"stats":{"Line":2}},{"line":362,"address":4904222,"length":1,"stats":{"Line":1}},{"line":372,"address":4904236,"length":1,"stats":{"Line":1}},{"line":373,"address":4904299,"length":1,"stats":{"Line":1}},{"line":374,"address":4904469,"length":1,"stats":{"Line":1}},{"line":378,"address":4904640,"length":1,"stats":{"Line":2}},{"line":379,"address":4904654,"length":1,"stats":{"Line":1}},{"line":390,"address":4904668,"length":1,"stats":{"Line":1}},{"line":391,"address":4904731,"length":1,"stats":{"Line":1}},{"line":392,"address":4904901,"length":1,"stats":{"Line":1}},{"line":396,"address":4905072,"length":1,"stats":{"Line":2}},{"line":397,"address":4905086,"length":1,"stats":{"Line":1}},{"line":406,"address":4905100,"length":1,"stats":{"Line":1}},{"line":407,"address":4905163,"length":1,"stats":{"Line":1}},{"line":408,"address":4905333,"length":1,"stats":{"Line":1}},{"line":412,"address":4905504,"length":1,"stats":{"Line":2}},{"line":413,"address":4905518,"length":1,"stats":{"Line":1}},{"line":431,"address":4905532,"length":1,"stats":{"Line":1}},{"line":432,"address":4905595,"length":1,"stats":{"Line":1}},{"line":433,"address":4905765,"length":1,"stats":{"Line":1}}],"covered":76,"coverable":113},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","revocation","v10.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Revocation documents V// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Revocation documents V10.\n\nuse dup_crypto::keys::*;\n\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::*;\nuse crate::text_document_traits::*;\n\n#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq)]\n/// Wrap an Compact Revocation document (in block content)\npub struct CompactRevocationDocumentV10 {\n /// Issuer\n pub issuer: PubKey,\n /// Signature\n pub signature: Sig,\n}\n\nimpl CompactTextDocument for CompactRevocationDocumentV10 {\n fn as_compact_text(\u0026self) -\u003e String {\n format!(\n \"{issuer}:{signature}\",\n issuer = self.issuer,\n signature = self.signature,\n )\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// Revocation document for jsonification\npub struct CompactRevocationDocumentV10Stringified {\n /// Document issuer\n pub issuer: String,\n /// Document signature\n pub signature: String,\n}\n\nimpl ToStringObject for CompactRevocationDocumentV10 {\n type StringObject = CompactRevocationDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e CompactRevocationDocumentV10Stringified {\n CompactRevocationDocumentV10Stringified {\n issuer: format!(\"{}\", self.issuer),\n signature: format!(\"{}\", self.signature),\n }\n }\n}\n\n/// Wrap an Revocation document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub struct RevocationDocumentV10 {\n /// Document as text.\n ///\n /// Is used to check signatures, and other values mut be extracted from it.\n text: String,\n\n /// Name of the currency.\n currency: String,\n /// Document issuer (there should be only one).\n issuers: Vec\u003cPubKey\u003e,\n /// Username of target identity\n identity_username: String,\n /// Target Identity document blockstamp.\n identity_blockstamp: Blockstamp,\n /// Target Identity document signature.\n identity_sig: Sig,\n /// Document signature (there should be only one).\n signatures: Vec\u003cSig\u003e,\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// Revocation document for jsonification\npub struct RevocationDocumentV10Stringified {\n /// Name of the currency.\n currency: String,\n /// Document issuer\n issuer: String,\n /// Username of target identity\n identity_username: String,\n /// Target Identity document blockstamp.\n identity_blockstamp: String,\n /// Target Identity document signature.\n identity_sig: String,\n /// Document signature\n signature: String,\n}\n\nimpl ToStringObject for RevocationDocumentV10 {\n type StringObject = RevocationDocumentV10Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e RevocationDocumentV10Stringified {\n RevocationDocumentV10Stringified {\n currency: self.currency.clone(),\n issuer: format!(\"{}\", self.issuers[0]),\n identity_username: self.identity_username.clone(),\n identity_blockstamp: format!(\"{}\", self.identity_blockstamp),\n identity_sig: format!(\"{}\", self.identity_sig),\n signature: format!(\"{}\", self.signatures[0]),\n }\n }\n}\n\nimpl RevocationDocumentV10 {\n /// Username of target identity\n pub fn identity_username(\u0026self) -\u003e \u0026str {\n \u0026self.identity_username\n }\n /// From pest parser pair\n pub fn from_pest_pair(\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cRevocationDocumentV10, TextDocumentParseError\u003e {\n let doc = pair.as_str();\n let mut currency = \"\";\n let mut pubkeys = Vec::with_capacity(1);\n let mut uid = \"\";\n let mut sigs = Vec::with_capacity(2);\n let mut blockstamps = Vec::with_capacity(1);\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency = field.as_str(),\n Rule::pubkey =\u003e pubkeys.push(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.\n )),\n Rule::uid =\u003e {\n uid = field.as_str();\n }\n Rule::blockstamp =\u003e {\n let mut inner_rules = field.into_inner(); // { integer ~ \"-\" ~ hash }\n\n let block_id: \u0026str = inner_rules.next().unwrap().as_str();\n let block_hash: \u0026str = inner_rules.next().unwrap().as_str();\n blockstamps.push(Blockstamp {\n id: BlockNumber(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.\n hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.\n });\n }\n Rule::ed25519_sig =\u003e {\n sigs.push(Sig::Ed25519(\n ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.\n ));\n }\n Rule::EOI =\u003e (),\n _ =\u003e fatal_error!(\"unexpected rule\"), // Grammar ensures that we never reach this line\n }\n }\n Ok(RevocationDocumentV10 {\n text: doc.to_owned(),\n issuers: vec![pubkeys[0]],\n currency: currency.to_owned(),\n identity_username: uid.to_owned(),\n identity_blockstamp: blockstamps[0],\n identity_sig: sigs[0],\n signatures: vec![sigs[1]],\n })\n }\n}\n\nimpl Document for RevocationDocumentV10 {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n 10\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n \u0026self.currency\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n unimplemented!()\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n self.as_text_without_signature().as_bytes()\n }\n}\n\nimpl TextDocument for RevocationDocumentV10 {\n type CompactTextDocument_ = CompactRevocationDocumentV10;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n \u0026self.text\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n CompactRevocationDocumentV10 {\n issuer: self.issuers[0],\n signature: self.signatures[0],\n }\n }\n}\n\n/// Revocation document builder.\n#[derive(Debug, Copy, Clone)]\npub struct RevocationDocumentV10Builder\u003c'a\u003e {\n /// Document currency.\n pub currency: \u0026'a str,\n /// Revocation issuer.\n pub issuer: \u0026'a PubKey,\n /// Username of target Identity.\n pub identity_username: \u0026'a str,\n /// Blockstamp of target Identity.\n pub identity_blockstamp: \u0026'a Blockstamp,\n /// Signature of target Identity.\n pub identity_sig: \u0026'a Sig,\n}\n\nimpl\u003c'a\u003e RevocationDocumentV10Builder\u003c'a\u003e {\n fn build_with_text_and_sigs(self, text: String, signatures: Vec\u003cSig\u003e) -\u003e RevocationDocumentV10 {\n RevocationDocumentV10 {\n text,\n currency: self.currency.to_string(),\n issuers: vec![*self.issuer],\n identity_username: self.identity_username.to_string(),\n identity_blockstamp: *self.identity_blockstamp,\n identity_sig: *self.identity_sig,\n signatures,\n }\n }\n}\n\nimpl\u003c'a\u003e DocumentBuilder for RevocationDocumentV10Builder\u003c'a\u003e {\n type Document = RevocationDocumentV10;\n type PrivateKey = PrivKey;\n\n fn build_with_signature(\u0026self, signatures: Vec\u003cSig\u003e) -\u003e RevocationDocumentV10 {\n self.build_with_text_and_sigs(self.generate_text(), signatures)\n }\n\n fn build_and_sign(\u0026self, private_keys: Vec\u003cPrivKey\u003e) -\u003e RevocationDocumentV10 {\n let (text, signatures) = self.build_signed_text(private_keys);\n self.build_with_text_and_sigs(text, signatures)\n }\n}\n\nimpl\u003c'a\u003e TextDocumentBuilder for RevocationDocumentV10Builder\u003c'a\u003e {\n fn generate_text(\u0026self) -\u003e String {\n format!(\n \"Version: 10\nType: Revocation\nCurrency: {currency}\nIssuer: {issuer}\nIdtyUniqueID: {idty_uid}\nIdtyTimestamp: {idty_blockstamp}\nIdtySignature: {idty_sig}\n\",\n currency = self.currency,\n issuer = self.issuer,\n idty_uid = self.identity_username,\n idty_blockstamp = self.identity_blockstamp,\n idty_sig = self.identity_sig,\n )\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use dup_crypto::keys::{PrivateKey, PublicKey, Signature};\n\n #[test]\n fn generate_real_document() {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let prikey = PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\\\n iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7\",\n )\n .unwrap(),\n );\n\n let sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"XXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==\",\n ).unwrap());\n\n let identity_blockstamp = Blockstamp::from_string(\n \"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\",\n )\n .unwrap();\n\n let identity_sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\",\n ).unwrap());\n\n let builder = RevocationDocumentV10Builder {\n currency: \"g1\",\n issuer: \u0026pubkey,\n identity_username: \"tic\",\n identity_blockstamp: \u0026identity_blockstamp,\n identity_sig: \u0026identity_sig,\n };\n\n assert!(builder\n .build_with_signature(vec![sig])\n .verify_signatures()\n .is_ok());\n\n assert!(builder\n .build_and_sign(vec![prikey])\n .verify_signatures()\n .is_ok());\n }\n\n #[test]\n fn revocation_document() {\n let doc = \"Version: 10\nType: Revocation\nCurrency: g1\nIssuer: DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\nIdtyUniqueID: tic\nIdtyTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nIdtySignature: 1eubHHbuNfilHMM0G2bI30iZzebQ2cQ1PC7uPAw08FGMMmQCRerlF/3pc4sAcsnexsxBseA/3lY03KlONqJBAg==\nXXOgI++6qpY9O31ml/FcfbXCE6aixIrgkT5jL7kBle3YOMr+8wrp7Rt+z9hDVjrNfYX2gpeJsuMNfG4T/fzVDQ==\";\n\n let doc = RevocationDocumentParser::parse(doc).unwrap();\n println!(\"Doc : {:?}\", doc);\n assert!(doc.verify_signatures().is_ok())\n }\n}\n","traces":[{"line":49,"address":null,"length":0,"stats":{"Line":0}},{"line":50,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":1}},{"line":143,"address":null,"length":0,"stats":{"Line":1}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":148,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":4877684,"length":1,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":4877897,"length":1,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":4878259,"length":1,"stats":{"Line":1}},{"line":165,"address":4878358,"length":1,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":4878541,"length":1,"stats":{"Line":1}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":4878718,"length":1,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":197,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":208,"address":null,"length":0,"stats":{"Line":1}},{"line":209,"address":null,"length":0,"stats":{"Line":1}},{"line":212,"address":null,"length":0,"stats":{"Line":1}},{"line":213,"address":null,"length":0,"stats":{"Line":1}},{"line":220,"address":null,"length":0,"stats":{"Line":1}},{"line":221,"address":null,"length":0,"stats":{"Line":1}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":1}},{"line":251,"address":null,"length":0,"stats":{"Line":1}},{"line":252,"address":null,"length":0,"stats":{"Line":1}},{"line":253,"address":null,"length":0,"stats":{"Line":1}},{"line":254,"address":null,"length":0,"stats":{"Line":1}},{"line":255,"address":null,"length":0,"stats":{"Line":1}},{"line":265,"address":null,"length":0,"stats":{"Line":1}},{"line":266,"address":null,"length":0,"stats":{"Line":1}},{"line":269,"address":null,"length":0,"stats":{"Line":1}},{"line":270,"address":null,"length":0,"stats":{"Line":1}},{"line":271,"address":null,"length":0,"stats":{"Line":1}},{"line":276,"address":null,"length":0,"stats":{"Line":1}},{"line":277,"address":null,"length":0,"stats":{"Line":1}},{"line":286,"address":null,"length":0,"stats":{"Line":1}},{"line":287,"address":null,"length":0,"stats":{"Line":1}},{"line":288,"address":null,"length":0,"stats":{"Line":1}},{"line":289,"address":null,"length":0,"stats":{"Line":1}},{"line":290,"address":null,"length":0,"stats":{"Line":1}},{"line":301,"address":4207936,"length":1,"stats":{"Line":2}},{"line":302,"address":4265488,"length":1,"stats":{"Line":1}},{"line":303,"address":4265422,"length":1,"stats":{"Line":1}},{"line":307,"address":4265600,"length":1,"stats":{"Line":1}},{"line":308,"address":4265551,"length":1,"stats":{"Line":1}},{"line":315,"address":4265649,"length":1,"stats":{"Line":1}},{"line":319,"address":4265749,"length":1,"stats":{"Line":1}},{"line":324,"address":4265800,"length":1,"stats":{"Line":1}},{"line":328,"address":4265934,"length":1,"stats":{"Line":1}},{"line":336,"address":4266135,"length":1,"stats":{"Line":1}},{"line":337,"address":4266021,"length":1,"stats":{"Line":1}},{"line":341,"address":4266434,"length":1,"stats":{"Line":1}},{"line":342,"address":4266317,"length":1,"stats":{"Line":1}},{"line":348,"address":4207968,"length":1,"stats":{"Line":2}},{"line":349,"address":4266734,"length":1,"stats":{"Line":1}},{"line":358,"address":4266748,"length":1,"stats":{"Line":1}},{"line":359,"address":4266811,"length":1,"stats":{"Line":1}},{"line":360,"address":4266981,"length":1,"stats":{"Line":1}}],"covered":73,"coverable":102},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","revocation.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Revocation documents.\n\npub mod v10;\n\npub use v10::{\n CompactRevocationDocumentV10, CompactRevocationDocumentV10Stringified, RevocationDocumentV10,\n RevocationDocumentV10Stringified,\n};\n\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::*;\n\nuse dup_crypto::keys::*;\nuse pest::Parser;\n\n/// Wrap an Revocation document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub enum RevocationDocument {\n /// Revocation document v10\n V10(RevocationDocumentV10),\n}\n\n/// Wrap an Compact Revocation document.\n///\n/// Must be created by a revocation document.\n#[derive(Debug, Copy, Clone, Deserialize, Serialize, PartialEq, Eq)]\npub enum CompactRevocationDocument {\n /// Compact revocation document v10\n V10(CompactRevocationDocumentV10),\n}\n\nimpl Document for RevocationDocument {\n type PublicKey = PubKey;\n\n #[inline]\n fn version(\u0026self) -\u003e u16 {\n match self {\n RevocationDocument::V10(_) =\u003e 10u16,\n }\n }\n\n #[inline]\n fn currency(\u0026self) -\u003e \u0026str {\n match self {\n RevocationDocument::V10(revoc_v10) =\u003e revoc_v10.currency(),\n }\n }\n\n #[inline]\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n match self {\n RevocationDocument::V10(revoc_v10) =\u003e revoc_v10.blockstamp(),\n }\n }\n\n #[inline]\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n match self {\n RevocationDocument::V10(revoc_v10) =\u003e revoc_v10.issuers(),\n }\n }\n\n #[inline]\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n match self {\n RevocationDocument::V10(revoc_v10) =\u003e revoc_v10.signatures(),\n }\n }\n\n #[inline]\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n match self {\n RevocationDocument::V10(revoc_v10) =\u003e revoc_v10.as_bytes(),\n }\n }\n}\n\n/// Revocation document parser\n#[derive(Debug, Clone, Copy)]\npub struct RevocationDocumentParser;\n\nimpl TextDocumentParser\u003cRule\u003e for RevocationDocumentParser {\n type DocumentType = RevocationDocument;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let mut revoc_pairs = DocumentsParser::parse(Rule::revoc, doc)?;\n let revoc_pair = revoc_pairs.next().unwrap(); // get and unwrap the `revoc` rule; never fails\n Self::from_pest_pair(revoc_pair)\n }\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let revoc_vx_pair = pair.into_inner().next().unwrap(); // get and unwrap the `revoc_vX` rule; never fails\n\n match revoc_vx_pair.as_rule() {\n Rule::revoc_v10 =\u003e Self::from_versioned_pest_pair(10, revoc_vx_pair),\n _ =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"{:#?}\",\n revoc_vx_pair.as_rule()\n ))),\n }\n }\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 10 =\u003e Ok(RevocationDocument::V10(\n RevocationDocumentV10::from_pest_pair(pair)?,\n )),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub enum RevocationDocumentStringified {\n V10(RevocationDocumentV10Stringified),\n}\n\nimpl ToStringObject for RevocationDocument {\n type StringObject = RevocationDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match self {\n RevocationDocument::V10(idty) =\u003e {\n RevocationDocumentStringified::V10(idty.to_string_object())\n }\n }\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Serialize)]\npub enum CompactRevocationDocumentStringified {\n V10(CompactRevocationDocumentV10Stringified),\n}\n\nimpl ToStringObject for CompactRevocationDocument {\n type StringObject = CompactRevocationDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n match self {\n CompactRevocationDocument::V10(doc) =\u003e {\n CompactRevocationDocumentStringified::V10(doc.to_string_object())\n }\n }\n }\n}\n","traces":[{"line":53,"address":null,"length":0,"stats":{"Line":0}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":4906691,"length":1,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":1}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":109,"address":4906922,"length":1,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":124,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":1}},{"line":126,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":146,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}}],"covered":18,"coverable":44},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","documents","transaction.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Wrappers around Transaction documents.\n\nuse dup_crypto::hashs::*;\nuse durs_common_tools::fatal_error;\nuse pest::iterators::Pair;\nuse pest::iterators::Pairs;\nuse pest::Parser;\nuse std::ops::{Add, Deref, Sub};\nuse std::str::FromStr;\nuse unwrap::unwrap;\n\nuse crate::blockstamp::Blockstamp;\nuse crate::documents::*;\nuse crate::text_document_traits::*;\n\n/// Wrap a transaction amount\n#[derive(Debug, Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Deserialize, Hash, Serialize)]\npub struct TxAmount(pub isize);\n\nimpl Add for TxAmount {\n type Output = TxAmount;\n fn add(self, a: TxAmount) -\u003e Self::Output {\n TxAmount(self.0 + a.0)\n }\n}\n\nimpl Sub for TxAmount {\n type Output = TxAmount;\n fn sub(self, a: TxAmount) -\u003e Self::Output {\n TxAmount(self.0 - a.0)\n }\n}\n\n/// Wrap a transaction amout base\n#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Deserialize, Hash, Serialize)]\npub struct TxBase(pub usize);\n\n/// Wrap a transaction index\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]\npub struct TxIndex(pub usize);\n\n/// Wrap a transaction input\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize, Serialize)]\npub enum TransactionInput {\n /// Universal Dividend Input\n D(TxAmount, TxBase, PubKey, BlockNumber),\n /// Previous Transaction Input\n T(TxAmount, TxBase, Hash, TxIndex),\n}\n\nimpl ToString for TransactionInput {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n TransactionInput::D(amount, base, pubkey, block_number) =\u003e {\n format!(\"{}:{}:D:{}:{}\", amount.0, base.0, pubkey, block_number.0)\n }\n TransactionInput::T(amount, base, ref tx_hash, tx_index) =\u003e {\n format!(\"{}:{}:T:{}:{}\", amount.0, base.0, tx_hash, tx_index.0)\n }\n }\n }\n}\n\nimpl TransactionInput {\n fn from_pest_pair(mut pairs: Pairs\u003cRule\u003e) -\u003e TransactionInput {\n let tx_input_type_pair = pairs.next().unwrap();\n match tx_input_type_pair.as_rule() {\n Rule::tx_input_du =\u003e {\n let mut inner_rules = tx_input_type_pair.into_inner(); // ${ tx_amount ~ \":\" ~ tx_amount_base ~ \":D:\" ~ pubkey ~ \":\" ~ du_block_id }\n\n TransactionInput::D(\n TxAmount(inner_rules.next().unwrap().as_str().parse().unwrap()),\n TxBase(inner_rules.next().unwrap().as_str().parse().unwrap()),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(inner_rules.next().unwrap().as_str())\n .unwrap(),\n ),\n BlockNumber(inner_rules.next().unwrap().as_str().parse().unwrap()),\n )\n }\n Rule::tx_input_tx =\u003e {\n let mut inner_rules = tx_input_type_pair.into_inner(); // ${ tx_amount ~ \":\" ~ tx_amount_base ~ \":D:\" ~ pubkey ~ \":\" ~ du_block_id }\n\n TransactionInput::T(\n TxAmount(inner_rules.next().unwrap().as_str().parse().unwrap()),\n TxBase(inner_rules.next().unwrap().as_str().parse().unwrap()),\n Hash::from_hex(inner_rules.next().unwrap().as_str()).unwrap(),\n TxIndex(inner_rules.next().unwrap().as_str().parse().unwrap()),\n )\n }\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", tx_input_type_pair.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n}\n\nimpl FromStr for TransactionInput {\n type Err = TextDocumentParseError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match DocumentsParser::parse(Rule::tx_input, source) {\n Ok(mut pairs) =\u003e Ok(TransactionInput::from_pest_pair(\n pairs.next().unwrap().into_inner(),\n )),\n Err(_) =\u003e Err(TextDocumentParseError::InvalidInnerFormat(\n \"Invalid unlocks !\".to_owned(),\n )),\n }\n }\n}\n\n/*impl TransactionInput {\n /// Parse Transaction Input from string.\n pub fn from_str(source: \u0026str) -\u003e Result\u003cTransactionInput, TextDocumentParseError\u003e {\n if let Some(caps) = D_INPUT_REGEX.captures(source) {\n let amount = \u0026caps[\"amount\"];\n let base = \u0026caps[\"base\"];\n let pubkey = \u0026caps[\"pubkey\"];\n let block_number = \u0026caps[\"block_number\"];\n Ok(TransactionInput::D(\n TxAmount(amount.parse().expect(\"fail to parse input amount !\")),\n TxBase(base.parse().expect(\"fail to parse input base !\")),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(pubkey).expect(\"fail to parse input pubkey !\"),\n ),\n BlockNumber(\n block_number\n .parse()\n .expect(\"fail to parse input block_number !\"),\n ),\n ))\n //Ok(TransactionInput::D(10, 0, PubKey::Ed25519(ed25519::PublicKey::from_base58(\"FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa\").unwrap(), 0)))\n } else if let Some(caps) = T_INPUT_REGEX.captures(source) {\n let amount = \u0026caps[\"amount\"];\n let base = \u0026caps[\"base\"];\n let tx_hash = \u0026caps[\"tx_hash\"];\n let tx_index = \u0026caps[\"tx_index\"];\n Ok(TransactionInput::T(\n TxAmount(amount.parse().expect(\"fail to parse input amount\")),\n TxBase(base.parse().expect(\"fail to parse base amount\")),\n Hash::from_hex(tx_hash).expect(\"fail to parse tx_hash\"),\n TxIndex(tx_index.parse().expect(\"fail to parse tx_index amount\")),\n ))\n } else {\n println!(\"Fail to parse this input = {:?}\", source);\n Err(TextDocumentParseError::InvalidInnerFormat(\"Transaction2\"))\n }\n }\n}*/\n\n/// Wrap a transaction unlock proof\n#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]\npub enum TransactionUnlockProof {\n /// Indicates that the signature of the corresponding key is at the bottom of the document\n Sig(usize),\n /// Provides the code to unlock the corresponding funds\n Xhx(String),\n}\n\nimpl ToString for TransactionUnlockProof {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n TransactionUnlockProof::Sig(ref index) =\u003e format!(\"SIG({})\", index),\n TransactionUnlockProof::Xhx(ref hash) =\u003e format!(\"XHX({})\", hash),\n }\n }\n}\n\n/// Wrap a transaction unlocks input\n#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]\npub struct TransactionInputUnlocks {\n /// Input index\n pub index: usize,\n /// List of proof to unlock funds\n pub unlocks: Vec\u003cTransactionUnlockProof\u003e,\n}\n\nimpl ToString for TransactionInputUnlocks {\n fn to_string(\u0026self) -\u003e String {\n let mut result: String = format!(\"{}:\", self.index);\n for unlock in \u0026self.unlocks {\n result.push_str(\u0026format!(\"{} \", unlock.to_string()));\n }\n let new_size = result.len() - 1;\n result.truncate(new_size);\n result\n }\n}\n\nimpl TransactionInputUnlocks {\n fn from_pest_pair(pairs: Pairs\u003cRule\u003e) -\u003e TransactionInputUnlocks {\n let mut input_index = 0;\n let mut unlock_conds = Vec::new();\n for unlock_field in pairs {\n // ${ input_index ~ \":\" ~ unlock_cond ~ (\" \" ~ unlock_cond)* }\n match unlock_field.as_rule() {\n Rule::input_index =\u003e input_index = unlock_field.as_str().parse().unwrap(),\n Rule::unlock_sig =\u003e unlock_conds.push(TransactionUnlockProof::Sig(\n unlock_field\n .into_inner()\n .next()\n .unwrap()\n .as_str()\n .parse()\n .unwrap(),\n )),\n Rule::unlock_xhx =\u003e unlock_conds.push(TransactionUnlockProof::Xhx(String::from(\n unlock_field.into_inner().next().unwrap().as_str(),\n ))),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", unlock_field.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n TransactionInputUnlocks {\n index: input_index,\n unlocks: unlock_conds,\n }\n }\n}\n\nimpl FromStr for TransactionInputUnlocks {\n type Err = TextDocumentParseError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match DocumentsParser::parse(Rule::tx_unlock, source) {\n Ok(mut pairs) =\u003e Ok(TransactionInputUnlocks::from_pest_pair(\n pairs.next().unwrap().into_inner(),\n )),\n Err(_) =\u003e Err(TextDocumentParseError::InvalidInnerFormat(\n \"Invalid unlocks !\".to_owned(),\n )),\n }\n }\n}\n\n/// Wrap a transaction ouput condition\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]\npub enum TransactionOutputCondition {\n /// The consumption of funds will require a valid signature of the specified key\n Sig(PubKey),\n /// The consumption of funds will require to provide a code with the hash indicated\n Xhx(Hash),\n /// Funds may not be consumed until the blockchain reaches the timestamp indicated.\n Cltv(u64),\n /// Funds may not be consumed before the duration indicated, starting from the timestamp of the block where the transaction is written.\n Csv(u64),\n}\n\nimpl ToString for TransactionOutputCondition {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n TransactionOutputCondition::Sig(ref pubkey) =\u003e format!(\"SIG({})\", pubkey),\n TransactionOutputCondition::Xhx(ref hash) =\u003e format!(\"XHX({})\", hash),\n TransactionOutputCondition::Cltv(timestamp) =\u003e format!(\"CLTV({})\", timestamp),\n TransactionOutputCondition::Csv(duration) =\u003e format!(\"CSV({})\", duration),\n }\n }\n}\n\n/// Wrap an utxo conditions\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]\npub struct UTXOConditions {\n /// We are obliged to allow the introduction of the original text (instead of the self-generated text),\n /// because the original text may contain errors that are unfortunately allowed by duniter-ts.\n pub origin_str: Option\u003cString\u003e,\n /// Store script conditions\n pub conditions: UTXOConditionsGroup,\n}\n\nimpl UTXOConditions {\n /// Lightens the UTXOConditions (for example to store it while minimizing the space required)\n pub fn reduce(\u0026mut self) {\n if self.origin_str.is_some()\n \u0026\u0026 self.origin_str.clone().expect(\"safe unwrap\") == self.conditions.to_string()\n {\n self.origin_str = None;\n }\n }\n /// Check validity of this UTXOConditions\n pub fn check(\u0026self) -\u003e bool {\n !(self.origin_str.is_some()\n \u0026\u0026 self.origin_str.clone().expect(\"safe unwrap\") != self.conditions.to_string())\n }\n}\n\nimpl ToString for UTXOConditions {\n fn to_string(\u0026self) -\u003e String {\n if let Some(ref origin_str) = self.origin_str {\n origin_str.to_string()\n } else {\n self.conditions.to_string()\n }\n }\n}\n\n/// Wrap a transaction ouput condition group\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]\npub enum UTXOConditionsGroup {\n /// Single\n Single(TransactionOutputCondition),\n /// Brackets\n Brackets(Box\u003cUTXOConditionsGroup\u003e),\n /// And operator\n And(Box\u003cUTXOConditionsGroup\u003e, Box\u003cUTXOConditionsGroup\u003e),\n /// Or operator\n Or(Box\u003cUTXOConditionsGroup\u003e, Box\u003cUTXOConditionsGroup\u003e),\n}\n\nmacro_rules! utxo_conds_wrap_op_chain {\n ($op:expr, $fn_name:ident) =\u003e {\n fn $fn_name(conds_subgroups: \u0026mut Vec\u003cUTXOConditionsGroup\u003e) -\u003e UTXOConditionsGroup {\n if conds_subgroups.len() == 2 {\n $op(\n Box::new(conds_subgroups[0].clone()),\n Box::new(conds_subgroups[1].clone()),\n )\n } else if conds_subgroups.len() \u003e 2 {\n let last_subgroup = conds_subgroups.pop().unwrap();\n let previous_last_subgroup = conds_subgroups.pop().unwrap();\n conds_subgroups.push($op(\n Box::new(previous_last_subgroup),\n Box::new(last_subgroup),\n ));\n UTXOConditionsGroup::$fn_name(conds_subgroups)\n } else {\n fatal_error!(\n \"Grammar should ensure that and chain contains at least two conditions subgroups !\"\n )\n }\n }\n }\n}\n\nimpl UTXOConditionsGroup {\n /// Wrap UTXO and chain\n utxo_conds_wrap_op_chain!(UTXOConditionsGroup::And, new_and_chain);\n /// Wrap UTXO or chain\n utxo_conds_wrap_op_chain!(UTXOConditionsGroup::Or, new_or_chain);\n\n /// Wrap UTXO conditions\n pub fn wrap_utxo_conds(pair: Pair\u003cRule\u003e) -\u003e UTXOConditionsGroup {\n match pair.as_rule() {\n Rule::output_and_group =\u003e {\n let and_pairs = pair.into_inner();\n let mut conds_subgroups: Vec\u003cUTXOConditionsGroup\u003e = and_pairs\n .map(UTXOConditionsGroup::wrap_utxo_conds)\n .collect();\n UTXOConditionsGroup::Brackets(Box::new(UTXOConditionsGroup::new_and_chain(\n \u0026mut conds_subgroups,\n )))\n }\n Rule::output_or_group =\u003e {\n let or_pairs = pair.into_inner();\n let mut conds_subgroups: Vec\u003cUTXOConditionsGroup\u003e =\n or_pairs.map(UTXOConditionsGroup::wrap_utxo_conds).collect();\n UTXOConditionsGroup::Brackets(Box::new(UTXOConditionsGroup::new_or_chain(\n \u0026mut conds_subgroups,\n )))\n }\n Rule::output_cond_sig =\u003e {\n UTXOConditionsGroup::Single(TransactionOutputCondition::Sig(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(pair.into_inner().next().unwrap().as_str())\n .unwrap(),\n )))\n }\n Rule::output_cond_xhx =\u003e UTXOConditionsGroup::Single(TransactionOutputCondition::Xhx(\n Hash::from_hex(pair.into_inner().next().unwrap().as_str()).unwrap(),\n )),\n Rule::output_cond_csv =\u003e UTXOConditionsGroup::Single(TransactionOutputCondition::Csv(\n pair.into_inner().next().unwrap().as_str().parse().unwrap(),\n )),\n Rule::output_cond_cltv =\u003e {\n UTXOConditionsGroup::Single(TransactionOutputCondition::Cltv(\n pair.into_inner().next().unwrap().as_str().parse().unwrap(),\n ))\n }\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", pair.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n}\n\nimpl ToString for UTXOConditionsGroup {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n UTXOConditionsGroup::Single(ref condition) =\u003e condition.to_string(),\n UTXOConditionsGroup::Brackets(ref condition_group) =\u003e {\n format!(\"({})\", condition_group.deref().to_string())\n }\n UTXOConditionsGroup::And(ref condition_group_1, ref condition_group_2) =\u003e format!(\n \"{} \u0026\u0026 {}\",\n condition_group_1.deref().to_string(),\n condition_group_2.deref().to_string()\n ),\n UTXOConditionsGroup::Or(ref condition_group_1, ref condition_group_2) =\u003e format!(\n \"{} || {}\",\n condition_group_1.deref().to_string(),\n condition_group_2.deref().to_string()\n ),\n }\n }\n}\n\n/// Wrap a transaction ouput\n#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]\npub struct TransactionOutput {\n /// Amount\n pub amount: TxAmount,\n /// Base\n pub base: TxBase,\n /// List of conditions for consum this output\n pub conditions: UTXOConditions,\n}\n\nimpl TransactionOutput {\n /// Lightens the TransactionOutput (for example to store it while minimizing the space required)\n fn reduce(\u0026mut self) {\n self.conditions.reduce()\n }\n /// Check validity of this output\n pub fn check(\u0026self) -\u003e bool {\n self.conditions.check()\n }\n}\n\nimpl ToString for TransactionOutput {\n fn to_string(\u0026self) -\u003e String {\n format!(\n \"{}:{}:{}\",\n self.amount.0,\n self.base.0,\n self.conditions.to_string()\n )\n }\n}\n\nimpl TransactionOutput {\n fn from_pest_pair(mut utxo_pairs: Pairs\u003cRule\u003e) -\u003e TransactionOutput {\n let amount = TxAmount(utxo_pairs.next().unwrap().as_str().parse().unwrap());\n let base = TxBase(utxo_pairs.next().unwrap().as_str().parse().unwrap());\n let conditions_pairs = utxo_pairs.next().unwrap();\n let conditions_origin_str = conditions_pairs.as_str();\n TransactionOutput {\n amount,\n base,\n conditions: UTXOConditions {\n origin_str: Some(String::from(conditions_origin_str)),\n conditions: UTXOConditionsGroup::wrap_utxo_conds(conditions_pairs),\n },\n }\n }\n}\n\nimpl FromStr for TransactionOutput {\n type Err = TextDocumentParseError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n let output_parts: Vec\u003c\u0026str\u003e = source.split(':').collect();\n let amount = output_parts.get(0);\n let base = output_parts.get(1);\n let conditions_origin_str = output_parts.get(2);\n\n let str_to_parse = if amount.is_some() \u0026\u0026 base.is_some() \u0026\u0026 conditions_origin_str.is_some()\n {\n format!(\n \"{}:{}:({})\",\n unwrap!(amount),\n unwrap!(base),\n unwrap!(conditions_origin_str)\n )\n } else {\n source.to_owned()\n };\n\n match DocumentsParser::parse(Rule::tx_output, \u0026str_to_parse) {\n Ok(mut utxo_pairs) =\u003e {\n let mut output =\n TransactionOutput::from_pest_pair(utxo_pairs.next().unwrap().into_inner());\n output.conditions.origin_str = conditions_origin_str.map(ToString::to_string);\n Ok(output)\n }\n Err(_) =\u003e match DocumentsParser::parse(Rule::tx_output, source) {\n Ok(mut utxo_pairs) =\u003e {\n let mut output =\n TransactionOutput::from_pest_pair(utxo_pairs.next().unwrap().into_inner());\n output.conditions.origin_str = conditions_origin_str.map(ToString::to_string);\n Ok(output)\n }\n Err(e) =\u003e Err(TextDocumentParseError::InvalidInnerFormat(format!(\n \"Invalid output : {}\",\n e\n ))),\n },\n }\n }\n}\n\n/// Wrap a Transaction document.\n///\n/// Must be created by parsing a text document or using a builder.\n#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]\npub struct TransactionDocument {\n /// Document as text.\n ///\n /// Is used to check signatures, and other values\n /// must be extracted from it.\n text: Option\u003cString\u003e,\n\n /// Currency.\n currency: String,\n /// Blockstamp\n blockstamp: Blockstamp,\n /// Locktime\n locktime: u64,\n /// Document issuer (there should be only one).\n issuers: Vec\u003cPubKey\u003e,\n /// Transaction inputs.\n inputs: Vec\u003cTransactionInput\u003e,\n /// Inputs unlocks.\n unlocks: Vec\u003cTransactionInputUnlocks\u003e,\n /// Transaction outputs.\n outputs: Vec\u003cTransactionOutput\u003e,\n /// Transaction comment\n comment: String,\n /// Document signature (there should be only one).\n signatures: Vec\u003cSig\u003e,\n /// Transaction hash\n hash: Option\u003cHash\u003e,\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// Transaction document stringifed\npub struct TransactionDocumentStringified {\n /// Currency.\n pub currency: String,\n /// Blockstamp\n pub blockstamp: String,\n /// Locktime\n pub locktime: u64,\n /// Document issuer (there should be only one).\n pub issuers: Vec\u003cString\u003e,\n /// Transaction inputs.\n pub inputs: Vec\u003cString\u003e,\n /// Inputs unlocks.\n pub unlocks: Vec\u003cString\u003e,\n /// Transaction outputs.\n pub outputs: Vec\u003cString\u003e,\n /// Transaction comment\n pub comment: String,\n /// Document signatures\n pub signatures: Vec\u003cString\u003e,\n /// Transaction hash\n pub hash: Option\u003cString\u003e,\n}\n\nimpl ToStringObject for TransactionDocument {\n type StringObject = TransactionDocumentStringified;\n\n fn to_string_object(\u0026self) -\u003e TransactionDocumentStringified {\n TransactionDocumentStringified {\n currency: self.currency.clone(),\n blockstamp: format!(\"{}\", self.blockstamp),\n locktime: self.locktime,\n issuers: self.issuers.iter().map(|p| format!(\"{}\", p)).collect(),\n inputs: self\n .inputs\n .iter()\n .map(TransactionInput::to_string)\n .collect(),\n unlocks: self\n .unlocks\n .iter()\n .map(TransactionInputUnlocks::to_string)\n .collect(),\n outputs: self\n .outputs\n .iter()\n .map(TransactionOutput::to_string)\n .collect(),\n comment: self.comment.clone(),\n signatures: self.signatures.iter().map(|s| format!(\"{}\", s)).collect(),\n hash: if let Some(hash) = self.hash {\n Some(hash.to_string())\n } else {\n None\n },\n }\n }\n}\n\nimpl TransactionDocument {\n /// Compute transaction hash\n pub fn compute_hash(\u0026mut self) -\u003e Hash {\n let mut hashing_text = if let Some(ref text) = self.text {\n text.clone()\n } else {\n fatal_error!(\"Try to compute_hash of tx with None text !\")\n };\n for sig in \u0026self.signatures {\n hashing_text.push_str(\u0026sig.to_string());\n hashing_text.push_str(\"\\n\");\n }\n //println!(\"tx_text_hasing={}\", hashing_text);\n self.hash = Some(Hash::compute_str(\u0026hashing_text));\n self.hash.expect(\"Try to get hash of a reduce tx !\")\n }\n /// get transaction hash option\n pub fn get_hash_opt(\u0026self) -\u003e Option\u003cHash\u003e {\n self.hash\n }\n /// Get transaction hash\n pub fn get_hash(\u0026mut self) -\u003e Hash {\n if let Some(hash) = self.hash {\n hash\n } else {\n self.compute_hash()\n }\n }\n /// Get transaction inputs\n pub fn get_inputs(\u0026self) -\u003e \u0026[TransactionInput] {\n \u0026self.inputs\n }\n /// Get transaction outputs\n pub fn get_outputs(\u0026self) -\u003e \u0026[TransactionOutput] {\n \u0026self.outputs\n }\n /// Lightens the transaction (for example to store it while minimizing the space required)\n pub fn reduce(\u0026mut self) {\n self.text = None;\n self.hash = None;\n for output in \u0026mut self.outputs {\n output.reduce()\n }\n }\n /// from pest parser pair\n pub fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cTransactionDocument, TextDocumentParseError\u003e {\n let doc = pair.as_str();\n let mut currency = \"\";\n let mut blockstamp = Blockstamp::default();\n let mut locktime = 0;\n let mut issuers = Vec::new();\n let mut inputs = Vec::new();\n let mut unlocks = Vec::new();\n let mut outputs = Vec::new();\n let mut comment = \"\";\n let mut sigs = Vec::new();\n\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency = field.as_str(),\n Rule::blockstamp =\u003e {\n let mut inner_rules = field.into_inner(); // ${ block_id ~ \"-\" ~ hash }\n\n let block_id: \u0026str = inner_rules.next().unwrap().as_str();\n let block_hash: \u0026str = inner_rules.next().unwrap().as_str();\n blockstamp = Blockstamp {\n id: BlockNumber(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.\n hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.\n };\n }\n Rule::tx_locktime =\u003e locktime = field.as_str().parse().unwrap(), // Grammar ensures that we have digits characters.\n Rule::pubkey =\u003e issuers.push(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(field.as_str()).unwrap(), // Grammar ensures that we have a base58 string.\n )),\n Rule::tx_input =\u003e inputs.push(TransactionInput::from_pest_pair(field.into_inner())),\n Rule::tx_unlock =\u003e {\n unlocks.push(TransactionInputUnlocks::from_pest_pair(field.into_inner()))\n }\n Rule::tx_output =\u003e {\n outputs.push(TransactionOutput::from_pest_pair(field.into_inner()))\n }\n Rule::tx_comment =\u003e comment = field.as_str(),\n Rule::ed25519_sig =\u003e {\n sigs.push(Sig::Ed25519(\n ed25519::Signature::from_base64(field.as_str()).unwrap(), // Grammar ensures that we have a base64 string.\n ));\n }\n Rule::EOI =\u003e (),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", field.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n\n Ok(TransactionDocument {\n text: Some(doc.to_owned()),\n currency: currency.to_owned(),\n blockstamp,\n locktime,\n issuers,\n inputs,\n unlocks,\n outputs,\n comment: comment.to_owned(),\n signatures: sigs,\n hash: None,\n })\n }\n}\n\nimpl Document for TransactionDocument {\n type PublicKey = PubKey;\n\n fn version(\u0026self) -\u003e u16 {\n 10\n }\n\n fn currency(\u0026self) -\u003e \u0026str {\n \u0026self.currency\n }\n\n fn blockstamp(\u0026self) -\u003e Blockstamp {\n self.blockstamp\n }\n\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cPubKey\u003e {\n \u0026self.issuers\n }\n\n fn signatures(\u0026self) -\u003e \u0026Vec\u003cSig\u003e {\n \u0026self.signatures\n }\n\n fn as_bytes(\u0026self) -\u003e \u0026[u8] {\n self.as_text_without_signature().as_bytes()\n }\n}\n\nimpl CompactTextDocument for TransactionDocument {\n fn as_compact_text(\u0026self) -\u003e String {\n let mut issuers_str = String::from(\"\");\n for issuer in self.issuers.clone() {\n issuers_str.push_str(\"\\n\");\n issuers_str.push_str(\u0026issuer.to_string());\n }\n let mut inputs_str = String::from(\"\");\n for input in self.inputs.clone() {\n inputs_str.push_str(\"\\n\");\n inputs_str.push_str(\u0026input.to_string());\n }\n let mut unlocks_str = String::from(\"\");\n for unlock in self.unlocks.clone() {\n unlocks_str.push_str(\"\\n\");\n unlocks_str.push_str(\u0026unlock.to_string());\n }\n let mut outputs_str = String::from(\"\");\n for output in self.outputs.clone() {\n outputs_str.push_str(\"\\n\");\n outputs_str.push_str(\u0026output.to_string());\n }\n let mut comment_str = self.comment.clone();\n if !comment_str.is_empty() {\n comment_str.push_str(\"\\n\");\n }\n let mut signatures_str = String::from(\"\");\n for sig in self.signatures.clone() {\n signatures_str.push_str(\u0026sig.to_string());\n signatures_str.push_str(\"\\n\");\n }\n // Remove end line step\n signatures_str.pop();\n format!(\n \"TX:10:{issuers_count}:{inputs_count}:{unlocks_count}:{outputs_count}:{has_comment}:{locktime}\n{blockstamp}{issuers}{inputs}{unlocks}{outputs}\\n{comment}{signatures}\",\n issuers_count = self.issuers.len(),\n inputs_count = self.inputs.len(),\n unlocks_count = self.unlocks.len(),\n outputs_count = self.outputs.len(),\n has_comment = if self.comment.is_empty() { 0 } else { 1 },\n locktime = self.locktime,\n blockstamp = self.blockstamp,\n issuers = issuers_str,\n inputs = inputs_str,\n unlocks = unlocks_str,\n outputs = outputs_str,\n comment = comment_str,\n signatures = signatures_str,\n )\n }\n}\n\nimpl TextDocument for TransactionDocument {\n type CompactTextDocument_ = TransactionDocument;\n\n fn as_text(\u0026self) -\u003e \u0026str {\n if let Some(ref text) = self.text {\n text\n } else {\n fatal_error!(\"Try to get text of tx whti None text !\")\n }\n }\n\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_ {\n self.clone()\n }\n}\n\n/// Transaction document builder.\n#[derive(Debug, Copy, Clone)]\npub struct TransactionDocumentBuilder\u003c'a\u003e {\n /// Document currency.\n pub currency: \u0026'a str,\n /// Reference blockstamp.\n pub blockstamp: \u0026'a Blockstamp,\n /// Locktime\n pub locktime: \u0026'a u64,\n /// Transaction Document issuers.\n pub issuers: \u0026'a Vec\u003cPubKey\u003e,\n /// Transaction inputs.\n pub inputs: \u0026'a Vec\u003cTransactionInput\u003e,\n /// Inputs unlocks.\n pub unlocks: \u0026'a Vec\u003cTransactionInputUnlocks\u003e,\n /// Transaction ouputs.\n pub outputs: \u0026'a Vec\u003cTransactionOutput\u003e,\n /// Transaction comment\n pub comment: \u0026'a str,\n /// Transaction hash\n pub hash: Option\u003cHash\u003e,\n}\n\nimpl\u003c'a\u003e TransactionDocumentBuilder\u003c'a\u003e {\n fn build_with_text_and_sigs(self, text: String, signatures: Vec\u003cSig\u003e) -\u003e TransactionDocument {\n TransactionDocument {\n text: Some(text),\n currency: self.currency.to_string(),\n blockstamp: *self.blockstamp,\n locktime: *self.locktime,\n issuers: self.issuers.clone(),\n inputs: self.inputs.clone(),\n unlocks: self.unlocks.clone(),\n outputs: self.outputs.clone(),\n comment: String::from(self.comment),\n signatures,\n hash: self.hash,\n }\n }\n}\n\nimpl\u003c'a\u003e DocumentBuilder for TransactionDocumentBuilder\u003c'a\u003e {\n type Document = TransactionDocument;\n type PrivateKey = PrivKey;\n\n fn build_with_signature(\u0026self, signatures: Vec\u003cSig\u003e) -\u003e TransactionDocument {\n self.build_with_text_and_sigs(self.generate_text(), signatures)\n }\n\n fn build_and_sign(\u0026self, private_keys: Vec\u003cPrivKey\u003e) -\u003e TransactionDocument {\n let (text, signatures) = self.build_signed_text(private_keys);\n self.build_with_text_and_sigs(text, signatures)\n }\n}\n\nimpl\u003c'a\u003e TextDocumentBuilder for TransactionDocumentBuilder\u003c'a\u003e {\n fn generate_text(\u0026self) -\u003e String {\n let mut issuers_string: String = \"\".to_owned();\n let mut inputs_string: String = \"\".to_owned();\n let mut unlocks_string: String = \"\".to_owned();\n let mut outputs_string: String = \"\".to_owned();\n for issuer in self.issuers {\n issuers_string.push_str(\u0026format!(\"{}\\n\", issuer.to_string()))\n }\n for input in self.inputs {\n inputs_string.push_str(\u0026format!(\"{}\\n\", input.to_string()))\n }\n for unlock in self.unlocks {\n unlocks_string.push_str(\u0026format!(\"{}\\n\", unlock.to_string()))\n }\n for output in self.outputs {\n outputs_string.push_str(\u0026format!(\"{}\\n\", output.to_string()))\n }\n format!(\n \"Version: 10\nType: Transaction\nCurrency: {currency}\nBlockstamp: {blockstamp}\nLocktime: {locktime}\nIssuers:\n{issuers}Inputs:\n{inputs}Unlocks:\n{unlocks}Outputs:\n{outputs}Comment: {comment}\n\",\n currency = self.currency,\n blockstamp = self.blockstamp,\n locktime = self.locktime,\n issuers = issuers_string,\n inputs = inputs_string,\n unlocks = unlocks_string,\n outputs = outputs_string,\n comment = self.comment,\n )\n }\n}\n\n/// Transaction document parser\n#[derive(Debug, Clone, Copy)]\npub struct TransactionDocumentParser;\n\nimpl TextDocumentParser\u003cRule\u003e for TransactionDocumentParser {\n type DocumentType = TransactionDocument;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let mut tx_pairs = DocumentsParser::parse(Rule::tx, doc)?;\n let tx_pair = tx_pairs.next().unwrap(); // get and unwrap the `tx` rule; never fails\n Self::from_pest_pair(tx_pair)\n }\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let tx_vx_pair = pair.into_inner().next().unwrap(); // get and unwrap the `tx_vX` rule; never fails\n\n match tx_vx_pair.as_rule() {\n Rule::tx_v10 =\u003e Ok(TransactionDocument::from_pest_pair(tx_vx_pair)?),\n _ =\u003e Err(TextDocumentParseError::UnexpectedRule(format!(\n \"{:#?}\",\n tx_vx_pair.as_rule()\n ))),\n }\n }\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 10 =\u003e Ok(TransactionDocument::from_pest_pair(pair)?),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::Document;\n\n #[test]\n fn generate_real_document() {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n );\n\n let prikey = PrivKey::Ed25519(\n ed25519::PrivateKey::from_base58(\n \"468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5G\\\n iERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7\",\n )\n .unwrap(),\n );\n\n let sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"pRQeKlzCsvPNmYAAkEP5jPPQO1RwrtFMRfCajEfkkrG0UQE0DhoTkxG3Zs2JFmvAFLw67pn1V5NQ08zsSfJkBg==\",\n ).unwrap());\n\n let block = Blockstamp::from_string(\n \"0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\",\n )\n .unwrap();\n\n let builder = TransactionDocumentBuilder {\n currency: \"duniter_unit_test_currency\",\n blockstamp: \u0026block,\n locktime: \u00260,\n issuers: \u0026vec![pubkey],\n inputs: \u0026vec![TransactionInput::D(\n TxAmount(10),\n TxBase(0),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\")\n .unwrap(),\n ),\n BlockNumber(0),\n )],\n unlocks: \u0026vec![TransactionInputUnlocks {\n index: 0,\n unlocks: vec![TransactionUnlockProof::Sig(0)],\n }],\n outputs: \u0026vec![TransactionOutput::from_str(\n \"10:0:SIG(FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa)\",\n )\n .expect(\"fail to parse output !\")],\n comment: \"test\",\n hash: None,\n };\n println!(\n \"Signature = {:?}\",\n builder.build_and_sign(vec![prikey]).signatures()\n );\n assert!(builder\n .build_with_signature(vec![sig])\n .verify_signatures()\n .is_ok());\n assert!(builder\n .build_and_sign(vec![prikey])\n .verify_signatures()\n .is_ok());\n }\n\n #[test]\n fn compute_transaction_hash() {\n let pubkey = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"FEkbc4BfJukSWnCU6Hed6dgwwTuPFTVdgz5LpL4iHr9J\")\n .unwrap(),\n );\n\n let sig = Sig::Ed25519(ed25519::Signature::from_base64(\n \"XEwKwKF8AI1gWPT7elR4IN+bW3Qn02Dk15TEgrKtY/S2qfZsNaodsLofqHLI24BBwZ5aadpC88ntmjo/UW9oDQ==\",\n ).unwrap());\n\n let block = Blockstamp::from_string(\n \"60-00001FE00410FCD5991EDD18AA7DDF15F4C8393A64FA92A1DB1C1CA2E220128D\",\n )\n .unwrap();\n\n let builder = TransactionDocumentBuilder {\n currency: \"g1\",\n blockstamp: \u0026block,\n locktime: \u00260,\n issuers: \u0026vec![pubkey],\n inputs: \u0026vec![TransactionInput::T(\n TxAmount(950),\n TxBase(0),\n Hash::from_hex(\"2CF1ACD8FE8DC93EE39A1D55881C50D87C55892AE8E4DB71D4EBAB3D412AA8FD\")\n .unwrap(),\n TxIndex(1),\n )],\n unlocks: \u0026vec![\n TransactionInputUnlocks::from_str(\"0:SIG(0)\").expect(\"fail to parse unlock !\")\n ],\n outputs: \u0026vec![\n TransactionOutput::from_str(\n \"30:0:SIG(38MEAZN68Pz1DTvT3tqgxx4yQP6snJCQhPqEFxbDk4aE)\",\n )\n .expect(\"fail to parse output !\"),\n TransactionOutput::from_str(\n \"920:0:SIG(FEkbc4BfJukSWnCU6Hed6dgwwTuPFTVdgz5LpL4iHr9J)\",\n )\n .expect(\"fail to parse output !\"),\n ],\n comment: \"Pour cesium merci\",\n hash: None,\n };\n let mut tx_doc = builder.build_with_signature(vec![sig]);\n tx_doc.hash = None;\n assert!(tx_doc.verify_signatures().is_ok());\n assert_eq!(\n tx_doc.get_hash(),\n Hash::from_hex(\"876D2430E0B66E2CE4467866D8F923D68896CACD6AA49CDD8BDD0096B834DEF1\")\n .expect(\"fail to parse hash\")\n );\n }\n\n #[test]\n fn parse_transaction_document() {\n let doc = \"Version: 10\nType: Transaction\nCurrency: duniter_unit_test_currency\nBlockstamp: 204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B\nLocktime: 0\nIssuers:\nDNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\n4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR\nFD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa\nInputs:\n40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2\n70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8\n20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46\n70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3\n20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5\n15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46\nUnlocks:\n0:SIG(0)\n1:XHX(7665798292)\n2:SIG(0)\n3:SIG(0) SIG(2)\n4:SIG(0) SIG(1) SIG(2)\n5:SIG(2)\nOutputs:\n120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)\n146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)\n49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA))\nComment: -----@@@----- (why not this comment?)\nkL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw==\ne3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw==\nw69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw==\";\n\n let doc = TransactionDocumentParser::parse(doc)\n .expect(\"fail to parse test transaction document !\");\n //println!(\"Doc : {:?}\", doc);\n println!(\"{}\", doc.generate_compact_text());\n assert!(doc.verify_signatures().is_ok());\n assert_eq!(\n doc.generate_compact_text(),\n \"TX:10:3:6:6:3:1:0\n204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B\nDNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV\n4tNQ7d9pj2Da5wUVoW9mFn7JjuPoowF977au8DdhEjVR\nFD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa\n40:2:T:6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3:2\n70:2:T:3A09A20E9014110FD224889F13357BAB4EC78A72F95CA03394D8CCA2936A7435:8\n20:2:D:DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV:46\n70:2:T:A0D9B4CDC113ECE1145C5525873821398890AE842F4B318BD076095A23E70956:3\n20:2:T:67F2045B5318777CC52CD38B424F3E40DDA823FA0364625F124BABE0030E7B5B:5\n15:2:D:FD9wujR7KABw88RyKEGBYRLz8PA6jzVCbcBAsrBXBqSa:46\n0:SIG(0)\n1:XHX(7665798292)\n2:SIG(0)\n3:SIG(0) SIG(2)\n4:SIG(0) SIG(1) SIG(2)\n5:SIG(2)\n120:2:SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)\n146:2:SIG(DSz4rgncXCytsUMW2JU2yhLquZECD2XpEkpP9gG5HyAx)\n49:2:(SIG(6DyGr5LFtFmbaJYRvcs9WmBsr4cbJbJ1EV9zBbqG7A6i) || XHX(3EB4702F2AC2FD3FA4FDC46A4FC05AE8CDEE1A85F2AC2FD3FA4FDC46A4FC01CA))\n-----@@@----- (why not this comment?)\nkL59C1izKjcRN429AlKdshwhWbasvyL7sthI757zm1DfZTdTIctDWlKbYeG/tS7QyAgI3gcfrTHPhu1E1lKCBw==\ne3LpgB2RZ/E/BCxPJsn+TDDyxGYzrIsMyDt//KhJCjIQD6pNUxr5M5jrq2OwQZgwmz91YcmoQ2XRQAUDpe4BAw==\nw69bYgiQxDmCReB0Dugt9BstXlAKnwJkKCdWvCeZ9KnUCv0FJys6klzYk/O/b9t74tYhWZSX0bhETWHiwfpWBw==\"\n );\n }\n}\n","traces":[{"line":37,"address":null,"length":0,"stats":{"Line":1}},{"line":38,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":67,"address":null,"length":0,"stats":{"Line":3}},{"line":68,"address":null,"length":0,"stats":{"Line":3}},{"line":69,"address":null,"length":0,"stats":{"Line":3}},{"line":70,"address":null,"length":0,"stats":{"Line":3}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":3}},{"line":81,"address":null,"length":0,"stats":{"Line":3}},{"line":82,"address":null,"length":0,"stats":{"Line":3}},{"line":83,"address":null,"length":0,"stats":{"Line":3}},{"line":84,"address":5034465,"length":1,"stats":{"Line":3}},{"line":87,"address":null,"length":0,"stats":{"Line":3}},{"line":88,"address":null,"length":0,"stats":{"Line":3}},{"line":89,"address":null,"length":0,"stats":{"Line":3}},{"line":90,"address":null,"length":0,"stats":{"Line":3}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":3}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":5035589,"length":1,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":5036634,"length":1,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":3}},{"line":115,"address":null,"length":0,"stats":{"Line":3}},{"line":116,"address":null,"length":0,"stats":{"Line":3}},{"line":117,"address":null,"length":0,"stats":{"Line":3}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":3}},{"line":176,"address":null,"length":0,"stats":{"Line":3}},{"line":177,"address":null,"length":0,"stats":{"Line":3}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":193,"address":null,"length":0,"stats":{"Line":3}},{"line":194,"address":null,"length":0,"stats":{"Line":3}},{"line":195,"address":null,"length":0,"stats":{"Line":3}},{"line":196,"address":null,"length":0,"stats":{"Line":3}},{"line":198,"address":null,"length":0,"stats":{"Line":3}},{"line":199,"address":null,"length":0,"stats":{"Line":3}},{"line":200,"address":null,"length":0,"stats":{"Line":3}},{"line":205,"address":null,"length":0,"stats":{"Line":3}},{"line":206,"address":null,"length":0,"stats":{"Line":3}},{"line":207,"address":null,"length":0,"stats":{"Line":3}},{"line":208,"address":null,"length":0,"stats":{"Line":3}},{"line":210,"address":null,"length":0,"stats":{"Line":3}},{"line":211,"address":null,"length":0,"stats":{"Line":3}},{"line":212,"address":null,"length":0,"stats":{"Line":3}},{"line":213,"address":null,"length":0,"stats":{"Line":3}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":1}},{"line":222,"address":null,"length":0,"stats":{"Line":1}},{"line":224,"address":5042493,"length":1,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":3}},{"line":238,"address":null,"length":0,"stats":{"Line":3}},{"line":239,"address":null,"length":0,"stats":{"Line":3}},{"line":240,"address":null,"length":0,"stats":{"Line":3}},{"line":242,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":1}},{"line":264,"address":null,"length":0,"stats":{"Line":1}},{"line":265,"address":null,"length":0,"stats":{"Line":1}},{"line":266,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":268,"address":null,"length":0,"stats":{"Line":0}},{"line":285,"address":null,"length":0,"stats":{"Line":1}},{"line":286,"address":null,"length":0,"stats":{"Line":1}},{"line":287,"address":null,"length":0,"stats":{"Line":1}},{"line":289,"address":null,"length":0,"stats":{"Line":1}},{"line":293,"address":null,"length":0,"stats":{"Line":0}},{"line":294,"address":null,"length":0,"stats":{"Line":0}},{"line":295,"address":null,"length":0,"stats":{"Line":0}},{"line":300,"address":null,"length":0,"stats":{"Line":3}},{"line":301,"address":null,"length":0,"stats":{"Line":3}},{"line":302,"address":null,"length":0,"stats":{"Line":3}},{"line":303,"address":null,"length":0,"stats":{"Line":0}},{"line":304,"address":null,"length":0,"stats":{"Line":0}},{"line":324,"address":5099424,"length":1,"stats":{"Line":1}},{"line":325,"address":5099445,"length":1,"stats":{"Line":1}},{"line":326,"address":5099804,"length":1,"stats":{"Line":1}},{"line":327,"address":5099533,"length":1,"stats":{"Line":1}},{"line":328,"address":5099679,"length":1,"stats":{"Line":1}},{"line":330,"address":5099848,"length":1,"stats":{"Line":0}},{"line":331,"address":5099888,"length":1,"stats":{"Line":0}},{"line":332,"address":5099940,"length":1,"stats":{"Line":0}},{"line":333,"address":5099994,"length":1,"stats":{"Line":0}},{"line":334,"address":5100002,"length":1,"stats":{"Line":0}},{"line":335,"address":5100168,"length":1,"stats":{"Line":0}},{"line":337,"address":5100350,"length":1,"stats":{"Line":0}},{"line":338,"address":5100373,"length":1,"stats":{"Line":0}},{"line":339,"address":5100393,"length":1,"stats":{"Line":0}},{"line":354,"address":null,"length":0,"stats":{"Line":3}},{"line":355,"address":null,"length":0,"stats":{"Line":3}},{"line":356,"address":null,"length":0,"stats":{"Line":3}},{"line":357,"address":null,"length":0,"stats":{"Line":0}},{"line":358,"address":null,"length":0,"stats":{"Line":0}},{"line":359,"address":null,"length":0,"stats":{"Line":0}},{"line":360,"address":null,"length":0,"stats":{"Line":0}},{"line":361,"address":null,"length":0,"stats":{"Line":0}},{"line":362,"address":null,"length":0,"stats":{"Line":0}},{"line":365,"address":null,"length":0,"stats":{"Line":0}},{"line":366,"address":null,"length":0,"stats":{"Line":1}},{"line":367,"address":null,"length":0,"stats":{"Line":0}},{"line":368,"address":null,"length":0,"stats":{"Line":1}},{"line":369,"address":null,"length":0,"stats":{"Line":1}},{"line":370,"address":null,"length":0,"stats":{"Line":0}},{"line":373,"address":null,"length":0,"stats":{"Line":0}},{"line":374,"address":null,"length":0,"stats":{"Line":3}},{"line":375,"address":null,"length":0,"stats":{"Line":3}},{"line":376,"address":null,"length":0,"stats":{"Line":0}},{"line":379,"address":null,"length":0,"stats":{"Line":1}},{"line":380,"address":null,"length":0,"stats":{"Line":1}},{"line":382,"address":null,"length":0,"stats":{"Line":0}},{"line":383,"address":null,"length":0,"stats":{"Line":0}},{"line":385,"address":null,"length":0,"stats":{"Line":0}},{"line":386,"address":null,"length":0,"stats":{"Line":0}},{"line":387,"address":null,"length":0,"stats":{"Line":0}},{"line":390,"address":5049463,"length":1,"stats":{"Line":0}},{"line":396,"address":null,"length":0,"stats":{"Line":1}},{"line":397,"address":null,"length":0,"stats":{"Line":1}},{"line":398,"address":null,"length":0,"stats":{"Line":1}},{"line":399,"address":null,"length":0,"stats":{"Line":0}},{"line":400,"address":null,"length":0,"stats":{"Line":0}},{"line":402,"address":null,"length":0,"stats":{"Line":0}},{"line":404,"address":null,"length":0,"stats":{"Line":0}},{"line":405,"address":null,"length":0,"stats":{"Line":0}},{"line":407,"address":null,"length":0,"stats":{"Line":0}},{"line":409,"address":null,"length":0,"stats":{"Line":0}},{"line":410,"address":null,"length":0,"stats":{"Line":0}},{"line":429,"address":null,"length":0,"stats":{"Line":1}},{"line":430,"address":null,"length":0,"stats":{"Line":1}},{"line":433,"address":null,"length":0,"stats":{"Line":0}},{"line":434,"address":null,"length":0,"stats":{"Line":0}},{"line":439,"address":null,"length":0,"stats":{"Line":3}},{"line":440,"address":null,"length":0,"stats":{"Line":3}},{"line":442,"address":null,"length":0,"stats":{"Line":3}},{"line":443,"address":null,"length":0,"stats":{"Line":3}},{"line":444,"address":null,"length":0,"stats":{"Line":3}},{"line":450,"address":null,"length":0,"stats":{"Line":3}},{"line":451,"address":null,"length":0,"stats":{"Line":3}},{"line":452,"address":null,"length":0,"stats":{"Line":3}},{"line":453,"address":null,"length":0,"stats":{"Line":3}},{"line":454,"address":null,"length":0,"stats":{"Line":3}},{"line":458,"address":null,"length":0,"stats":{"Line":3}},{"line":469,"address":null,"length":0,"stats":{"Line":3}},{"line":470,"address":null,"length":0,"stats":{"Line":3}},{"line":471,"address":null,"length":0,"stats":{"Line":3}},{"line":472,"address":null,"length":0,"stats":{"Line":3}},{"line":473,"address":null,"length":0,"stats":{"Line":3}},{"line":475,"address":null,"length":0,"stats":{"Line":3}},{"line":477,"address":null,"length":0,"stats":{"Line":3}},{"line":478,"address":null,"length":0,"stats":{"Line":0}},{"line":479,"address":null,"length":0,"stats":{"Line":3}},{"line":480,"address":null,"length":0,"stats":{"Line":3}},{"line":481,"address":null,"length":0,"stats":{"Line":3}},{"line":483,"address":null,"length":0,"stats":{"Line":0}},{"line":484,"address":null,"length":0,"stats":{"Line":0}},{"line":487,"address":null,"length":0,"stats":{"Line":3}},{"line":488,"address":null,"length":0,"stats":{"Line":3}},{"line":489,"address":null,"length":0,"stats":{"Line":0}},{"line":490,"address":null,"length":0,"stats":{"Line":0}},{"line":491,"address":null,"length":0,"stats":{"Line":0}},{"line":492,"address":null,"length":0,"stats":{"Line":0}},{"line":494,"address":null,"length":0,"stats":{"Line":3}},{"line":495,"address":null,"length":0,"stats":{"Line":3}},{"line":496,"address":null,"length":0,"stats":{"Line":0}},{"line":497,"address":null,"length":0,"stats":{"Line":3}},{"line":498,"address":null,"length":0,"stats":{"Line":3}},{"line":499,"address":null,"length":0,"stats":{"Line":3}},{"line":501,"address":null,"length":0,"stats":{"Line":0}},{"line":502,"address":null,"length":0,"stats":{"Line":0}},{"line":503,"address":null,"length":0,"stats":{"Line":0}},{"line":571,"address":null,"length":0,"stats":{"Line":0}},{"line":573,"address":null,"length":0,"stats":{"Line":0}},{"line":574,"address":null,"length":0,"stats":{"Line":0}},{"line":575,"address":null,"length":0,"stats":{"Line":0}},{"line":576,"address":null,"length":0,"stats":{"Line":0}},{"line":577,"address":null,"length":0,"stats":{"Line":0}},{"line":582,"address":null,"length":0,"stats":{"Line":0}},{"line":587,"address":null,"length":0,"stats":{"Line":0}},{"line":592,"address":null,"length":0,"stats":{"Line":0}},{"line":593,"address":null,"length":0,"stats":{"Line":0}},{"line":594,"address":null,"length":0,"stats":{"Line":0}},{"line":605,"address":null,"length":0,"stats":{"Line":2}},{"line":606,"address":null,"length":0,"stats":{"Line":2}},{"line":607,"address":null,"length":0,"stats":{"Line":2}},{"line":608,"address":null,"length":0,"stats":{"Line":0}},{"line":609,"address":null,"length":0,"stats":{"Line":0}},{"line":611,"address":null,"length":0,"stats":{"Line":2}},{"line":612,"address":null,"length":0,"stats":{"Line":2}},{"line":613,"address":null,"length":0,"stats":{"Line":2}},{"line":616,"address":null,"length":0,"stats":{"Line":2}},{"line":617,"address":null,"length":0,"stats":{"Line":2}},{"line":620,"address":null,"length":0,"stats":{"Line":0}},{"line":621,"address":null,"length":0,"stats":{"Line":0}},{"line":624,"address":null,"length":0,"stats":{"Line":2}},{"line":625,"address":null,"length":0,"stats":{"Line":2}},{"line":626,"address":null,"length":0,"stats":{"Line":0}},{"line":627,"address":null,"length":0,"stats":{"Line":0}},{"line":628,"address":null,"length":0,"stats":{"Line":2}},{"line":632,"address":null,"length":0,"stats":{"Line":1}},{"line":633,"address":null,"length":0,"stats":{"Line":1}},{"line":636,"address":null,"length":0,"stats":{"Line":1}},{"line":637,"address":null,"length":0,"stats":{"Line":1}},{"line":640,"address":null,"length":0,"stats":{"Line":1}},{"line":641,"address":null,"length":0,"stats":{"Line":1}},{"line":642,"address":null,"length":0,"stats":{"Line":1}},{"line":643,"address":null,"length":0,"stats":{"Line":1}},{"line":644,"address":null,"length":0,"stats":{"Line":1}},{"line":648,"address":null,"length":0,"stats":{"Line":1}},{"line":649,"address":null,"length":0,"stats":{"Line":1}},{"line":650,"address":null,"length":0,"stats":{"Line":1}},{"line":651,"address":null,"length":0,"stats":{"Line":1}},{"line":652,"address":null,"length":0,"stats":{"Line":1}},{"line":653,"address":null,"length":0,"stats":{"Line":1}},{"line":654,"address":null,"length":0,"stats":{"Line":1}},{"line":655,"address":null,"length":0,"stats":{"Line":1}},{"line":656,"address":null,"length":0,"stats":{"Line":1}},{"line":657,"address":null,"length":0,"stats":{"Line":1}},{"line":658,"address":null,"length":0,"stats":{"Line":1}},{"line":660,"address":null,"length":0,"stats":{"Line":1}},{"line":661,"address":null,"length":0,"stats":{"Line":1}},{"line":662,"address":null,"length":0,"stats":{"Line":1}},{"line":663,"address":null,"length":0,"stats":{"Line":0}},{"line":664,"address":5063870,"length":1,"stats":{"Line":1}},{"line":666,"address":null,"length":0,"stats":{"Line":1}},{"line":667,"address":null,"length":0,"stats":{"Line":1}},{"line":668,"address":null,"length":0,"stats":{"Line":1}},{"line":669,"address":5064271,"length":1,"stats":{"Line":1}},{"line":670,"address":5064378,"length":1,"stats":{"Line":1}},{"line":673,"address":5064538,"length":1,"stats":{"Line":1}},{"line":674,"address":null,"length":0,"stats":{"Line":1}},{"line":675,"address":5064656,"length":1,"stats":{"Line":1}},{"line":677,"address":null,"length":0,"stats":{"Line":1}},{"line":678,"address":null,"length":0,"stats":{"Line":0}},{"line":679,"address":null,"length":0,"stats":{"Line":1}},{"line":681,"address":null,"length":0,"stats":{"Line":0}},{"line":682,"address":null,"length":0,"stats":{"Line":1}},{"line":684,"address":null,"length":0,"stats":{"Line":1}},{"line":685,"address":null,"length":0,"stats":{"Line":0}},{"line":686,"address":null,"length":0,"stats":{"Line":1}},{"line":687,"address":5065259,"length":1,"stats":{"Line":1}},{"line":690,"address":null,"length":0,"stats":{"Line":0}},{"line":691,"address":5065456,"length":1,"stats":{"Line":0}},{"line":695,"address":null,"length":0,"stats":{"Line":1}},{"line":696,"address":null,"length":0,"stats":{"Line":1}},{"line":697,"address":null,"length":0,"stats":{"Line":1}},{"line":698,"address":null,"length":0,"stats":{"Line":1}},{"line":699,"address":null,"length":0,"stats":{"Line":1}},{"line":700,"address":null,"length":0,"stats":{"Line":1}},{"line":701,"address":null,"length":0,"stats":{"Line":1}},{"line":702,"address":null,"length":0,"stats":{"Line":1}},{"line":703,"address":null,"length":0,"stats":{"Line":1}},{"line":704,"address":null,"length":0,"stats":{"Line":1}},{"line":705,"address":null,"length":0,"stats":{"Line":1}},{"line":706,"address":null,"length":0,"stats":{"Line":1}},{"line":714,"address":null,"length":0,"stats":{"Line":0}},{"line":715,"address":null,"length":0,"stats":{"Line":0}},{"line":718,"address":null,"length":0,"stats":{"Line":0}},{"line":719,"address":null,"length":0,"stats":{"Line":0}},{"line":722,"address":null,"length":0,"stats":{"Line":0}},{"line":723,"address":null,"length":0,"stats":{"Line":0}},{"line":726,"address":null,"length":0,"stats":{"Line":2}},{"line":727,"address":null,"length":0,"stats":{"Line":2}},{"line":730,"address":null,"length":0,"stats":{"Line":2}},{"line":731,"address":null,"length":0,"stats":{"Line":2}},{"line":734,"address":null,"length":0,"stats":{"Line":2}},{"line":735,"address":null,"length":0,"stats":{"Line":2}},{"line":740,"address":null,"length":0,"stats":{"Line":1}},{"line":741,"address":null,"length":0,"stats":{"Line":1}},{"line":742,"address":null,"length":0,"stats":{"Line":1}},{"line":743,"address":null,"length":0,"stats":{"Line":1}},{"line":744,"address":null,"length":0,"stats":{"Line":1}},{"line":746,"address":null,"length":0,"stats":{"Line":1}},{"line":747,"address":null,"length":0,"stats":{"Line":1}},{"line":748,"address":null,"length":0,"stats":{"Line":1}},{"line":749,"address":null,"length":0,"stats":{"Line":1}},{"line":751,"address":null,"length":0,"stats":{"Line":1}},{"line":752,"address":null,"length":0,"stats":{"Line":1}},{"line":753,"address":null,"length":0,"stats":{"Line":1}},{"line":754,"address":null,"length":0,"stats":{"Line":1}},{"line":756,"address":null,"length":0,"stats":{"Line":1}},{"line":757,"address":null,"length":0,"stats":{"Line":1}},{"line":758,"address":null,"length":0,"stats":{"Line":1}},{"line":759,"address":null,"length":0,"stats":{"Line":1}},{"line":761,"address":null,"length":0,"stats":{"Line":1}},{"line":762,"address":null,"length":0,"stats":{"Line":1}},{"line":763,"address":null,"length":0,"stats":{"Line":1}},{"line":765,"address":null,"length":0,"stats":{"Line":1}},{"line":766,"address":null,"length":0,"stats":{"Line":1}},{"line":767,"address":null,"length":0,"stats":{"Line":1}},{"line":768,"address":null,"length":0,"stats":{"Line":1}},{"line":771,"address":null,"length":0,"stats":{"Line":1}},{"line":772,"address":null,"length":0,"stats":{"Line":1}},{"line":775,"address":null,"length":0,"stats":{"Line":1}},{"line":776,"address":null,"length":0,"stats":{"Line":1}},{"line":777,"address":null,"length":0,"stats":{"Line":1}},{"line":778,"address":null,"length":0,"stats":{"Line":1}},{"line":779,"address":null,"length":0,"stats":{"Line":1}},{"line":780,"address":null,"length":0,"stats":{"Line":1}},{"line":781,"address":null,"length":0,"stats":{"Line":1}},{"line":782,"address":null,"length":0,"stats":{"Line":0}},{"line":783,"address":null,"length":0,"stats":{"Line":0}},{"line":784,"address":null,"length":0,"stats":{"Line":0}},{"line":785,"address":null,"length":0,"stats":{"Line":0}},{"line":786,"address":null,"length":0,"stats":{"Line":0}},{"line":787,"address":null,"length":0,"stats":{"Line":0}},{"line":795,"address":null,"length":0,"stats":{"Line":2}},{"line":796,"address":null,"length":0,"stats":{"Line":2}},{"line":797,"address":null,"length":0,"stats":{"Line":2}},{"line":798,"address":null,"length":0,"stats":{"Line":0}},{"line":799,"address":null,"length":0,"stats":{"Line":0}},{"line":803,"address":null,"length":0,"stats":{"Line":1}},{"line":804,"address":null,"length":0,"stats":{"Line":1}},{"line":832,"address":null,"length":0,"stats":{"Line":3}},{"line":834,"address":null,"length":0,"stats":{"Line":3}},{"line":835,"address":null,"length":0,"stats":{"Line":3}},{"line":836,"address":null,"length":0,"stats":{"Line":3}},{"line":837,"address":null,"length":0,"stats":{"Line":3}},{"line":838,"address":null,"length":0,"stats":{"Line":3}},{"line":839,"address":null,"length":0,"stats":{"Line":3}},{"line":840,"address":null,"length":0,"stats":{"Line":3}},{"line":841,"address":null,"length":0,"stats":{"Line":3}},{"line":842,"address":null,"length":0,"stats":{"Line":3}},{"line":844,"address":null,"length":0,"stats":{"Line":3}},{"line":853,"address":null,"length":0,"stats":{"Line":3}},{"line":854,"address":null,"length":0,"stats":{"Line":3}},{"line":857,"address":null,"length":0,"stats":{"Line":1}},{"line":858,"address":null,"length":0,"stats":{"Line":1}},{"line":859,"address":null,"length":0,"stats":{"Line":1}},{"line":864,"address":null,"length":0,"stats":{"Line":3}},{"line":865,"address":null,"length":0,"stats":{"Line":3}},{"line":866,"address":null,"length":0,"stats":{"Line":3}},{"line":867,"address":null,"length":0,"stats":{"Line":3}},{"line":868,"address":null,"length":0,"stats":{"Line":3}},{"line":869,"address":null,"length":0,"stats":{"Line":3}},{"line":870,"address":null,"length":0,"stats":{"Line":3}},{"line":872,"address":null,"length":0,"stats":{"Line":3}},{"line":873,"address":null,"length":0,"stats":{"Line":3}},{"line":875,"address":null,"length":0,"stats":{"Line":3}},{"line":876,"address":null,"length":0,"stats":{"Line":3}},{"line":878,"address":null,"length":0,"stats":{"Line":3}},{"line":879,"address":null,"length":0,"stats":{"Line":3}},{"line":881,"address":null,"length":0,"stats":{"Line":3}},{"line":893,"address":null,"length":0,"stats":{"Line":3}},{"line":894,"address":null,"length":0,"stats":{"Line":3}},{"line":895,"address":null,"length":0,"stats":{"Line":3}},{"line":896,"address":null,"length":0,"stats":{"Line":0}},{"line":897,"address":null,"length":0,"stats":{"Line":0}},{"line":898,"address":null,"length":0,"stats":{"Line":0}},{"line":899,"address":null,"length":0,"stats":{"Line":0}},{"line":900,"address":null,"length":0,"stats":{"Line":3}},{"line":912,"address":null,"length":0,"stats":{"Line":1}},{"line":913,"address":null,"length":0,"stats":{"Line":1}},{"line":914,"address":5083086,"length":1,"stats":{"Line":1}},{"line":915,"address":null,"length":0,"stats":{"Line":1}},{"line":918,"address":null,"length":0,"stats":{"Line":1}},{"line":919,"address":5083338,"length":1,"stats":{"Line":1}},{"line":921,"address":null,"length":0,"stats":{"Line":1}},{"line":922,"address":null,"length":0,"stats":{"Line":1}},{"line":923,"address":null,"length":0,"stats":{"Line":0}},{"line":924,"address":null,"length":0,"stats":{"Line":0}},{"line":925,"address":null,"length":0,"stats":{"Line":0}},{"line":930,"address":null,"length":0,"stats":{"Line":0}},{"line":934,"address":null,"length":0,"stats":{"Line":0}},{"line":935,"address":null,"length":0,"stats":{"Line":0}},{"line":936,"address":null,"length":0,"stats":{"Line":0}},{"line":937,"address":null,"length":0,"stats":{"Line":0}},{"line":938,"address":null,"length":0,"stats":{"Line":0}},{"line":950,"address":5005024,"length":1,"stats":{"Line":2}},{"line":951,"address":5689552,"length":1,"stats":{"Line":1}},{"line":952,"address":5689486,"length":1,"stats":{"Line":1}},{"line":956,"address":5689673,"length":1,"stats":{"Line":1}},{"line":957,"address":5689624,"length":1,"stats":{"Line":1}},{"line":964,"address":5689725,"length":1,"stats":{"Line":1}},{"line":968,"address":5689828,"length":1,"stats":{"Line":1}},{"line":973,"address":5690805,"length":1,"stats":{"Line":1}},{"line":977,"address":5689872,"length":1,"stats":{"Line":1}},{"line":978,"address":5689999,"length":1,"stats":{"Line":1}},{"line":979,"address":5690004,"length":1,"stats":{"Line":1}},{"line":980,"address":5690016,"length":1,"stats":{"Line":1}},{"line":981,"address":5690101,"length":1,"stats":{"Line":1}},{"line":982,"address":5690028,"length":1,"stats":{"Line":1}},{"line":985,"address":5690141,"length":1,"stats":{"Line":1}},{"line":987,"address":5690337,"length":1,"stats":{"Line":1}},{"line":989,"address":5690376,"length":1,"stats":{"Line":1}},{"line":991,"address":5690597,"length":1,"stats":{"Line":1}},{"line":996,"address":5690797,"length":1,"stats":{"Line":1}},{"line":998,"address":5691250,"length":1,"stats":{"Line":0}},{"line":1000,"address":5691010,"length":1,"stats":{"Line":1}},{"line":1002,"address":5691648,"length":1,"stats":{"Line":1}},{"line":1003,"address":5691453,"length":1,"stats":{"Line":1}},{"line":1006,"address":5692018,"length":1,"stats":{"Line":1}},{"line":1007,"address":5691823,"length":1,"stats":{"Line":1}},{"line":1013,"address":5005056,"length":1,"stats":{"Line":2}},{"line":1014,"address":5692592,"length":1,"stats":{"Line":1}},{"line":1015,"address":5692526,"length":1,"stats":{"Line":1}},{"line":1019,"address":5692649,"length":1,"stats":{"Line":1}},{"line":1023,"address":5692747,"length":1,"stats":{"Line":1}},{"line":1028,"address":5693749,"length":1,"stats":{"Line":1}},{"line":1032,"address":5692791,"length":1,"stats":{"Line":1}},{"line":1033,"address":5692915,"length":1,"stats":{"Line":1}},{"line":1034,"address":5692928,"length":1,"stats":{"Line":1}},{"line":1035,"address":5692940,"length":1,"stats":{"Line":1}},{"line":1036,"address":5692952,"length":1,"stats":{"Line":1}},{"line":1038,"address":5693025,"length":1,"stats":{"Line":1}},{"line":1040,"address":5693210,"length":1,"stats":{"Line":1}},{"line":1041,"address":5693231,"length":1,"stats":{"Line":1}},{"line":1043,"address":5693380,"length":1,"stats":{"Line":1}},{"line":1044,"address":5693393,"length":1,"stats":{"Line":1}},{"line":1048,"address":5693473,"length":1,"stats":{"Line":1}},{"line":1054,"address":5693741,"length":1,"stats":{"Line":1}},{"line":1056,"address":5693954,"length":1,"stats":{"Line":1}},{"line":1057,"address":5694174,"length":1,"stats":{"Line":1}},{"line":1058,"address":5694198,"length":1,"stats":{"Line":1}},{"line":1059,"address":5694415,"length":1,"stats":{"Line":1}},{"line":1060,"address":5694338,"length":1,"stats":{"Line":1}},{"line":1061,"address":5694345,"length":1,"stats":{"Line":1}},{"line":1067,"address":5005088,"length":1,"stats":{"Line":2}},{"line":1068,"address":5695070,"length":1,"stats":{"Line":1}},{"line":1100,"address":5695084,"length":1,"stats":{"Line":1}},{"line":1103,"address":5695170,"length":1,"stats":{"Line":1}},{"line":1104,"address":5695389,"length":1,"stats":{"Line":1}},{"line":1105,"address":5695529,"length":1,"stats":{"Line":1}},{"line":1106,"address":5695514,"length":1,"stats":{"Line":1}}],"covered":314,"coverable":434},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Implements the Dunitrust Documents Protocol.\n\n#![deny(\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces\n)]\n\n#[macro_use]\nextern crate log;\n#[macro_use]\nextern crate failure;\n#[macro_use]\nextern crate pest_derive;\n#[cfg(test)]\n#[macro_use]\nextern crate pretty_assertions;\n#[macro_use]\nextern crate serde_derive;\n\npub mod blockstamp;\npub mod documents;\npub mod parsers;\npub mod text_document_traits;\n\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\nuse pest::iterators::Pair;\nuse pest::RuleType;\nuse serde::Serialize;\nuse std::cmp::Ordering;\nuse std::fmt::{Debug, Display, Error, Formatter};\nuse std::net::AddrParseError;\n\npub use crate::blockstamp::{Blockstamp, PreviousBlockstamp};\n\n#[derive(Parser)]\n#[grammar = \"documents_grammar.pest\"]\n/// Parser for Documents\nstruct DocumentsParser;\n\npub trait TextDocumentParser\u003cR: RuleType\u003e {\n /// Type of document generated by the parser\n type DocumentType;\n\n /// Parse text document from raw format\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e;\n /// Parse text document from pest pairs\n fn from_pest_pair(pairs: Pair\u003cR\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e;\n /// Parse text document from versioned pest pairs\n fn from_versioned_pest_pair(\n version: u16,\n pairs: Pair\u003cR\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e;\n}\n\n/// Error with pest parser (grammar)\n#[derive(Debug, Clone, Eq, Fail, PartialEq)]\n#[fail(display = \"Grammar error: {}\", _0)]\npub struct PestError(pub String);\n\nimpl\u003cT: pest::RuleType\u003e From\u003cpest::error::Error\u003cT\u003e\u003e for PestError {\n fn from(e: pest::error::Error\u003cT\u003e) -\u003e Self {\n PestError(format!(\"{}\", e))\n }\n}\n\n/// List of possible errors while parsing a text document.\n#[derive(Debug, Clone, Eq, Fail, PartialEq)]\npub enum TextDocumentParseError {\n /// The given source don't have a valid specific document format (document type).\n #[fail(display = \"TextDocumentParseError: Invalid inner format: {}\", _0)]\n InvalidInnerFormat(String),\n /// Ip address parse error\n #[fail(display = \"TextDocumentParseError: invalid ip: {}\", _0)]\n IpAddrError(AddrParseError),\n /// Error with pest parser\n #[fail(display = \"TextDocumentParseError: {}\", _0)]\n PestError(PestError),\n /// Unexpected rule\n #[fail(display = \"TextDocumentParseError: Unexpected rule: '{}'\", _0)]\n UnexpectedRule(String),\n /// Unexpected version\n #[fail(display = \"TextDocumentParseError: Unexpected version: '{}'\", _0)]\n UnexpectedVersion(String),\n /// Unknown type\n #[fail(display = \"TextDocumentParseError: UnknownType.\")]\n UnknownType,\n}\n\nimpl From\u003cAddrParseError\u003e for TextDocumentParseError {\n fn from(e: AddrParseError) -\u003e Self {\n TextDocumentParseError::IpAddrError(e)\n }\n}\n\nimpl From\u003cPestError\u003e for TextDocumentParseError {\n fn from(e: PestError) -\u003e Self {\n TextDocumentParseError::PestError(e)\n }\n}\n\nimpl\u003cT: pest::RuleType\u003e From\u003cpest::error::Error\u003cT\u003e\u003e for TextDocumentParseError {\n fn from(e: pest::error::Error\u003cT\u003e) -\u003e Self {\n TextDocumentParseError::PestError(e.into())\n }\n}\n\n/// A block Id.\n#[derive(Copy, Clone, Debug, Deserialize, Ord, PartialEq, PartialOrd, Eq, Hash, Serialize)]\npub struct BlockNumber(pub u32);\n\nimpl Display for BlockNumber {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.0)\n }\n}\n\n/// Wrapper of a block hash.\n#[derive(Copy, Clone, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Hash, Serialize)]\npub struct BlockHash(pub Hash);\n\nimpl Display for BlockHash {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}\", self.0.to_hex())\n }\n}\n\nimpl Debug for BlockHash {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"BlockHash({})\", self)\n }\n}\n\n/// trait providing commun methods for any documents of any protocol version.\n///\n/// # Design choice\n///\n/// Allow only ed25519 for protocol 10 and many differents\n/// schemes for protocol 11 through a proxy type.\npub trait Document: Debug + Clone + PartialEq + Eq {\n /// Type of the `PublicKey` used by the document.\n type PublicKey: PublicKey;\n\n /// Get document as bytes for signature verification.\n fn as_bytes(\u0026self) -\u003e \u0026[u8];\n\n /// Get document blockstamp\n fn blockstamp(\u0026self) -\u003e Blockstamp;\n\n /// Get document currency name.\n fn currency(\u0026self) -\u003e \u0026str;\n\n /// Iterate over document issuers.\n fn issuers(\u0026self) -\u003e \u0026Vec\u003cSelf::PublicKey\u003e;\n\n /// Some documents do not directly store the sequence of bytes that will be signed but generate\n // it on request, so these types of documents cannot provide a reference to the signed bytes.\n fn no_as_bytes(\u0026self) -\u003e bool {\n false\n }\n\n /// Get document to bytes for signature verification.\n fn to_bytes(\u0026self) -\u003e Vec\u003cu8\u003e {\n self.as_bytes().to_vec()\n }\n\n /// Iterate over document signatures.\n fn signatures(\u0026self) -\u003e \u0026Vec\u003c\u003cSelf::PublicKey as PublicKey\u003e::Signature\u003e;\n\n /// Verify one signature\n #[inline]\n fn verify_one_signature(\n \u0026self,\n public_key: \u0026Self::PublicKey,\n signature: \u0026\u003cSelf::PublicKey as PublicKey\u003e::Signature,\n ) -\u003e Result\u003c(), SigError\u003e {\n if self.no_as_bytes() {\n public_key.verify(\u0026self.to_bytes(), signature)\n } else {\n public_key.verify(self.as_bytes(), signature)\n }\n }\n\n /// Verify signatures of document content (as text format)\n fn verify_signatures(\u0026self) -\u003e Result\u003c(), DocumentSigsErr\u003e {\n let issuers_count = self.issuers().len();\n let signatures_count = self.signatures().len();\n\n if issuers_count != signatures_count {\n Err(DocumentSigsErr::IncompletePairs(\n issuers_count,\n signatures_count,\n ))\n } else {\n let issuers = self.issuers();\n let signatures = self.signatures();\n let mismatches: HashMap\u003cusize, SigError\u003e = issuers\n .iter()\n .zip(signatures)\n .enumerate()\n .filter_map(|(i, (key, signature))| {\n if let Err(e) = self.verify_one_signature(key, signature) {\n Some((i, e))\n } else {\n None\n }\n })\n .collect();\n\n if mismatches.is_empty() {\n Ok(())\n } else {\n Err(DocumentSigsErr::Invalid(mismatches))\n }\n }\n }\n\n /// Get document version.\n fn version(\u0026self) -\u003e u16;\n}\n\nuse std::collections::HashMap;\n\n// todo: à mon avis faudrait pas que y ait de Valid() dans cette enum\n// et du coup faudrait que les fonctions qui renvoient un DocumentSigsErr renvoie Result\u003c(), DocumentSigsErr\u003e\n// du coup SignatureError dans la local verif sert plus à rien.\n\n/// List of possible errors for document signatures verification.\n#[derive(Debug, Eq, PartialEq)]\npub enum DocumentSigsErr {\n /// Not same amount of issuers and signatures.\n /// (issuers count, signatures count)\n IncompletePairs(usize, usize),\n /// Signatures don't match.\n /// List of mismatching pairs indexes.\n Invalid(HashMap\u003cusize, SigError\u003e),\n}\n\n/// Trait helper for building new documents.\npub trait DocumentBuilder {\n /// Type of the builded document.\n type Document: Document;\n\n /// Type of the private keys signing the documents.\n type PrivateKey: PrivateKey\u003c\n Signature = \u003c\u003cSelf::Document as Document\u003e::PublicKey as PublicKey\u003e::Signature,\n \u003e;\n\n /// Build a document with provided signatures.\n fn build_with_signature(\n \u0026self,\n signatures: Vec\u003c\u003c\u003cSelf::Document as Document\u003e::PublicKey as PublicKey\u003e::Signature\u003e,\n ) -\u003e Self::Document;\n\n /// Build a document and sign it with the private key.\n fn build_and_sign(\u0026self, private_keys: Vec\u003cSelf::PrivateKey\u003e) -\u003e Self::Document;\n}\n\n/// Trait for a document parser from a `S` source\n/// format to a `D` document. Will return the\n/// parsed document or an `E` error.\npub trait DocumentParser\u003cS, D, E\u003e {\n /// Parse a source and return a document or an error.\n fn parse(source: S) -\u003e Result\u003cD, E\u003e;\n}\n\n/// Stringify a document\npub trait ToStringObject {\n type StringObject: Serialize;\n /// Transforms object fields into string\n fn to_string_object(\u0026self) -\u003e Self::StringObject;\n}\n\n/// Jsonify a document\npub trait ToJsonObject: ToStringObject {\n /// Convert to JSON String\n fn to_json_string(\u0026self) -\u003e Result\u003cString, serde_json::Error\u003e {\n Ok(serde_json::to_string(\u0026self.to_string_object())?)\n }\n /// Convert to JSON String pretty\n fn to_json_string_pretty(\u0026self) -\u003e Result\u003cString, serde_json::Error\u003e {\n Ok(serde_json::to_string_pretty(\u0026self.to_string_object())?)\n }\n}\n\nimpl\u003cT: ToStringObject\u003e ToJsonObject for T {}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::documents::UserDocumentDUBP;\n\n #[test]\n fn parse_dubp_document() {\n let text = \"Version: 10\nType: Identity\nCurrency: g1\nIssuer: D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\nUniqueID: elois\nTimestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855\nYdnclvw76/JHcKSmU9kl9Ie0ne5/X8NYOqPqbGnufIK3eEPRYYdEYaQh+zffuFhbtIRjv6m/DkVLH5cLy/IyAg==\";\n\n let doc = UserDocumentDUBP::parse(text).expect(\"Fail to parse UserDocumentDUBP !\");\n println!(\"Doc : {:?}\", doc);\n }\n}\n","traces":[{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":4}},{"line":134,"address":null,"length":0,"stats":{"Line":4}},{"line":143,"address":null,"length":0,"stats":{"Line":4}},{"line":144,"address":null,"length":0,"stats":{"Line":4}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":11}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":12}},{"line":197,"address":null,"length":0,"stats":{"Line":12}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":200,"address":null,"length":0,"stats":{"Line":11}},{"line":205,"address":null,"length":0,"stats":{"Line":12}},{"line":206,"address":null,"length":0,"stats":{"Line":12}},{"line":207,"address":null,"length":0,"stats":{"Line":12}},{"line":209,"address":null,"length":0,"stats":{"Line":12}},{"line":210,"address":null,"length":0,"stats":{"Line":1}},{"line":211,"address":null,"length":0,"stats":{"Line":1}},{"line":212,"address":null,"length":0,"stats":{"Line":1}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":12}},{"line":216,"address":null,"length":0,"stats":{"Line":12}},{"line":217,"address":null,"length":0,"stats":{"Line":12}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":12}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":24}},{"line":222,"address":null,"length":0,"stats":{"Line":12}},{"line":223,"address":null,"length":0,"stats":{"Line":1}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":12}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":230,"address":null,"length":0,"stats":{"Line":12}},{"line":231,"address":null,"length":0,"stats":{"Line":12}},{"line":232,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":297,"address":null,"length":0,"stats":{"Line":0}},{"line":298,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":314,"address":4222400,"length":1,"stats":{"Line":2}},{"line":315,"address":5461214,"length":1,"stats":{"Line":1}},{"line":323,"address":5461228,"length":1,"stats":{"Line":1}},{"line":324,"address":5461306,"length":1,"stats":{"Line":1}}],"covered":31,"coverable":55},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","blocks.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::documents::block::{v10::TxDocOrTxHash, BlockDocument, BlockDocumentV10};\nuse crate::documents::membership::v10::MembershipType;\nuse crate::parsers::{serde_json_value_to_pest_json_value, DefaultHasher};\nuse crate::*;\nuse dup_crypto::bases::BaseConvertionError;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\nuse dup_currency_params::genesis_block_params::v10::BlockV10Parameters;\nuse dup_currency_params::CurrencyName;\nuse failure::Error;\nuse json_pest_parser::*;\nuse std::str::FromStr;\n\npub fn parse_json_block_from_serde_value(\n serde_json_value: \u0026serde_json::Value,\n) -\u003e Result\u003cBlockDocument, Error\u003e {\n parse_json_block(\u0026serde_json_value_to_pest_json_value(serde_json_value)?)\n}\n\npub fn parse_json_block(json_block: \u0026JSONValue\u003cDefaultHasher\u003e) -\u003e Result\u003cBlockDocument, Error\u003e {\n if !json_block.is_object() {\n return Err(ParseJsonError {\n cause: \"Json block must be an object !\".to_owned(),\n }\n .into());\n }\n\n let json_block = json_block.to_object().expect(\"safe unwrap\");\n\n let currency = get_str(json_block, \"currency\")?;\n\n let block_number = get_number(json_block, \"number\")?.trunc() as u32;\n\n Ok(BlockDocument::V10(BlockDocumentV10 {\n version: get_number(json_block, \"version\")?.trunc() as u32,\n nonce: get_u64(json_block, \"nonce\")?,\n number: BlockNumber(block_number),\n pow_min: get_number(json_block, \"powMin\")?.trunc() as usize,\n time: get_number(json_block, \"time\")?.trunc() as u64,\n median_time: get_number(json_block, \"medianTime\")?.trunc() as u64,\n members_count: get_number(json_block, \"membersCount\")?.trunc() as usize,\n monetary_mass: get_number(json_block, \"monetaryMass\")\n .unwrap_or(0f64)\n .trunc() as usize,\n unit_base: get_number(json_block, \"unitbase\")?.trunc() as usize,\n issuers_count: get_number(json_block, \"issuersCount\")?.trunc() as usize,\n issuers_frame: get_number(json_block, \"issuersFrame\")?.trunc() as usize,\n issuers_frame_var: get_number(json_block, \"issuersFrameVar\")?.trunc() as isize,\n currency: CurrencyName(currency.to_owned()),\n issuers: vec![PubKey::Ed25519(ed25519::PublicKey::from_base58(get_str(\n json_block, \"issuer\",\n )?)?)],\n signatures: vec![Sig::Ed25519(ed25519::Signature::from_base64(get_str(\n json_block,\n \"signature\",\n )?)?)],\n hash: Some(BlockHash(Hash::from_hex(get_str(json_block, \"hash\")?)?)),\n parameters: if let Some(params) = get_optional_str_not_empty(json_block, \"parameters\")? {\n Some(BlockV10Parameters::from_str(params)?)\n } else {\n None\n },\n previous_hash: if block_number == 0 {\n None\n } else {\n Some(Hash::from_hex(get_str(json_block, \"previousHash\")?)?)\n },\n previous_issuer: if block_number == 0 {\n None\n } else {\n Some(PubKey::Ed25519(ed25519::PublicKey::from_base58(get_str(\n json_block,\n \"previousIssuer\",\n )?)?))\n },\n inner_hash: Some(Hash::from_hex(get_str(json_block, \"inner_hash\")?)?),\n dividend: get_optional_usize(json_block, \"dividend\")?,\n identities: crate::parsers::identities::parse_compact_identities(\n currency,\n get_str_array(json_block, \"identities\")?,\n )?,\n joiners: crate::parsers::memberships::parse_compact_memberships(\n currency,\n MembershipType::In(),\n \u0026get_str_array(json_block, \"joiners\")?,\n )?,\n actives: crate::parsers::memberships::parse_compact_memberships(\n currency,\n MembershipType::In(),\n \u0026get_str_array(json_block, \"actives\")?,\n )?,\n leavers: crate::parsers::memberships::parse_compact_memberships(\n currency,\n MembershipType::Out(),\n \u0026get_str_array(json_block, \"leavers\")?,\n )?,\n revoked: crate::parsers::revoked::parse_revocations_into_compact(\u0026get_str_array(\n json_block, \"revoked\",\n )?),\n excluded: get_str_array(json_block, \"excluded\")?\n .iter()\n .map(|p| ed25519::PublicKey::from_base58(p))\n .map(|p| p.map(PubKey::Ed25519))\n .collect::\u003cResult\u003cVec\u003cPubKey\u003e, BaseConvertionError\u003e\u003e()?,\n certifications: crate::parsers::certifications::parse_certifications_into_compact(\n \u0026get_str_array(json_block, \"certifications\")?,\n ),\n transactions: json_block\n .get(\"transactions\")\n .ok_or_else(|| ParseJsonError {\n cause: \"Fail to parse json block : field 'transactions' must exist !\".to_owned(),\n })?\n .to_array()\n .ok_or_else(|| ParseJsonError {\n cause: \"Fail to parse json block : field 'transactions' must be an array !\"\n .to_owned(),\n })?\n .iter()\n .map(|tx| crate::parsers::transactions::parse_json_transaction(tx))\n .map(|tx_result| tx_result.map(|tx_doc| TxDocOrTxHash::TxDoc(Box::new(tx_doc))))\n .collect::\u003cResult\u003cVec\u003cTxDocOrTxHash\u003e, Error\u003e\u003e()?,\n }))\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::documents::block::BlockDocumentTrait;\n\n #[test]\n fn parse_empty_json_block() {\n let block_json_str = r#\"{\n \"version\": 10,\n \"nonce\": 10200000037108,\n \"number\": 7,\n \"powMin\": 70,\n \"time\": 1488987677,\n \"medianTime\": 1488987394,\n \"membersCount\": 59,\n \"monetaryMass\": 59000,\n \"unitbase\": 0,\n \"issuersCount\": 1,\n \"issuersFrame\": 6,\n \"issuersFrameVar\": 0,\n \"currency\": \"g1\",\n \"issuer\": \"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\",\n \"signature\": \"xaWNjdFeE4yr9+AKckgR6QuAvMzmKUWfY+uIlC3HKjn2apJqG70Gf59A71W+Ucz6E9WPXRzDDF/xOrf6GCGHCA==\",\n \"hash\": \"0000407900D981FC17B5A6FBCF8E8AFA4C00FAD7AFC5BEA9A96FF505E5D105EC\",\n \"parameters\": \"\",\n \"previousHash\": \"0000379BBE6ABC18DCFD6E4733F9F76CB06593D10FAEDF722BE190C277AC16EA\",\n \"previousIssuer\": \"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\",\n \"inner_hash\": \"CF2701092D5A34A55802E343B5F8D61D9B7E8089F1F13A19721234DF5B2F0F38\",\n \"dividend\": null,\n \"identities\": [],\n \"joiners\": [],\n \"actives\": [],\n \"leavers\": [],\n \"revoked\": [],\n \"excluded\": [],\n \"certifications\": [],\n \"transactions\": [],\n \"raw\": \"Version: 10\\nType: Block\\nCurrency: g1\\nNumber: 7\\nPoWMin: 70\\nTime: 1488987677\\nMedianTime: 1488987394\\nUnitBase: 0\\nIssuer: 2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\\nIssuersFrame: 6\\nIssuersFrameVar: 0\\nDifferentIssuersCount: 1\\nPreviousHash: 0000379BBE6ABC18DCFD6E4733F9F76CB06593D10FAEDF722BE190C277AC16EA\\nPreviousIssuer: 2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\\nMembersCount: 59\\nIdentities:\\nJoiners:\\nActives:\\nLeavers:\\nRevoked:\\nExcluded:\\nCertifications:\\nTransactions:\\nInnerHash: CF2701092D5A34A55802E343B5F8D61D9B7E8089F1F13A19721234DF5B2F0F38\\nNonce: 10200000037108\\n\"\n }\"#;\n\n let block_json_value = json_pest_parser::parse_json_string(block_json_str)\n .expect(\"Fail to parse json block !\");\n assert_eq!(\n BlockDocument::V10(BlockDocumentV10 {\n version: 10,\n nonce: 10200000037108,\n number: BlockNumber(7),\n pow_min: 70,\n time: 1488987677,\n median_time: 1488987394,\n members_count: 59,\n monetary_mass: 59000,\n unit_base: 0,\n issuers_count: 1,\n issuers_frame: 6,\n issuers_frame_var: 0,\n currency: CurrencyName(\"g1\".to_owned()),\n issuers: vec![PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\")\n .expect(\"Fail to parse issuer !\")\n )],\n signatures: vec![Sig::Ed25519(\n ed25519::Signature::from_base64(\"xaWNjdFeE4yr9+AKckgR6QuAvMzmKUWfY+uIlC3HKjn2apJqG70Gf59A71W+Ucz6E9WPXRzDDF/xOrf6GCGHCA==\").expect(\"Fail to parse sig !\")\n )],\n hash: Some(BlockHash(\n Hash::from_hex(\n \"0000407900D981FC17B5A6FBCF8E8AFA4C00FAD7AFC5BEA9A96FF505E5D105EC\"\n )\n .expect(\"Fail to parse hash !\")\n )),\n parameters: None,\n previous_hash: Some(Hash::from_hex(\n \"0000379BBE6ABC18DCFD6E4733F9F76CB06593D10FAEDF722BE190C277AC16EA\"\n )\n .expect(\"Fail to parse previous_hash !\")),\n previous_issuer: Some(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\")\n .expect(\"Fail to parse previous issuer !\")\n )),\n inner_hash: Some(\n Hash::from_hex(\n \"CF2701092D5A34A55802E343B5F8D61D9B7E8089F1F13A19721234DF5B2F0F38\"\n )\n .expect(\"Fail to parse inner hash !\")\n ),\n dividend: None,\n identities: vec![],\n joiners: vec![],\n actives: vec![],\n leavers: vec![],\n revoked: vec![],\n excluded: vec![],\n certifications: vec![],\n transactions: vec![],\n }),\n parse_json_block(\u0026block_json_value).expect(\"Fail to parse block_json_value !\")\n );\n }\n\n use crate::documents::block::VerifyBlockHashError;\n\n #[test]\n fn parse_json_block_with_one_tx() -\u003e Result\u003c(), VerifyBlockHashError\u003e {\n let block_json_str = r#\"{\n \"version\": 10,\n \"nonce\": 10100000033688,\n \"number\": 52,\n \"powMin\": 74,\n \"time\": 1488990898,\n \"medianTime\": 1488990117,\n \"membersCount\": 59,\n \"monetaryMass\": 59000,\n \"unitbase\": 0,\n \"issuersCount\": 1,\n \"issuersFrame\": 6,\n \"issuersFrameVar\": 0,\n \"currency\": \"g1\",\n \"issuer\": \"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\",\n \"signature\": \"4/UIwXzWQekbYw7fpD8ueMH4GnDEwCM+DvDaTfquBXOvFXLRYo/S+Vrk5u7so/98gYaZ2O7Myh20xgQvhh5FDQ==\",\n \"hash\": \"000057D4B29AF6DADB16F841F19C54C00EB244CECA9C8F2D4839D54E5F91451C\",\n \"parameters\": \"\",\n \"previousHash\": \"00000FEDA61240DD125A26886FEB2E6995B52A94778C71224CAF8492FF257D47\",\n \"previousIssuer\": \"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\",\n \"inner_hash\": \"6B27ACDA51F416449E5A61FC69438F8974D11FC27EB7A992410C276FC0B9BA5F\",\n \"dividend\": null,\n \"identities\": [],\n \"joiners\": [],\n \"actives\": [],\n \"leavers\": [],\n \"revoked\": [],\n \"excluded\": [],\n \"certifications\": [],\n \"transactions\": [\n {\n \"version\": 10,\n \"currency\": \"g1\",\n \"locktime\": 0,\n \"blockstamp\": \"50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7\",\n \"blockstampTime\": 1488990016,\n \"issuers\": [\n \"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\"\n ],\n \"inputs\": [\n \"1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1\"\n ],\n \"outputs\": [\n \"1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)\",\n \"999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)\"\n ],\n \"unlocks\": [\n \"0:SIG(0)\"\n ],\n \"signatures\": [\n \"fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==\"\n ],\n \"comment\": \"TEST\",\n \"block_number\": 0,\n \"time\": 0\n }\n ],\n \"raw\": \"Version: 10\\nType: Block\\nCurrency: g1\\nNumber: 52\\nPoWMin: 74\\nTime: 1488990898\\nMedianTime: 1488990117\\nUnitBase: 0\\nIssuer: 2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\\nIssuersFrame: 6\\nIssuersFrameVar: 0\\nDifferentIssuersCount: 1\\nPreviousHash: 00000FEDA61240DD125A26886FEB2E6995B52A94778C71224CAF8492FF257D47\\nPreviousIssuer: 2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\\nMembersCount: 59\\nIdentities:\\nJoiners:\\nActives:\\nLeavers:\\nRevoked:\\nExcluded:\\nCertifications:\\nTransactions:\\nTX:10:1:1:1:2:1:0\\n50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7\\n2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\\n1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1\\n0:SIG(0)\\n1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)\\n999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)\\nTEST\\nfAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==\\nInnerHash: 6B27ACDA51F416449E5A61FC69438F8974D11FC27EB7A992410C276FC0B9BA5F\\nNonce: 10100000033688\\n\"\n }\"#;\n\n let block_json_value = json_pest_parser::parse_json_string(block_json_str)\n .expect(\"Fail to parse json block !\");\n\n let expected_block = BlockDocument::V10(BlockDocumentV10 {\n version: 10,\n nonce: 10100000033688,\n number: BlockNumber(52),\n pow_min: 74,\n time: 1488990898,\n median_time: 1488990117,\n members_count: 59,\n monetary_mass: 59000,\n unit_base: 0,\n issuers_count: 1,\n issuers_frame: 6,\n issuers_frame_var: 0,\n currency: CurrencyName(\"g1\".to_owned()),\n issuers: vec![PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\")\n .expect(\"Fail to parse issuer !\")\n )],\n signatures: vec![Sig::Ed25519(\n ed25519::Signature::from_base64(\"4/UIwXzWQekbYw7fpD8ueMH4GnDEwCM+DvDaTfquBXOvFXLRYo/S+Vrk5u7so/98gYaZ2O7Myh20xgQvhh5FDQ==\").expect(\"Fail to parse sig !\")\n )],\n hash: Some(BlockHash(\n Hash::from_hex(\n \"000057D4B29AF6DADB16F841F19C54C00EB244CECA9C8F2D4839D54E5F91451C\"\n )\n .expect(\"Fail to parse hash !\")\n )),\n parameters: None,\n previous_hash: Some(Hash::from_hex(\n \"00000FEDA61240DD125A26886FEB2E6995B52A94778C71224CAF8492FF257D47\"\n )\n .expect(\"Fail to parse previous_hash !\")),\n previous_issuer: Some(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\")\n .expect(\"Fail to parse previous issuer !\")\n )),\n inner_hash: Some(\n Hash::from_hex(\n \"6B27ACDA51F416449E5A61FC69438F8974D11FC27EB7A992410C276FC0B9BA5F\"\n )\n .expect(\"Fail to parse inner hash !\")\n ),\n dividend: None,\n identities: vec![],\n joiners: vec![],\n actives: vec![],\n leavers: vec![],\n revoked: vec![],\n excluded: vec![],\n certifications: vec![],\n transactions: vec![TxDocOrTxHash::TxDoc(Box::new(crate::parsers::tests::first_g1_tx_doc()))],\n });\n assert_eq!(\n expected_block,\n parse_json_block(\u0026block_json_value).expect(\"Fail to parse block_json_value !\")\n );\n\n expected_block.verify_inner_hash()?;\n Ok(())\n }\n}\n","traces":[{"line":29,"address":5633792,"length":1,"stats":{"Line":1}},{"line":32,"address":5633807,"length":1,"stats":{"Line":1}},{"line":35,"address":5634352,"length":1,"stats":{"Line":2}},{"line":36,"address":5634386,"length":1,"stats":{"Line":2}},{"line":37,"address":5634840,"length":1,"stats":{"Line":0}},{"line":38,"address":5634819,"length":1,"stats":{"Line":0}},{"line":43,"address":5634982,"length":1,"stats":{"Line":2}},{"line":45,"address":5635046,"length":1,"stats":{"Line":2}},{"line":47,"address":5635382,"length":1,"stats":{"Line":2}},{"line":49,"address":5670325,"length":1,"stats":{"Line":2}},{"line":50,"address":5635746,"length":1,"stats":{"Line":2}},{"line":51,"address":5636067,"length":1,"stats":{"Line":2}},{"line":52,"address":5636384,"length":1,"stats":{"Line":2}},{"line":53,"address":5636398,"length":1,"stats":{"Line":2}},{"line":54,"address":5636822,"length":1,"stats":{"Line":2}},{"line":55,"address":5637277,"length":1,"stats":{"Line":2}},{"line":56,"address":5637763,"length":1,"stats":{"Line":2}},{"line":57,"address":5638280,"length":1,"stats":{"Line":2}},{"line":60,"address":5638436,"length":1,"stats":{"Line":2}},{"line":61,"address":5638984,"length":1,"stats":{"Line":2}},{"line":62,"address":5639563,"length":1,"stats":{"Line":2}},{"line":63,"address":5640173,"length":1,"stats":{"Line":2}},{"line":64,"address":5640767,"length":1,"stats":{"Line":2}},{"line":65,"address":5640848,"length":1,"stats":{"Line":2}},{"line":66,"address":5640853,"length":1,"stats":{"Line":2}},{"line":68,"address":5642277,"length":1,"stats":{"Line":2}},{"line":69,"address":5642282,"length":1,"stats":{"Line":2}},{"line":72,"address":5643923,"length":1,"stats":{"Line":2}},{"line":73,"address":5645511,"length":1,"stats":{"Line":2}},{"line":74,"address":5646382,"length":1,"stats":{"Line":0}},{"line":76,"address":5647236,"length":1,"stats":{"Line":2}},{"line":78,"address":5647248,"length":1,"stats":{"Line":2}},{"line":79,"address":5647258,"length":1,"stats":{"Line":0}},{"line":81,"address":5647271,"length":1,"stats":{"Line":2}},{"line":83,"address":5648465,"length":1,"stats":{"Line":2}},{"line":84,"address":5648475,"length":1,"stats":{"Line":0}},{"line":86,"address":5648496,"length":1,"stats":{"Line":2}},{"line":87,"address":5648488,"length":1,"stats":{"Line":2}},{"line":91,"address":5649728,"length":1,"stats":{"Line":2}},{"line":92,"address":5651390,"length":1,"stats":{"Line":2}},{"line":93,"address":5653292,"length":1,"stats":{"Line":2}},{"line":94,"address":5652243,"length":1,"stats":{"Line":2}},{"line":95,"address":5652259,"length":1,"stats":{"Line":2}},{"line":97,"address":5655355,"length":1,"stats":{"Line":2}},{"line":98,"address":5654204,"length":1,"stats":{"Line":2}},{"line":99,"address":5654220,"length":1,"stats":{"Line":2}},{"line":100,"address":5654228,"length":1,"stats":{"Line":2}},{"line":102,"address":5657557,"length":1,"stats":{"Line":2}},{"line":103,"address":5656341,"length":1,"stats":{"Line":2}},{"line":104,"address":5656357,"length":1,"stats":{"Line":2}},{"line":105,"address":5656365,"length":1,"stats":{"Line":2}},{"line":107,"address":5659874,"length":1,"stats":{"Line":2}},{"line":108,"address":5658593,"length":1,"stats":{"Line":2}},{"line":109,"address":5658609,"length":1,"stats":{"Line":2}},{"line":110,"address":5658617,"length":1,"stats":{"Line":2}},{"line":112,"address":5660968,"length":1,"stats":{"Line":2}},{"line":113,"address":5660960,"length":1,"stats":{"Line":2}},{"line":115,"address":5662291,"length":1,"stats":{"Line":2}},{"line":117,"address":5494224,"length":1,"stats":{"Line":0}},{"line":118,"address":5494272,"length":1,"stats":{"Line":0}},{"line":119,"address":5663839,"length":1,"stats":{"Line":2}},{"line":120,"address":5666434,"length":1,"stats":{"Line":2}},{"line":121,"address":5664977,"length":1,"stats":{"Line":2}},{"line":123,"address":5666441,"length":1,"stats":{"Line":2}},{"line":125,"address":5494352,"length":1,"stats":{"Line":0}},{"line":126,"address":5494366,"length":1,"stats":{"Line":0}},{"line":129,"address":5494448,"length":1,"stats":{"Line":0}},{"line":130,"address":5494462,"length":1,"stats":{"Line":0}},{"line":134,"address":5494544,"length":1,"stats":{"Line":2}},{"line":135,"address":5494592,"length":1,"stats":{"Line":4}},{"line":136,"address":5669266,"length":1,"stats":{"Line":2}},{"line":146,"address":4775328,"length":1,"stats":{"Line":2}},{"line":147,"address":4775348,"length":1,"stats":{"Line":1}},{"line":180,"address":4775368,"length":1,"stats":{"Line":1}},{"line":182,"address":4777812,"length":1,"stats":{"Line":0}},{"line":183,"address":4776990,"length":1,"stats":{"Line":1}},{"line":186,"address":4775451,"length":1,"stats":{"Line":1}},{"line":196,"address":4775462,"length":1,"stats":{"Line":1}},{"line":197,"address":4775531,"length":1,"stats":{"Line":1}},{"line":198,"address":4775536,"length":1,"stats":{"Line":1}},{"line":201,"address":4775747,"length":1,"stats":{"Line":1}},{"line":202,"address":4775752,"length":1,"stats":{"Line":1}},{"line":204,"address":4776074,"length":1,"stats":{"Line":1}},{"line":205,"address":4775994,"length":1,"stats":{"Line":1}},{"line":210,"address":4776146,"length":1,"stats":{"Line":1}},{"line":211,"address":4776158,"length":1,"stats":{"Line":1}},{"line":215,"address":4776322,"length":1,"stats":{"Line":1}},{"line":216,"address":4776260,"length":1,"stats":{"Line":1}},{"line":219,"address":4776470,"length":1,"stats":{"Line":1}},{"line":220,"address":4776408,"length":1,"stats":{"Line":1}},{"line":225,"address":4776510,"length":1,"stats":{"Line":1}},{"line":226,"address":4776531,"length":1,"stats":{"Line":1}},{"line":227,"address":4776576,"length":1,"stats":{"Line":1}},{"line":228,"address":4776639,"length":1,"stats":{"Line":1}},{"line":229,"address":4776699,"length":1,"stats":{"Line":1}},{"line":230,"address":4776759,"length":1,"stats":{"Line":1}},{"line":231,"address":4776819,"length":1,"stats":{"Line":1}},{"line":232,"address":4776879,"length":1,"stats":{"Line":1}},{"line":233,"address":4776939,"length":1,"stats":{"Line":1}},{"line":235,"address":4777805,"length":1,"stats":{"Line":1}},{"line":242,"address":4778560,"length":1,"stats":{"Line":2}},{"line":243,"address":4778583,"length":1,"stats":{"Line":1}},{"line":303,"address":4778603,"length":1,"stats":{"Line":1}},{"line":306,"address":4780411,"length":1,"stats":{"Line":1}},{"line":309,"address":4778705,"length":1,"stats":{"Line":1}},{"line":319,"address":4778716,"length":1,"stats":{"Line":1}},{"line":320,"address":4778785,"length":1,"stats":{"Line":1}},{"line":321,"address":4778790,"length":1,"stats":{"Line":1}},{"line":324,"address":4779007,"length":1,"stats":{"Line":1}},{"line":325,"address":4779012,"length":1,"stats":{"Line":1}},{"line":327,"address":4779340,"length":1,"stats":{"Line":1}},{"line":328,"address":4779260,"length":1,"stats":{"Line":1}},{"line":333,"address":4779412,"length":1,"stats":{"Line":1}},{"line":334,"address":4779424,"length":1,"stats":{"Line":1}},{"line":338,"address":4779588,"length":1,"stats":{"Line":1}},{"line":339,"address":4779526,"length":1,"stats":{"Line":1}},{"line":342,"address":4779736,"length":1,"stats":{"Line":1}},{"line":343,"address":4779674,"length":1,"stats":{"Line":1}},{"line":348,"address":4779776,"length":1,"stats":{"Line":1}},{"line":349,"address":4779797,"length":1,"stats":{"Line":1}},{"line":350,"address":4779848,"length":1,"stats":{"Line":1}},{"line":351,"address":4779917,"length":1,"stats":{"Line":1}},{"line":352,"address":4779977,"length":1,"stats":{"Line":1}},{"line":353,"address":4780037,"length":1,"stats":{"Line":1}},{"line":354,"address":4780097,"length":1,"stats":{"Line":1}},{"line":355,"address":4780157,"length":1,"stats":{"Line":1}},{"line":356,"address":4780218,"length":1,"stats":{"Line":1}},{"line":358,"address":4781294,"length":1,"stats":{"Line":1}},{"line":360,"address":4781226,"length":1,"stats":{"Line":1}},{"line":363,"address":4781647,"length":1,"stats":{"Line":1}},{"line":364,"address":4781990,"length":1,"stats":{"Line":1}}],"covered":119,"coverable":131},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","certifications.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::documents::certification::{CertificationDocumentV10, CompactCertificationDocumentV10};\nuse crate::text_document_traits::TextDocumentFormat;\nuse crate::BlockNumber;\nuse dup_crypto::keys::*;\n\n/// Parse array of certification json documents into vector of `CompactCertificationDocument`\npub fn parse_certifications_into_compact(\n str_certs: \u0026[\u0026str],\n) -\u003e Vec\u003cTextDocumentFormat\u003cCertificationDocumentV10\u003e\u003e {\n let mut certifications: Vec\u003cTextDocumentFormat\u003cCertificationDocumentV10\u003e\u003e = Vec::new();\n for certification in str_certs {\n let certifications_datas: Vec\u003c\u0026str\u003e = certification.split(':').collect();\n if certifications_datas.len() == 4 {\n certifications.push(TextDocumentFormat::Compact(\n CompactCertificationDocumentV10 {\n issuer: PubKey::Ed25519(\n ed25519::PublicKey::from_base58(certifications_datas[0])\n .expect(\"Receive block in wrong format : fail to parse issuer !\"),\n ),\n target: PubKey::Ed25519(\n ed25519::PublicKey::from_base58(certifications_datas[1])\n .expect(\"Receive block in wrong format : fail to parse target !\"),\n ),\n block_number: BlockNumber(\n certifications_datas[2]\n .parse()\n .expect(\"Receive block in wrong format : fail to parse block number !\"),\n ),\n signature: Sig::Ed25519(\n ed25519::Signature::from_base64(certifications_datas[3])\n .expect(\"Receive block in wrong format : fail to parse signature !\"),\n ),\n },\n ));\n }\n }\n certifications\n}\n","traces":[{"line":22,"address":5459760,"length":1,"stats":{"Line":2}},{"line":25,"address":5459786,"length":1,"stats":{"Line":2}},{"line":26,"address":5459829,"length":1,"stats":{"Line":2}},{"line":27,"address":5460038,"length":1,"stats":{"Line":1}},{"line":28,"address":5460157,"length":1,"stats":{"Line":1}},{"line":29,"address":5460947,"length":1,"stats":{"Line":1}},{"line":30,"address":5460763,"length":1,"stats":{"Line":1}},{"line":31,"address":5460305,"length":1,"stats":{"Line":1}},{"line":32,"address":5460215,"length":1,"stats":{"Line":1}},{"line":35,"address":5460436,"length":1,"stats":{"Line":1}},{"line":36,"address":5460345,"length":1,"stats":{"Line":1}},{"line":39,"address":5460593,"length":1,"stats":{"Line":1}},{"line":40,"address":5460476,"length":1,"stats":{"Line":1}},{"line":44,"address":5460691,"length":1,"stats":{"Line":1}},{"line":45,"address":5460600,"length":1,"stats":{"Line":1}},{"line":52,"address":5460077,"length":1,"stats":{"Line":2}}],"covered":16,"coverable":16},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","identities.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::documents::identity::v10::*;\nuse crate::Blockstamp;\nuse crate::DocumentBuilder;\nuse dup_crypto::keys::*;\n\n#[derive(Debug, Fail)]\n#[fail(display = \"Fail to parse identity : {:?} !\", cause)]\npub struct ParseIdentityError {\n pub cause: String,\n}\n\n/// Parse a compact identity\npub fn parse_compact_identities(\n currency: \u0026str,\n str_identities: Vec\u003c\u0026str\u003e,\n) -\u003e Result\u003cVec\u003cIdentityDocumentV10\u003e, ParseIdentityError\u003e {\n let mut identities = Vec::with_capacity(str_identities.len());\n\n for str_identity in str_identities {\n let idty_elements: Vec\u003c\u0026str\u003e = str_identity.split(':').collect();\n let issuer = match ed25519::PublicKey::from_base58(idty_elements[0]) {\n Ok(pubkey) =\u003e PubKey::Ed25519(pubkey),\n Err(_) =\u003e {\n return Err(ParseIdentityError {\n cause: \"invalid pubkey\".to_owned(),\n });\n }\n };\n let signature = match ed25519::Signature::from_base64(idty_elements[1]) {\n Ok(sig) =\u003e Sig::Ed25519(sig),\n Err(_) =\u003e {\n return Err(ParseIdentityError {\n cause: \"invalid signature\".to_owned(),\n });\n }\n };\n let blockstamp = match Blockstamp::from_string(idty_elements[2]) {\n Ok(blockstamp) =\u003e blockstamp,\n Err(_) =\u003e {\n return Err(ParseIdentityError {\n cause: \"invalid blockstamp\".to_owned(),\n });\n }\n };\n let username = idty_elements[3];\n let idty_doc_builder = IdentityDocumentV10Builder {\n currency,\n username,\n blockstamp: \u0026blockstamp,\n issuer: \u0026issuer,\n };\n identities.push(idty_doc_builder.build_with_signature(vec![signature]))\n }\n\n Ok(identities)\n}\n","traces":[{"line":28,"address":5465920,"length":1,"stats":{"Line":2}},{"line":32,"address":5465946,"length":1,"stats":{"Line":2}},{"line":34,"address":5466040,"length":1,"stats":{"Line":2}},{"line":35,"address":5466400,"length":1,"stats":{"Line":0}},{"line":36,"address":5466485,"length":1,"stats":{"Line":0}},{"line":37,"address":5466558,"length":1,"stats":{"Line":0}},{"line":39,"address":5466770,"length":1,"stats":{"Line":0}},{"line":40,"address":5466743,"length":1,"stats":{"Line":0}},{"line":44,"address":5466711,"length":1,"stats":{"Line":0}},{"line":45,"address":5466961,"length":1,"stats":{"Line":0}},{"line":47,"address":5467269,"length":1,"stats":{"Line":0}},{"line":48,"address":5467242,"length":1,"stats":{"Line":0}},{"line":52,"address":5467210,"length":1,"stats":{"Line":0}},{"line":53,"address":5467400,"length":1,"stats":{"Line":0}},{"line":55,"address":5467600,"length":1,"stats":{"Line":0}},{"line":56,"address":5467573,"length":1,"stats":{"Line":0}},{"line":60,"address":5467541,"length":1,"stats":{"Line":0}},{"line":61,"address":5467764,"length":1,"stats":{"Line":0}},{"line":62,"address":5467732,"length":1,"stats":{"Line":0}},{"line":63,"address":5467748,"length":1,"stats":{"Line":0}},{"line":67,"address":5467838,"length":1,"stats":{"Line":0}},{"line":70,"address":5468085,"length":1,"stats":{"Line":2}}],"covered":4,"coverable":22},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","memberships.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::documents::membership::v10::*;\nuse crate::Blockstamp;\nuse crate::DocumentBuilder;\nuse dup_crypto::keys::*;\nuse failure::Error;\n\n#[derive(Debug, Fail, Copy, Clone)]\npub enum ParseMembershipError {\n #[fail(display = \"Fail to parse membership : wrong format !\")]\n WrongFormat,\n}\n\n/// Parse memberships documents from array of str\npub fn parse_compact_memberships(\n currency: \u0026str,\n membership_type: MembershipType,\n array_memberships: \u0026[\u0026str],\n) -\u003e Result\u003cVec\u003cMembershipDocumentV10\u003e, Error\u003e {\n //let memberships: Vec\u003cMembershipDocumentV10\u003e = Vec::new();\n array_memberships\n .iter()\n .map(|membership| {\n let membership_datas: Vec\u003c\u0026str\u003e = membership.split(':').collect();\n if membership_datas.len() == 5 {\n let membership_doc_builder = MembershipDocumentV10Builder {\n currency,\n issuer: \u0026PubKey::Ed25519(ed25519::PublicKey::from_base58(membership_datas[0])?),\n blockstamp: \u0026Blockstamp::from_string(membership_datas[2])?,\n membership: membership_type,\n identity_username: membership_datas[4],\n identity_blockstamp: \u0026Blockstamp::from_string(membership_datas[3])?,\n };\n let membership_sig =\n Sig::Ed25519(ed25519::Signature::from_base64(membership_datas[1])?);\n Ok(membership_doc_builder.build_with_signature(vec![membership_sig]))\n } else {\n Err(ParseMembershipError::WrongFormat.into())\n }\n })\n .collect()\n}\n","traces":[{"line":29,"address":4216288,"length":1,"stats":{"Line":2}},{"line":35,"address":4216323,"length":1,"stats":{"Line":2}}],"covered":2,"coverable":2},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n/// Parsers for block\npub mod blocks;\n\n/// Parsers for certifications\npub mod certifications;\n\n/// Parsers for identities\npub mod identities;\n\n/// Parsers for memberships\npub mod memberships;\n\n/// Parsers for revocations\npub mod revoked;\n\n/// Parsers for transactions\npub mod transactions;\n\nuse json_pest_parser::{JSONValue, Number};\nuse serde_json::Value;\nuse std::collections::HashMap;\n\ntype DefaultHasher = std::hash::BuildHasherDefault\u003cstd::collections::hash_map::DefaultHasher\u003e;\n\n#[derive(Copy, Clone, Debug, Fail)]\n#[fail(display = \"Fail to convert serde_json::Value into json_pest_parser::JSONValue\")]\nstruct JsonValueConversionError;\n\nfn serde_json_value_to_pest_json_value(\n value: \u0026Value,\n) -\u003e Result\u003cJSONValue\u003cDefaultHasher\u003e, JsonValueConversionError\u003e {\n match value {\n Value::Null =\u003e Ok(JSONValue::Null),\n Value::Bool(boolean) =\u003e Ok(JSONValue::Boolean(*boolean)),\n Value::Number(number) =\u003e Ok(JSONValue::Number(if let Some(u64_) = number.as_u64() {\n Number::U64(u64_)\n } else if let Some(f64_) = number.as_f64() {\n Number::F64(f64_)\n } else {\n return Err(JsonValueConversionError);\n })),\n Value::String(string) =\u003e Ok(JSONValue::String(string)),\n Value::Array(values) =\u003e Ok(JSONValue::Array(\n values\n .iter()\n .map(serde_json_value_to_pest_json_value)\n .collect::\u003cResult\u003cVec\u003cJSONValue\u003cDefaultHasher\u003e\u003e, JsonValueConversionError\u003e\u003e()?,\n )),\n Value::Object(map) =\u003e Ok(JSONValue::Object(\n map.into_iter()\n .map(|(k, v)| match serde_json_value_to_pest_json_value(v) {\n Ok(v) =\u003e Ok((k.as_str(), v)),\n Err(e) =\u003e Err(e),\n })\n .collect::\u003cResult\u003c\n HashMap\u003c\u0026str, JSONValue\u003cDefaultHasher\u003e, DefaultHasher\u003e,\n JsonValueConversionError,\n \u003e\u003e()?,\n )),\n }\n}\n\n//std::collections::HashMap\u003c\u0026str, json_pest_parser::JSONValue\u003c'_, std::hash::BuildHasherDefault\u003cstd::collections::hash_map::DefaultHasher\u003e\u003e\u003e\n//std::iter::Iterator\u003cItem=(\u0026std::string::String, json_pest_parser::JSONValue\u003c'_, std::hash::BuildHasherDefault\u003cstd::collections::hash_map::DefaultHasher\u003e\u003e)\u003e\n\n#[cfg(test)]\nmod tests {\n use crate::blockstamp::Blockstamp;\n use crate::documents::transaction::*;\n use crate::*;\n use std::str::FromStr;\n\n pub fn first_g1_tx_doc() -\u003e TransactionDocument {\n let expected_tx_builder = TransactionDocumentBuilder {\n currency: \u0026\"g1\",\n blockstamp: \u0026Blockstamp::from_string(\n \"50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7\",\n )\n .expect(\"Fail to parse blockstamp\"),\n locktime: \u00260,\n issuers: \u0026vec![PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\")\n .expect(\"Fail to parse issuer !\"),\n )],\n inputs: \u0026vec![TransactionInput::from_str(\n \"1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1\",\n )\n .expect(\"Fail to parse inputs\")],\n unlocks: \u0026vec![\n TransactionInputUnlocks::from_str(\"0:SIG(0)\").expect(\"Fail to parse unlocks\")\n ],\n outputs: \u0026vec![\n TransactionOutput::from_str(\n \"1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)\",\n )\n .expect(\"Fail to parse outputs\"),\n TransactionOutput::from_str(\n \"999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)\",\n )\n .expect(\"Fail to parse outputs\"),\n ],\n comment: \"TEST\",\n hash: None,\n };\n\n expected_tx_builder.build_with_signature(vec![Sig::Ed25519(\n ed25519::Signature::from_base64(\"fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==\").expect(\"Fail to parse sig !\")\n )])\n }\n}\n","traces":[{"line":44,"address":5003056,"length":1,"stats":{"Line":1}},{"line":47,"address":5003209,"length":1,"stats":{"Line":1}},{"line":48,"address":5003074,"length":1,"stats":{"Line":1}},{"line":49,"address":5003214,"length":1,"stats":{"Line":1}},{"line":50,"address":5003299,"length":1,"stats":{"Line":1}},{"line":51,"address":5003375,"length":1,"stats":{"Line":1}},{"line":52,"address":5003405,"length":1,"stats":{"Line":0}},{"line":53,"address":5003464,"length":1,"stats":{"Line":0}},{"line":55,"address":5003504,"length":1,"stats":{"Line":0}},{"line":57,"address":5003600,"length":1,"stats":{"Line":1}},{"line":58,"address":5003723,"length":1,"stats":{"Line":1}},{"line":59,"address":5003743,"length":1,"stats":{"Line":1}},{"line":62,"address":5003888,"length":1,"stats":{"Line":1}},{"line":64,"address":5004206,"length":1,"stats":{"Line":1}},{"line":65,"address":5004226,"length":1,"stats":{"Line":1}},{"line":66,"address":4789264,"length":1,"stats":{"Line":1}},{"line":67,"address":4789373,"length":1,"stats":{"Line":1}},{"line":68,"address":4789702,"length":1,"stats":{"Line":0}},{"line":73,"address":5004330,"length":1,"stats":{"Line":1}},{"line":88,"address":4782528,"length":1,"stats":{"Line":1}},{"line":89,"address":4783596,"length":1,"stats":{"Line":1}},{"line":90,"address":4782545,"length":1,"stats":{"Line":1}},{"line":91,"address":4782559,"length":1,"stats":{"Line":1}},{"line":96,"address":4782659,"length":1,"stats":{"Line":1}},{"line":97,"address":4782681,"length":1,"stats":{"Line":1}},{"line":100,"address":4782877,"length":1,"stats":{"Line":1}},{"line":104,"address":4783052,"length":1,"stats":{"Line":1}},{"line":105,"address":4783073,"length":1,"stats":{"Line":1}},{"line":107,"address":4783222,"length":1,"stats":{"Line":1}},{"line":108,"address":4783235,"length":1,"stats":{"Line":1}},{"line":112,"address":4783315,"length":1,"stats":{"Line":1}},{"line":118,"address":4783583,"length":1,"stats":{"Line":1}},{"line":121,"address":4783773,"length":1,"stats":{"Line":1}},{"line":122,"address":4783778,"length":1,"stats":{"Line":1}}],"covered":30,"coverable":34},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","revoked.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::documents::revocation::{CompactRevocationDocumentV10, RevocationDocumentV10};\nuse crate::text_document_traits::TextDocumentFormat;\nuse dup_crypto::keys::*;\n\n/// Parse array of revocations json documents into vector of `CompactRevocationDocumentV10`\npub fn parse_revocations_into_compact(\n str_revocations: \u0026[\u0026str],\n) -\u003e Vec\u003cTextDocumentFormat\u003cRevocationDocumentV10\u003e\u003e {\n let mut revocations: Vec\u003cTextDocumentFormat\u003cRevocationDocumentV10\u003e\u003e = Vec::new();\n for revocation in str_revocations {\n let revocations_datas: Vec\u003c\u0026str\u003e = revocation.split(':').collect();\n if revocations_datas.len() == 2 {\n revocations.push(TextDocumentFormat::Compact(CompactRevocationDocumentV10 {\n issuer: PubKey::Ed25519(\n ed25519::PublicKey::from_base58(revocations_datas[0])\n .expect(\"Receive block in wrong format !\"),\n ),\n signature: Sig::Ed25519(\n ed25519::Signature::from_base64(revocations_datas[1])\n .expect(\"Receive block in wrong format !\"),\n ),\n }));\n }\n }\n revocations\n}\n","traces":[{"line":21,"address":5463120,"length":1,"stats":{"Line":2}},{"line":24,"address":5463140,"length":1,"stats":{"Line":2}},{"line":25,"address":5463180,"length":1,"stats":{"Line":2}},{"line":26,"address":5463374,"length":1,"stats":{"Line":0}},{"line":27,"address":5463484,"length":1,"stats":{"Line":0}},{"line":28,"address":5463833,"length":1,"stats":{"Line":0}},{"line":29,"address":5463632,"length":1,"stats":{"Line":0}},{"line":30,"address":5463542,"length":1,"stats":{"Line":0}},{"line":33,"address":5463761,"length":1,"stats":{"Line":0}},{"line":34,"address":5463672,"length":1,"stats":{"Line":0}},{"line":40,"address":5463413,"length":1,"stats":{"Line":2}}],"covered":4,"coverable":11},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","parsers","transactions.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nuse crate::documents::transaction::*;\nuse crate::parsers::DefaultHasher;\nuse crate::TextDocumentParseError;\nuse crate::*;\nuse dup_crypto::bases::BaseConvertionError;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::*;\nuse failure::Error;\nuse json_pest_parser::*;\nuse std::str::FromStr;\n\n#[derive(Debug, Fail, Copy, Clone)]\npub enum ParseTxError {\n #[fail(display = \"Fail to parse transaction : wrong format !\")]\n WrongFormat,\n}\n\n/// Parse transaction from json value\npub fn parse_json_transaction(\n json_tx: \u0026JSONValue\u003cDefaultHasher\u003e,\n) -\u003e Result\u003cTransactionDocument, Error\u003e {\n if !json_tx.is_object() {\n return Err(ParseJsonError {\n cause: \"Json transaction must be an object !\".to_owned(),\n }\n .into());\n }\n\n let json_tx = json_tx.to_object().expect(\"safe unwrap\");\n\n let tx_doc_builder = TransactionDocumentBuilder {\n currency: get_str(json_tx, \"currency\")?,\n blockstamp: \u0026Blockstamp::from_string(get_str(json_tx, \"blockstamp\")?)?,\n locktime: \u0026(get_number(json_tx, \"locktime\")?.trunc() as u64),\n issuers: \u0026get_str_array(json_tx, \"issuers\")?\n .iter()\n .map(|p| ed25519::PublicKey::from_base58(p))\n .map(|p| p.map(PubKey::Ed25519))\n .collect::\u003cResult\u003cVec\u003cPubKey\u003e, BaseConvertionError\u003e\u003e()?,\n inputs: \u0026get_str_array(json_tx, \"inputs\")?\n .iter()\n .map(|i| TransactionInput::from_str(i))\n .collect::\u003cResult\u003cVec\u003cTransactionInput\u003e, TextDocumentParseError\u003e\u003e()?,\n unlocks: \u0026get_str_array(json_tx, \"unlocks\")?\n .iter()\n .map(|i| TransactionInputUnlocks::from_str(i))\n .collect::\u003cResult\u003cVec\u003cTransactionInputUnlocks\u003e, TextDocumentParseError\u003e\u003e()?,\n outputs: \u0026get_str_array(json_tx, \"outputs\")?\n .iter()\n .map(|i| TransactionOutput::from_str(i))\n .collect::\u003cResult\u003cVec\u003cTransactionOutput\u003e, TextDocumentParseError\u003e\u003e()?,\n comment: \u0026durs_common_tools::fns::str_escape::unescape_str(get_str(json_tx, \"comment\")?),\n hash: if let Some(hash_str) = get_optional_str(json_tx, \"hash\")? {\n Some(Hash::from_hex(hash_str)?)\n } else {\n None\n },\n };\n\n Ok(tx_doc_builder.build_with_signature(\n get_str_array(json_tx, \"signatures\")?\n .iter()\n .map(|p| ed25519::Signature::from_base64(p))\n .map(|p| p.map(Sig::Ed25519))\n .collect::\u003cResult\u003cVec\u003cSig\u003e, BaseConvertionError\u003e\u003e()?,\n ))\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn parse_empty_json_block() {\n let tx_json_str = r#\"{\n \"version\": 10,\n \"currency\": \"g1\",\n \"locktime\": 0,\n \"blockstamp\": \"50-00001DAA4559FEDB8320D1040B0F22B631459F36F237A0D9BC1EB923C12A12E7\",\n \"blockstampTime\": 1488990016,\n \"issuers\": [\n \"2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ\"\n ],\n \"inputs\": [\n \"1000:0:D:2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ:1\"\n ],\n \"outputs\": [\n \"1:0:SIG(Com8rJukCozHZyFao6AheSsfDQdPApxQRnz7QYFf64mm)\",\n \"999:0:SIG(2ny7YAdmzReQxAayyJZsyVYwYhVyax2thKcGknmQy5nQ)\"\n ],\n \"unlocks\": [\n \"0:SIG(0)\"\n ],\n \"signatures\": [\n \"fAH5Gor+8MtFzQZ++JaJO6U8JJ6+rkqKtPrRr/iufh3MYkoDGxmjzj6jCADQL+hkWBt8y8QzlgRkz0ixBcKHBw==\"\n ],\n \"comment\": \"TEST\",\n \"block_number\": 0,\n \"time\": 0\n }\"#;\n\n let tx_json_value =\n json_pest_parser::parse_json_string(tx_json_str).expect(\"Fail to parse json tx !\");\n\n assert_eq!(\n crate::parsers::tests::first_g1_tx_doc(),\n parse_json_transaction(\u0026tx_json_value).expect(\"Fail to parse tx_json_value !\")\n );\n }\n\n}\n","traces":[{"line":34,"address":4223280,"length":1,"stats":{"Line":2}},{"line":37,"address":4223304,"length":1,"stats":{"Line":2}},{"line":38,"address":4223534,"length":1,"stats":{"Line":0}},{"line":39,"address":4223513,"length":1,"stats":{"Line":0}},{"line":44,"address":4223666,"length":1,"stats":{"Line":2}},{"line":46,"address":4232677,"length":1,"stats":{"Line":2}},{"line":47,"address":4223730,"length":1,"stats":{"Line":2}},{"line":48,"address":4224046,"length":1,"stats":{"Line":2}},{"line":49,"address":4224727,"length":1,"stats":{"Line":2}},{"line":50,"address":4225151,"length":1,"stats":{"Line":2}},{"line":52,"address":4268048,"length":1,"stats":{"Line":2}},{"line":53,"address":4268096,"length":1,"stats":{"Line":2}},{"line":54,"address":4225844,"length":1,"stats":{"Line":2}},{"line":55,"address":4226212,"length":1,"stats":{"Line":2}},{"line":57,"address":4268176,"length":1,"stats":{"Line":2}},{"line":58,"address":4227037,"length":1,"stats":{"Line":2}},{"line":59,"address":4227417,"length":1,"stats":{"Line":2}},{"line":61,"address":4268224,"length":1,"stats":{"Line":2}},{"line":62,"address":4228324,"length":1,"stats":{"Line":2}},{"line":63,"address":4228724,"length":1,"stats":{"Line":2}},{"line":65,"address":4268272,"length":1,"stats":{"Line":2}},{"line":66,"address":4229713,"length":1,"stats":{"Line":2}},{"line":67,"address":4230133,"length":1,"stats":{"Line":2}},{"line":68,"address":4231008,"length":1,"stats":{"Line":2}},{"line":69,"address":4231983,"length":1,"stats":{"Line":1}},{"line":71,"address":4232661,"length":1,"stats":{"Line":1}},{"line":75,"address":4234375,"length":1,"stats":{"Line":2}},{"line":76,"address":4233109,"length":1,"stats":{"Line":2}},{"line":78,"address":4268320,"length":1,"stats":{"Line":2}},{"line":79,"address":4268368,"length":1,"stats":{"Line":2}},{"line":80,"address":4233884,"length":1,"stats":{"Line":2}},{"line":89,"address":4223248,"length":1,"stats":{"Line":2}},{"line":90,"address":4953950,"length":1,"stats":{"Line":1}},{"line":118,"address":4953964,"length":1,"stats":{"Line":1}},{"line":120,"address":4954114,"length":1,"stats":{"Line":0}},{"line":121,"address":4954040,"length":1,"stats":{"Line":1}},{"line":122,"address":4954060,"length":1,"stats":{"Line":1}}],"covered":34,"coverable":37},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","documents","src","text_document_traits.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Define the Text Document Traits.\n\nuse crate::*;\nuse dup_crypto::keys::*;\n\n#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]\n/// Contains a document in full or compact format\npub enum TextDocumentFormat\u003cD: TextDocument\u003e {\n /// Complete format (Allows to check the validity of the signature)\n Complete(D),\n /// Format present in the blocks (does not always allow to verify the signature)\n Compact(D::CompactTextDocument_),\n}\n\nimpl\u003cD: TextDocument\u003e TextDocumentFormat\u003cD\u003e {\n /// To compact document\n pub fn to_compact_document(\u0026self) -\u003e D::CompactTextDocument_ {\n match *self {\n TextDocumentFormat::Complete(ref doc) =\u003e doc.to_compact_document(),\n TextDocumentFormat::Compact(ref compact_doc) =\u003e (*compact_doc).clone(),\n }\n }\n}\n\n/// Trait for a compact text document.\npub trait CompactTextDocument: Sized + Clone {\n /// Generate document compact text.\n /// the compact format is the one used in the blocks.\n ///\n /// - Don't contains leading signatures\n /// - Contains line breaks on all line.\n fn as_compact_text(\u0026self) -\u003e String;\n}\n\nimpl\u003cD: TextDocument\u003e CompactTextDocument for TextDocumentFormat\u003cD\u003e {\n fn as_compact_text(\u0026self) -\u003e String {\n match *self {\n TextDocumentFormat::Complete(ref doc) =\u003e doc.generate_compact_text(),\n TextDocumentFormat::Compact(ref doc) =\u003e doc.as_compact_text(),\n }\n }\n}\n\n/// Trait for a V10 document.\npub trait TextDocument: Document\u003cPublicKey = PubKey\u003e {\n /// Type of associated compact document.\n type CompactTextDocument_: CompactTextDocument;\n\n /// Return document as text.\n fn as_text(\u0026self) -\u003e \u0026str;\n\n /// Return document as text without signature.\n fn as_text_without_signature(\u0026self) -\u003e \u0026str {\n let text = self.as_text();\n let mut lines: Vec\u003c\u0026str\u003e = self.as_text().split('\\n').collect();\n let sigs = self.signatures();\n let mut sigs_str_len = sigs.len() - 1;\n for _ in sigs {\n sigs_str_len += lines.pop().unwrap_or(\"\").len();\n }\n \u0026text[0..(text.len() - sigs_str_len)]\n }\n\n /*/// Return document as text with leading signatures.\n fn as_text_with_signatures(\u0026self) -\u003e String {\n let mut text = self.as_text().to_string();\n\n for sig in self.signatures() {\n text = format!(\"{}{}\\n\", text, sig.to_base64());\n }\n\n text\n }*/\n\n /// Generate compact document.\n /// the compact format is the one used in the blocks.\n /// - Don't contains leading signatures\n fn to_compact_document(\u0026self) -\u003e Self::CompactTextDocument_;\n\n /// Generate document compact text.\n /// the compact format is the one used in the blocks.\n ///\n /// - Don't contains leading signatures\n /// - Contains line breaks on all line.\n fn generate_compact_text(\u0026self) -\u003e String {\n self.to_compact_document().as_compact_text()\n }\n}\n\n/// Trait for a V10 document builder.\npub trait TextDocumentBuilder: DocumentBuilder {\n /// Generate document text.\n ///\n /// - Don't contains leading signatures\n /// - Contains line breaks on all line.\n fn generate_text(\u0026self) -\u003e String;\n\n /// Generate final document with signatures, and also return them in an array.\n ///\n /// Returns :\n ///\n /// - Text without signatures\n /// - Signatures\n fn build_signed_text(\u0026self, private_keys: Vec\u003cPrivKey\u003e) -\u003e (String, Vec\u003cSig\u003e) {\n let text = self.generate_text();\n\n let signatures: Vec\u003c_\u003e = {\n let text_bytes = text.as_bytes();\n private_keys\n .iter()\n .map(|key| key.sign(text_bytes))\n .collect()\n };\n\n (text, signatures)\n }\n}\n","traces":[{"line":32,"address":null,"length":0,"stats":{"Line":0}},{"line":33,"address":null,"length":0,"stats":{"Line":0}},{"line":34,"address":null,"length":0,"stats":{"Line":0}},{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":51,"address":null,"length":0,"stats":{"Line":1}},{"line":52,"address":null,"length":0,"stats":{"Line":1}},{"line":53,"address":null,"length":0,"stats":{"Line":1}},{"line":54,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":6}},{"line":69,"address":null,"length":0,"stats":{"Line":6}},{"line":70,"address":null,"length":0,"stats":{"Line":6}},{"line":71,"address":null,"length":0,"stats":{"Line":6}},{"line":72,"address":null,"length":0,"stats":{"Line":6}},{"line":73,"address":null,"length":0,"stats":{"Line":6}},{"line":74,"address":null,"length":0,"stats":{"Line":6}},{"line":76,"address":null,"length":0,"stats":{"Line":6}},{"line":100,"address":null,"length":0,"stats":{"Line":5}},{"line":101,"address":null,"length":0,"stats":{"Line":5}},{"line":119,"address":null,"length":0,"stats":{"Line":5}},{"line":120,"address":null,"length":0,"stats":{"Line":5}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":5}},{"line":124,"address":null,"length":0,"stats":{"Line":5}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":10}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":5}}],"covered":19,"coverable":27},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","json-pest-parser","src","lib.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! JSON parser based on [pest](https://pest.rs). \n//! It's is a personal crate for personal use. \n//! The grammar used is a copy of the grammar proposed in the \"pest book\". \n\n#![deny(\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces\n)]\n\n#[macro_use]\nextern crate failure;\n#[macro_use]\nextern crate pest_derive;\n\n#[cfg(test)]\n#[macro_use]\nextern crate pretty_assertions;\n\nuse failure::Error;\nuse pest::iterators::Pair;\nuse pest::Parser;\nuse std::collections::HashMap;\nuse std::str::FromStr;\n\n#[derive(Parser)]\n#[grammar = \"json_grammar.pest\"]\nstruct JSONParser;\n\n#[derive(Debug, PartialEq)]\npub enum JSONValue\u003c'a, S: std::hash::BuildHasher\u003e {\n Object(HashMap\u003c\u0026'a str, JSONValue\u003c'a, S\u003e, S\u003e),\n Array(Vec\u003cJSONValue\u003c'a, S\u003e\u003e),\n String(\u0026'a str),\n Number(Number),\n Boolean(bool),\n Null,\n}\n\n#[derive(Copy, Clone, Debug, PartialEq)]\npub enum Number {\n F64(f64),\n U64(u64),\n}\n\ntype JsonObject\u003c'a, S\u003e = HashMap\u003c\u0026'a str, JSONValue\u003c'a, S\u003e, S\u003e;\n\nimpl\u003c'a, S: std::hash::BuildHasher\u003e JSONValue\u003c'a, S\u003e {\n pub fn is_object(\u0026self) -\u003e bool {\n if let JSONValue::Object(_) = self {\n true\n } else {\n false\n }\n }\n\n pub fn to_object(\u0026self) -\u003e Option\u003c\u0026HashMap\u003c\u0026'a str, JSONValue\u003c'a, S\u003e, S\u003e\u003e {\n if let JSONValue::Object(object) = self {\n Some(object)\n } else {\n None\n }\n }\n\n pub fn is_array(\u0026self) -\u003e bool {\n if let JSONValue::Array(_) = self {\n true\n } else {\n false\n }\n }\n\n pub fn to_array(\u0026self) -\u003e Option\u003c\u0026Vec\u003cJSONValue\u003c'a, S\u003e\u003e\u003e {\n if let JSONValue::Array(array) = self {\n Some(array)\n } else {\n None\n }\n }\n\n pub fn is_str(\u0026self) -\u003e bool {\n if let JSONValue::String(_) = self {\n true\n } else {\n false\n }\n }\n\n pub fn to_str(\u0026self) -\u003e Option\u003c\u0026'a str\u003e {\n if let JSONValue::String(string) = self {\n Some(string)\n } else {\n None\n }\n }\n\n pub fn is_number(\u0026self) -\u003e bool {\n if let JSONValue::Number(_) = self {\n true\n } else {\n false\n }\n }\n\n pub fn to_f64(\u0026self) -\u003e Option\u003cf64\u003e {\n if let JSONValue::Number(number) = self {\n match number {\n Number::F64(f64_) =\u003e Some(*f64_),\n Number::U64(u64_) =\u003e Some(*u64_ as f64),\n }\n } else {\n None\n }\n }\n\n pub fn to_u64(\u0026self) -\u003e Option\u003cu64\u003e {\n if let JSONValue::Number(number) = self {\n if let Number::U64(u64_) = number {\n Some(*u64_)\n } else {\n None\n }\n } else {\n None\n }\n }\n\n pub fn is_bool(\u0026self) -\u003e bool {\n if let JSONValue::Boolean(_) = self {\n true\n } else {\n false\n }\n }\n\n pub fn to_bool(\u0026self) -\u003e Option\u003cbool\u003e {\n if let JSONValue::Boolean(boolean) = self {\n Some(*boolean)\n } else {\n None\n }\n }\n\n pub fn is_null(\u0026self) -\u003e bool {\n if let JSONValue::Null = self {\n true\n } else {\n false\n }\n }\n}\n\nimpl\u003c'a, S: std::hash::BuildHasher\u003e ToString for JSONValue\u003c'a, S\u003e {\n fn to_string(\u0026self) -\u003e String {\n match self {\n JSONValue::Object(o) =\u003e {\n let contents: Vec\u003c_\u003e = o\n .iter()\n .map(|(name, value)| format!(\"\\\"{}\\\":{}\", name, value.to_string()))\n .collect();\n format!(\"{{{}}}\", contents.join(\",\"))\n }\n JSONValue::Array(a) =\u003e {\n let contents: Vec\u003c_\u003e = a.iter().map(Self::to_string).collect();\n format!(\"[{}]\", contents.join(\",\"))\n }\n JSONValue::String(s) =\u003e format!(\"\\\"{}\\\"\", s),\n JSONValue::Number(n) =\u003e match n {\n Number::F64(f64_) =\u003e format!(\"{}\", f64_),\n Number::U64(u64_) =\u003e format!(\"{}\", u64_),\n },\n JSONValue::Boolean(b) =\u003e format!(\"{}\", b),\n JSONValue::Null =\u003e \"null\".to_owned(),\n }\n }\n}\n\n#[derive(Debug, Fail)]\n#[fail(display = \"Fail to parse JSON String : {:?}\", cause)]\npub struct ParseJsonError {\n pub cause: String,\n}\n\npub fn parse_json_string\u003c'a\u003e(\n source: \u0026'a str,\n) -\u003e Result\u003c\n JSONValue\u003c'a, std::hash::BuildHasherDefault\u003cstd::collections::hash_map::DefaultHasher\u003e\u003e,\n ParseJsonError,\n\u003e {\n parse_json_string_with_specific_hasher::\u003c\n std::hash::BuildHasherDefault\u003cstd::collections::hash_map::DefaultHasher\u003e,\n \u003e(source)\n}\n\npub fn parse_json_string_with_specific_hasher\u003cS: std::hash::BuildHasher + Default\u003e(\n source: \u0026str,\n) -\u003e Result\u003cJSONValue\u003cS\u003e, ParseJsonError\u003e {\n match JSONParser::parse(Rule::json, source) {\n Ok(mut pair) =\u003e Ok(parse_value(pair.next().unwrap())),\n Err(pest_error) =\u003e Err(ParseJsonError {\n cause: format!(\"{:?}\", pest_error),\n }),\n }\n}\n\nfn parse_value\u003cS: std::hash::BuildHasher + Default\u003e(pair: Pair\u003cRule\u003e) -\u003e JSONValue\u003cS\u003e {\n match pair.as_rule() {\n Rule::object =\u003e JSONValue::Object(\n pair.into_inner()\n .map(|pair| {\n let mut inner_rules = pair.into_inner();\n let name = inner_rules\n .next()\n .unwrap()\n .into_inner()\n .next()\n .unwrap()\n .as_str();\n let value = parse_value(inner_rules.next().unwrap());\n (name, value)\n })\n .collect(),\n ),\n Rule::array =\u003e JSONValue::Array(pair.into_inner().map(parse_value).collect()),\n Rule::string =\u003e JSONValue::String(pair.into_inner().next().unwrap().as_str()),\n Rule::number =\u003e {\n if let Ok(number_u64) = u64::from_str(pair.as_str()) {\n JSONValue::Number(Number::U64(number_u64))\n } else {\n JSONValue::Number(Number::F64(pair.as_str().parse().unwrap()))\n }\n }\n Rule::boolean =\u003e JSONValue::Boolean(pair.as_str().parse().unwrap()),\n Rule::null =\u003e JSONValue::Null,\n Rule::json\n | Rule::EOI\n | Rule::pair\n | Rule::value\n | Rule::inner_string\n | Rule::char\n | Rule::WHITESPACE =\u003e unreachable!(),\n }\n}\n\npub fn get_optional_usize\u003cS: std::hash::BuildHasher\u003e(\n json_block: \u0026HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cOption\u003cusize\u003e, Error\u003e {\n Ok(match json_block.get(field) {\n Some(value) =\u003e {\n if !value.is_null() {\n Some(\n value\n .to_f64()\n .ok_or_else(|| ParseJsonError {\n cause: format!(\n \"Fail to parse json : field '{}' must be a number !\",\n field\n ),\n })?\n .trunc() as usize,\n )\n } else {\n None\n }\n }\n None =\u003e None,\n })\n}\n\npub fn get_optional_str_not_empty\u003c'a, S: std::hash::BuildHasher\u003e(\n json_block: \u0026'a HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cOption\u003c\u0026'a str\u003e, Error\u003e {\n let result = get_optional_str(json_block, field);\n if let Ok(Some(value)) = result {\n if !value.is_empty() {\n Ok(Some(value))\n } else {\n Ok(None)\n }\n } else {\n result\n }\n}\n\npub fn get_optional_str\u003c'a, S: std::hash::BuildHasher\u003e(\n json_block: \u0026'a HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cOption\u003c\u0026'a str\u003e, Error\u003e {\n Ok(match json_block.get(field) {\n Some(value) =\u003e {\n if !value.is_null() {\n Some(value.to_str().ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be a string !\", field),\n })?)\n } else {\n None\n }\n }\n None =\u003e None,\n })\n}\n\npub fn get_u64\u003cS: std::hash::BuildHasher\u003e(\n json_block: \u0026HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cu64, Error\u003e {\n Ok(json_block\n .get(field)\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must exist !\", field),\n })?\n .to_u64()\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be a number !\", field),\n })?)\n}\n\npub fn get_number\u003cS: std::hash::BuildHasher\u003e(\n json_block: \u0026HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cf64, Error\u003e {\n Ok(json_block\n .get(field)\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must exist !\", field),\n })?\n .to_f64()\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be a number !\", field),\n })?)\n}\n\npub fn get_str\u003c'a, S: std::hash::BuildHasher\u003e(\n json_block: \u0026'a HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003c\u0026'a str, Error\u003e {\n Ok(json_block\n .get(field)\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must exist !\", field),\n })?\n .to_str()\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be a string !\", field),\n })?)\n}\n\npub fn get_str_array\u003c'a, S: std::hash::BuildHasher\u003e(\n json_block: \u0026'a HashMap\u003c\u0026str, JSONValue\u003cS\u003e, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cVec\u003c\u0026'a str\u003e, ParseJsonError\u003e {\n json_block\n .get(field)\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must exist !\", field),\n })?\n .to_array()\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be an array !\", field),\n })?\n .iter()\n .map(|v| {\n v.to_str().ok_or_else(|| ParseJsonError {\n cause: format!(\n \"Fail to parse json : field '{}' must be an array of string !\",\n field\n ),\n })\n })\n .collect()\n}\n\npub fn get_object_array\u003c'a, S: std::hash::BuildHasher\u003e(\n json_block: \u0026'a JsonObject\u003c'a, S\u003e,\n field: \u0026str,\n) -\u003e Result\u003cVec\u003c\u0026'a JsonObject\u003c'a, S\u003e\u003e, ParseJsonError\u003e {\n json_block\n .get(field)\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must exist !\", field),\n })?\n .to_array()\n .ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be an array !\", field),\n })?\n .iter()\n .map(|v| {\n v.to_object().ok_or_else(|| ParseJsonError {\n cause: format!(\"Fail to parse json : field '{}' must be an object !\", field),\n })\n })\n .collect()\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn test_parse_too_large_number() {\n assert_eq!(\n Ok(100_010_200_000_006_940),\n u64::from_str(\"100010200000006940\"),\n );\n\n let json_string = \"{\n \\\"nonce\\\": 100010200000006940\n }\";\n\n let json_value = parse_json_string(json_string).expect(\"Fail to parse json string !\");\n\n assert!(json_value.is_object());\n\n let json_object = json_value.to_object().expect(\"safe unwrap\");\n\n assert_eq!(\n json_object.get(\"nonce\"),\n Some(\u0026JSONValue::Number(Number::U64(100_010_200_000_006_940)))\n );\n }\n\n #[test]\n fn test_parse_json_string() {\n let json_string = \"{\n \\\"name\\\": \\\"toto\\\",\n \\\"age\\\": 25,\n \\\"friends\\\": [\n \\\"titi\\\",\n \\\"tata\\\"\n ]\n }\";\n\n let json_value = parse_json_string(json_string).expect(\"Fail to parse json string !\");\n\n assert!(json_value.is_object());\n\n let json_object = json_value.to_object().expect(\"safe unwrap\");\n\n assert_eq!(json_object.get(\"name\"), Some(\u0026JSONValue::String(\"toto\")));\n assert_eq!(\n json_object.get(\"age\"),\n Some(\u0026JSONValue::Number(Number::U64(25u64)))\n );\n\n let friends = json_object\n .get(\"friends\")\n .expect(\"frinds field must be exist\")\n .to_array()\n .expect(\"frinds field must be an array\");\n\n assert_eq!(2, friends.len());\n assert_eq!(\n \"titi\",\n friends[0]\n .to_str()\n .expect(\"friends field must be an array of String\")\n );\n assert_eq!(\n \"tata\",\n friends[1]\n .to_str()\n .expect(\"friends field must be an array of String\")\n );\n }\n\n}\n","traces":[{"line":68,"address":null,"length":0,"stats":{"Line":3}},{"line":69,"address":null,"length":0,"stats":{"Line":3}},{"line":70,"address":null,"length":0,"stats":{"Line":3}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":3}},{"line":77,"address":null,"length":0,"stats":{"Line":3}},{"line":78,"address":null,"length":0,"stats":{"Line":3}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":3}},{"line":93,"address":null,"length":0,"stats":{"Line":3}},{"line":94,"address":null,"length":0,"stats":{"Line":3}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":3}},{"line":109,"address":null,"length":0,"stats":{"Line":3}},{"line":110,"address":null,"length":0,"stats":{"Line":3}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":2}},{"line":125,"address":null,"length":0,"stats":{"Line":2}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":2}},{"line":128,"address":null,"length":0,"stats":{"Line":2}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":2}},{"line":136,"address":null,"length":0,"stats":{"Line":2}},{"line":137,"address":null,"length":0,"stats":{"Line":2}},{"line":138,"address":null,"length":0,"stats":{"Line":2}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":2}},{"line":164,"address":null,"length":0,"stats":{"Line":2}},{"line":165,"address":null,"length":0,"stats":{"Line":2}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":2}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":4256368,"length":1,"stats":{"Line":2}},{"line":211,"address":4256385,"length":1,"stats":{"Line":2}},{"line":214,"address":4278832,"length":1,"stats":{"Line":2}},{"line":217,"address":4278852,"length":1,"stats":{"Line":2}},{"line":218,"address":4278966,"length":1,"stats":{"Line":2}},{"line":219,"address":4279201,"length":1,"stats":{"Line":0}},{"line":220,"address":4279257,"length":1,"stats":{"Line":0}},{"line":225,"address":4279952,"length":1,"stats":{"Line":2}},{"line":226,"address":4279962,"length":1,"stats":{"Line":2}},{"line":228,"address":4280085,"length":1,"stats":{"Line":2}},{"line":229,"address":4281168,"length":1,"stats":{"Line":2}},{"line":230,"address":4281183,"length":1,"stats":{"Line":2}},{"line":231,"address":4281291,"length":1,"stats":{"Line":2}},{"line":237,"address":4281379,"length":1,"stats":{"Line":0}},{"line":238,"address":4281526,"length":1,"stats":{"Line":2}},{"line":239,"address":4281579,"length":1,"stats":{"Line":2}},{"line":243,"address":4280236,"length":1,"stats":{"Line":2}},{"line":244,"address":4280390,"length":1,"stats":{"Line":2}},{"line":246,"address":4280619,"length":1,"stats":{"Line":2}},{"line":247,"address":4280701,"length":1,"stats":{"Line":2}},{"line":249,"address":4280768,"length":1,"stats":{"Line":0}},{"line":252,"address":4280897,"length":1,"stats":{"Line":0}},{"line":253,"address":4280977,"length":1,"stats":{"Line":1}},{"line":264,"address":5372288,"length":1,"stats":{"Line":2}},{"line":268,"address":5372313,"length":1,"stats":{"Line":2}},{"line":269,"address":5372380,"length":1,"stats":{"Line":2}},{"line":270,"address":5372448,"length":1,"stats":{"Line":2}},{"line":271,"address":5372899,"length":1,"stats":{"Line":0}},{"line":272,"address":5372481,"length":1,"stats":{"Line":0}},{"line":274,"address":5372506,"length":1,"stats":{"Line":0}},{"line":276,"address":5373163,"length":1,"stats":{"Line":0}},{"line":277,"address":5373158,"length":1,"stats":{"Line":0}},{"line":282,"address":5372627,"length":1,"stats":{"Line":0}},{"line":283,"address":5372948,"length":1,"stats":{"Line":2}},{"line":286,"address":5372959,"length":1,"stats":{"Line":0}},{"line":290,"address":5373360,"length":1,"stats":{"Line":2}},{"line":294,"address":5373385,"length":1,"stats":{"Line":2}},{"line":295,"address":5373457,"length":1,"stats":{"Line":2}},{"line":296,"address":5373490,"length":1,"stats":{"Line":2}},{"line":297,"address":5373577,"length":1,"stats":{"Line":0}},{"line":299,"address":5373629,"length":1,"stats":{"Line":2}},{"line":302,"address":5373670,"length":1,"stats":{"Line":0}},{"line":306,"address":5370624,"length":1,"stats":{"Line":2}},{"line":310,"address":5370649,"length":1,"stats":{"Line":2}},{"line":311,"address":5370708,"length":1,"stats":{"Line":2}},{"line":312,"address":5370776,"length":1,"stats":{"Line":2}},{"line":313,"address":5370809,"length":1,"stats":{"Line":2}},{"line":314,"address":5371350,"length":1,"stats":{"Line":0}},{"line":316,"address":5370902,"length":1,"stats":{"Line":0}},{"line":317,"address":5371197,"length":1,"stats":{"Line":0}},{"line":320,"address":5371208,"length":1,"stats":{"Line":1}},{"line":324,"address":5375296,"length":1,"stats":{"Line":2}},{"line":328,"address":5375324,"length":1,"stats":{"Line":2}},{"line":329,"address":5375337,"length":1,"stats":{"Line":2}},{"line":330,"address":5375391,"length":1,"stats":{"Line":2}},{"line":331,"address":5376358,"length":1,"stats":{"Line":0}},{"line":334,"address":5375735,"length":1,"stats":{"Line":2}},{"line":335,"address":5376582,"length":1,"stats":{"Line":0}},{"line":339,"address":5367136,"length":1,"stats":{"Line":2}},{"line":343,"address":5367164,"length":1,"stats":{"Line":2}},{"line":344,"address":5367177,"length":1,"stats":{"Line":2}},{"line":345,"address":5367231,"length":1,"stats":{"Line":2}},{"line":346,"address":5368198,"length":1,"stats":{"Line":0}},{"line":349,"address":5367576,"length":1,"stats":{"Line":2}},{"line":350,"address":5368422,"length":1,"stats":{"Line":0}},{"line":354,"address":5373792,"length":1,"stats":{"Line":2}},{"line":358,"address":5373820,"length":1,"stats":{"Line":2}},{"line":359,"address":5373833,"length":1,"stats":{"Line":2}},{"line":360,"address":5373887,"length":1,"stats":{"Line":2}},{"line":361,"address":5375094,"length":1,"stats":{"Line":0}},{"line":364,"address":5374231,"length":1,"stats":{"Line":2}},{"line":365,"address":5374870,"length":1,"stats":{"Line":0}},{"line":369,"address":5368624,"length":1,"stats":{"Line":2}},{"line":373,"address":5368649,"length":1,"stats":{"Line":2}},{"line":421,"address":4251920,"length":1,"stats":{"Line":2}},{"line":422,"address":4404697,"length":1,"stats":{"Line":1}},{"line":424,"address":4404654,"length":1,"stats":{"Line":1}},{"line":427,"address":4404974,"length":1,"stats":{"Line":1}},{"line":431,"address":4404994,"length":1,"stats":{"Line":1}},{"line":433,"address":4405067,"length":1,"stats":{"Line":1}},{"line":435,"address":4405149,"length":1,"stats":{"Line":1}},{"line":437,"address":4405256,"length":1,"stats":{"Line":1}},{"line":438,"address":4405203,"length":1,"stats":{"Line":1}},{"line":444,"address":4251952,"length":1,"stats":{"Line":2}},{"line":445,"address":4405662,"length":1,"stats":{"Line":1}},{"line":454,"address":4405682,"length":1,"stats":{"Line":1}},{"line":456,"address":4405772,"length":1,"stats":{"Line":1}},{"line":458,"address":4405857,"length":1,"stats":{"Line":1}},{"line":460,"address":4405923,"length":1,"stats":{"Line":1}},{"line":461,"address":4406365,"length":1,"stats":{"Line":1}},{"line":462,"address":4406306,"length":1,"stats":{"Line":1}},{"line":466,"address":4406689,"length":1,"stats":{"Line":1}},{"line":472,"address":4406833,"length":1,"stats":{"Line":1}},{"line":473,"address":4407308,"length":1,"stats":{"Line":1}},{"line":475,"address":4407004,"length":1,"stats":{"Line":1}},{"line":479,"address":4407738,"length":1,"stats":{"Line":1}},{"line":481,"address":4407626,"length":1,"stats":{"Line":1}}],"covered":98,"coverable":179},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Implements the Documents of DUNP (DUniter Network Protocol).\n\n#![deny(\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces\n)]\n\n#[macro_use]\nextern crate pest_derive;\n#[cfg(test)]\n#[macro_use]\nextern crate pretty_assertions;\n#[macro_use]\nextern crate log;\n\npub mod host;\npub mod network_endpoint;\npub mod network_head;\npub mod network_head_v2;\npub mod network_head_v3;\npub mod network_peer;\npub mod url;\n\nuse crate::network_head::NetworkHead;\nuse crate::network_head_v3::NetworkHeadV3;\nuse crate::network_peer::PeerCard;\nuse crate::network_peer::PeerCardV11;\nuse dubp_documents::{TextDocumentParseError, TextDocumentParser};\nuse dup_crypto::hashs::*;\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\nuse pest::iterators::Pair;\nuse pest::Parser;\nuse serde::{Deserialize, Serialize};\nuse std::fmt::{Display, Error, Formatter};\nuse std::net::AddrParseError;\n\n#[derive(Parser)]\n#[grammar = \"network_documents.pest\"]\n/// Parser for network documents\nstruct NetworkDocsParser;\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\n/// Network document\npub enum NetworkDocument {\n /// Peer\n Peer(Box\u003cPeerCard\u003e),\n /// Head\n Head(NetworkHead),\n}\n\nimpl TextDocumentParser\u003cRule\u003e for NetworkDocument {\n type DocumentType = NetworkDocument;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cNetworkDocument, TextDocumentParseError\u003e {\n let mut net_doc_pairs = NetworkDocsParser::parse(Rule::network_document, doc)?;\n Ok(NetworkDocument::from_pest_pair(\n net_doc_pairs.next().unwrap().into_inner().next().unwrap(), // get and unwrap the `network_document` rule; never fails\n )?)\n }\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cNetworkDocument, TextDocumentParseError\u003e {\n Ok(match pair.as_rule() {\n Rule::peer_v11 =\u003e {\n NetworkDocument::Peer(Box::new(PeerCard::V11(PeerCardV11::from_pest_pair(pair)?)))\n }\n Rule::head_v3 =\u003e NetworkDocument::Head(NetworkHead::V3(Box::new(\n NetworkHeadV3::from_pest_pair(pair)?,\n ))),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", pair.as_rule()), // Grammar ensures that we never reach this line\n })\n }\n fn from_versioned_pest_pair(\n _version: u16,\n _pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cNetworkDocument, TextDocumentParseError\u003e {\n fatal_error!(\"Network document Network documents are not versioned together, please use from_pest_pair() instead.\")\n }\n}\n\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Random identifier with which several Duniter nodes with the same network keypair can be differentiated\npub struct NodeId(pub u32);\n\nimpl Default for NodeId {\n fn default() -\u003e NodeId {\n NodeId(0)\n }\n}\n\nimpl Display for NodeId {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{:x}\", self.0)\n }\n}\n\nimpl\u003c'a\u003e From\u003c\u0026'a str\u003e for NodeId {\n fn from(source: \u0026'a str) -\u003e NodeId {\n NodeId(u32::from_str_radix(source, 16).expect(\"Fail to parse NodeId\"))\n }\n}\n\n#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]\n/// Complete identifier of a duniter node.\npub struct NodeFullId(pub NodeId, pub PubKey);\n\nimpl Default for NodeFullId {\n fn default() -\u003e NodeFullId {\n NodeFullId(\n NodeId::default(),\n PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\")\n .unwrap(),\n ),\n )\n }\n}\n\nimpl Display for NodeFullId {\n fn fmt(\u0026self, f: \u0026mut Formatter) -\u003e Result\u003c(), Error\u003e {\n write!(f, \"{}-{}\", self.0, self.1)\n }\n}\n\nimpl NodeFullId {\n /// Compute sha256 hash\n pub fn sha256(\u0026self) -\u003e Hash {\n Hash::compute(format!(\"{}\", self).as_bytes())\n }\n /// To human string\n pub fn to_human_string(\u0026self) -\u003e String {\n let mut pubkey_string = self.1.to_string();\n pubkey_string.truncate(8);\n format!(\"{:8x}-{:8}\", (self.0).0, pubkey_string)\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::network_endpoint::*;\n use super::*;\n\n pub fn keypair1() -\u003e ed25519::KeyPair {\n ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV\".as_bytes(),\n \"JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_\".as_bytes(),\n )\n }\n\n #[test]\n fn parse_endpoint() {\n let issuer = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx\")\n .unwrap(),\n );\n let node_id = NodeId(u32::from_str_radix(\"c1c39a0a\", 16).unwrap());\n let full_id = NodeFullId(node_id, issuer);\n assert_eq!(\n EndpointV1::parse_from_raw(\"WS2P c1c39a0a i3.ifee.fr 80 /ws2p\", issuer, 0, 0),\n Ok(EndpointV1 {\n issuer,\n api: ApiName(String::from(\"WS2P\")),\n node_id: Some(node_id),\n hash_full_id: Some(full_id.sha256()),\n host: String::from(\"i3.ifee.fr\"),\n port: 80,\n path: Some(String::from(\"ws2p\")),\n raw_endpoint: String::from(\"WS2P c1c39a0a i3.ifee.fr 80 /ws2p\"),\n last_check: 0,\n status: 0,\n })\n );\n }\n\n #[test]\n fn parse_endpoint2() {\n let issuer = PubKey::Ed25519(\n ed25519::PublicKey::from_base58(\"5gJYnQp8v7bWwk7EWRoL8vCLof1r3y9c6VDdnGSM1GLv\")\n .unwrap(),\n );\n let node_id = NodeId(u32::from_str_radix(\"cb06a19b\", 16).unwrap());\n let full_id = NodeFullId(node_id, issuer);\n assert_eq!(\n EndpointV1::parse_from_raw(\"WS2P cb06a19b g1.imirhil.fr 53012\", issuer, 0, 0),\n Ok(EndpointV1 {\n issuer,\n api: ApiName(String::from(\"WS2P\")),\n node_id: Some(node_id),\n hash_full_id: Some(full_id.sha256()),\n host: String::from(\"g1.imirhil.fr\"),\n port: 53012,\n path: None,\n raw_endpoint: String::from(\"WS2P cb06a19b g1.imirhil.fr 53012\"),\n last_check: 0,\n status: 0,\n })\n );\n }\n}\n","traces":[{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":5543530,"length":1,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":5546062,"length":1,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":3}},{"line":112,"address":null,"length":0,"stats":{"Line":3}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":1}},{"line":140,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":4876448,"length":1,"stats":{"Line":1}},{"line":163,"address":4876458,"length":1,"stats":{"Line":1}},{"line":170,"address":4876672,"length":1,"stats":{"Line":2}},{"line":171,"address":4876753,"length":1,"stats":{"Line":1}},{"line":172,"address":4876686,"length":1,"stats":{"Line":1}},{"line":175,"address":4876813,"length":1,"stats":{"Line":1}},{"line":176,"address":4876900,"length":1,"stats":{"Line":1}},{"line":177,"address":4877240,"length":1,"stats":{"Line":0}},{"line":178,"address":4877021,"length":1,"stats":{"Line":1}},{"line":179,"address":4877459,"length":1,"stats":{"Line":1}},{"line":180,"address":4877093,"length":1,"stats":{"Line":1}},{"line":181,"address":4877133,"length":1,"stats":{"Line":1}},{"line":182,"address":4877192,"length":1,"stats":{"Line":1}},{"line":183,"address":4877233,"length":1,"stats":{"Line":1}},{"line":184,"address":4877313,"length":1,"stats":{"Line":1}},{"line":186,"address":4877340,"length":1,"stats":{"Line":1}},{"line":187,"address":4877399,"length":1,"stats":{"Line":1}},{"line":195,"address":4878304,"length":1,"stats":{"Line":2}},{"line":196,"address":4878385,"length":1,"stats":{"Line":1}},{"line":197,"address":4878318,"length":1,"stats":{"Line":1}},{"line":200,"address":4878445,"length":1,"stats":{"Line":1}},{"line":201,"address":4878532,"length":1,"stats":{"Line":1}},{"line":202,"address":4878872,"length":1,"stats":{"Line":0}},{"line":203,"address":4878653,"length":1,"stats":{"Line":1}},{"line":204,"address":4879011,"length":1,"stats":{"Line":1}},{"line":205,"address":4878725,"length":1,"stats":{"Line":1}},{"line":206,"address":4878765,"length":1,"stats":{"Line":1}},{"line":207,"address":4878824,"length":1,"stats":{"Line":1}},{"line":208,"address":4878865,"length":1,"stats":{"Line":1}},{"line":209,"address":4878945,"length":1,"stats":{"Line":1}},{"line":211,"address":4878972,"length":1,"stats":{"Line":1}},{"line":212,"address":4878984,"length":1,"stats":{"Line":1}}],"covered":36,"coverable":63},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","network_endpoint.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module defining the format of network endpoints and how to handle them.\n\nuse crate::*;\nuse dup_crypto::hashs::Hash;\nuse dup_crypto::keys::PubKey;\nuse hex;\nuse pest::iterators::Pair;\nuse pest::Parser;\nuse std::collections::HashSet;\nuse std::net::{Ipv4Addr, Ipv6Addr};\nuse std::str::FromStr;\n\n/// Total size of all fixed size fields of an EndpointV2\npub static ENDPOINTV2_FIXED_SIZE: \u0026'static usize = \u00269;\n/// Maximum number of network features\npub static MAX_NETWORK_FEATURES_COUNT: \u0026'static usize = \u00262040;\n/// Maximum number of api features\npub static MAX_API_FEATURES_COUNT: \u0026'static usize = \u00262040;\n\n#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]\n/// ApiFeatures\npub struct ApiFeatures(pub Vec\u003cu8\u003e);\n\nimpl ApiFeatures {\n fn is_empty(\u0026self) -\u003e bool {\n for byte in \u0026self.0 {\n if *byte \u003e 0u8 {\n return false;\n }\n }\n true\n }\n\n fn to_string(\u0026self) -\u003e String {\n if self.is_empty() {\n String::from(\"\")\n } else {\n let hex_str = hex::encode(self.0.clone());\n if hex_str.len() == 2 {\n format!(\"0x{} \", \u0026hex_str[1..])\n } else {\n format!(\"0x{} \", hex_str)\n }\n }\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Identifies the API of an endpoint\npub struct ApiName(pub String);\n\n/// Api version\n#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]\npub struct ApiVersion(pub usize);\n\n/// Api parts\n#[derive(Clone, Debug)]\npub struct ApiPart {\n pub name: ApiName,\n pub versions: HashSet\u003cApiVersion\u003e,\n}\n\nimpl ApiPart {\n pub fn union_exist(\u0026self, other: \u0026Self) -\u003e bool {\n if self.name == other.name {\n self.versions.intersection(\u0026other.versions).count() \u003e 0\n } else {\n false\n }\n }\n pub fn contains(\u0026self, api_name: \u0026ApiName, api_version: ApiVersion) -\u003e bool {\n if self.name == *api_name {\n self.versions.contains(\u0026api_version)\n } else {\n false\n }\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Endpoint v1\npub struct EndpointV1 {\n /// API Name\n pub api: ApiName,\n /// Node unique identifier\n pub node_id: Option\u003cNodeId\u003e,\n /// Public key of the node declaring this endpoint\n pub issuer: PubKey,\n /// NodeFullID hash\n pub hash_full_id: Option\u003cHash\u003e,\n /// hostname\n pub host: String,\n /// port number\n pub port: usize,\n /// Optional path\n pub path: Option\u003cString\u003e,\n /// Endpoint in raw format (as it appears on the peer card)\n pub raw_endpoint: String,\n /// Accessibility status of this endpoint (updated regularly)\n pub status: u32,\n /// Timestamp of the last connection attempt to this endpoint\n pub last_check: u64,\n}\n\nimpl EndpointV1 {\n /// Accessors providing node full identifier\n pub fn node_full_id(\u0026self) -\u003e Option\u003cNodeFullId\u003e {\n match self.node_id {\n Some(node_id) =\u003e Some(NodeFullId(node_id, self.issuer)),\n None =\u003e None,\n }\n }\n /// Generate endpoint url\n pub fn get_url(\u0026self, get_protocol: bool, _supported_ip_v6: bool) -\u003e Option\u003cString\u003e {\n let protocol = match \u0026self.api.0[..] {\n \"WS2P\" | \"WS2PTOR\" =\u003e \"ws\",\n _ =\u003e \"http\",\n };\n let tls = match self.port {\n 443 =\u003e \"s\",\n _ =\u003e \"\",\n };\n let path = match self.path {\n Some(ref path_string) =\u003e path_string.clone(),\n None =\u003e String::new(),\n };\n if get_protocol {\n Some(format!(\n \"{}{}://{}:{}/{}\",\n protocol, tls, self.host, self.port, path\n ))\n } else {\n Some(format!(\"{}:{}/{}\", self.host, self.port, path))\n }\n }\n /// Generate from pest pair\n fn from_pest_pair(\n pair: Pair\u003cRule\u003e,\n issuer: PubKey,\n status: u32,\n last_check: u64,\n ) -\u003e EndpointV1 {\n let raw_endpoint = String::from(pair.as_str());\n let mut api_name = \"\";\n let mut node_id = None;\n let mut hash_full_id = None;\n let mut host_str = \"\";\n let mut port = 0;\n let mut path = None;\n\n for ep_pair in pair.into_inner() {\n match ep_pair.as_rule() {\n Rule::api_name =\u003e api_name = ep_pair.as_str(),\n Rule::node_id =\u003e {\n node_id = Some(NodeId(u32::from_str_radix(ep_pair.as_str(), 16).unwrap()));\n hash_full_id = match node_id {\n Some(node_id_) =\u003e Some(NodeFullId(node_id_, issuer).sha256()),\n None =\u003e None,\n };\n }\n Rule::host =\u003e host_str = ep_pair.as_str(),\n Rule::port =\u003e port = ep_pair.as_str().parse().unwrap(),\n Rule::path_inner =\u003e path = Some(String::from(ep_pair.as_str())),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", ep_pair.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n EndpointV1 {\n issuer,\n api: ApiName(String::from(api_name)),\n node_id,\n hash_full_id,\n host: String::from(host_str),\n port,\n path,\n raw_endpoint,\n status,\n last_check,\n }\n }\n\n /// parse from ut8 format\n pub fn parse_from_raw(\n raw_endpoint: \u0026str,\n issuer: PubKey,\n status: u32,\n last_check: u64,\n ) -\u003e Result\u003cEndpointV1, TextDocumentParseError\u003e {\n let mut ep_v1_pairs = NetworkDocsParser::parse(Rule::endpoint_v1, raw_endpoint)?;\n Ok(EndpointV1::from_pest_pair(\n ep_v1_pairs.next().unwrap(),\n issuer,\n status,\n last_check,\n ))\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Network features\npub struct EndpointV2NetworkFeatures(pub Vec\u003cu8\u003e);\n\nimpl ToString for EndpointV2NetworkFeatures {\n fn to_string(\u0026self) -\u003e String {\n if self.is_empty() {\n return \"\".to_owned();\n }\n let mut features_str = Vec::with_capacity(2);\n if self.http() {\n features_str.push(\"HTTP\");\n }\n if self.ws() {\n features_str.push(\"WS\");\n }\n if self.tls() {\n features_str.push(\"S\");\n }\n if self.tor() {\n features_str.push(\"TOR\");\n }\n format!(\"{} \", features_str.join(\" \"))\n }\n}\n\nimpl EndpointV2NetworkFeatures {\n fn is_empty(\u0026self) -\u003e bool {\n for byte in \u0026self.0 {\n if *byte \u003e 0u8 {\n return false;\n }\n }\n true\n }\n /// Network features size\n pub fn size(\u0026self) -\u003e u8 {\n self.0.len() as u8\n }\n /// Convert Self into bytes\n pub fn to_bytes_slice(\u0026self) -\u003e \u0026[u8] {\n \u0026self.0\n }\n /// HTTP feature is enable ?\n pub fn http(\u0026self) -\u003e bool {\n self.0[0] \u0026 0b0000_0001 == 1u8\n }\n /// WS feature is enable ?\n pub fn ws(\u0026self) -\u003e bool {\n self.0[0] \u0026 0b0000_0010 == 2u8\n }\n /// TLS feature is enable ?\n pub fn tls(\u0026self) -\u003e bool {\n self.0[0] \u0026 0b0000_0100 == 4u8\n }\n /// TOR feature is enable ?\n pub fn tor(\u0026self) -\u003e bool {\n self.0[0] \u0026 0b0000_1000 == 8u8\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Endpoint\npub struct Endpoint {\n /// Endpoint content\n pub content: EndpointEnum,\n /// Accessibility status of this endpoint (updated regularly)\n pub status: u32,\n /// Timestamp of the last connection attempt to this endpoint\n pub last_check: u64,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Endpoint v2\npub struct EndpointV2 {\n /// API Name\n pub api: ApiName,\n /// API version\n pub api_version: u16,\n /// Network features\n pub network_features: EndpointV2NetworkFeatures,\n /// API features\n pub api_features: ApiFeatures,\n /// Domain name\n pub domain: Option\u003cString\u003e,\n /// IPv4\n pub ip_v4: Option\u003cIpv4Addr\u003e,\n /// IPv6\n pub ip_v6: Option\u003cIpv6Addr\u003e,\n /// port number\n pub port: u16,\n /// Optional path\n pub path: Option\u003cString\u003e,\n}\n\nimpl ToString for EndpointV2 {\n fn to_string(\u0026self) -\u003e String {\n let domain: String = if let Some(ref domain) = self.domain {\n format!(\"{} \", domain)\n } else {\n String::from(\"\")\n };\n let ip4: String = if let Some(ip4) = self.ip_v4 {\n format!(\"{} \", ip4.to_string())\n } else {\n String::from(\"\")\n };\n let ip6: String = if let Some(ip6) = self.ip_v6 {\n format!(\"[{}] \", ip6.to_string())\n } else {\n String::from(\"\")\n };\n let path = if let Some(ref path) = self.path {\n format!(\" {}\", path)\n } else {\n \"\".to_owned()\n };\n format!(\n \"{api} {version}{nf}{af}{ip4}{ip6}{domain}{port}{path}\",\n api = self.api.0,\n version = if self.api_version \u003e 0 {\n format!(\"V{} \", self.api_version)\n } else {\n \"\".to_owned()\n },\n nf = self.network_features.to_string(),\n af = self.api_features.to_string(),\n port = self.port,\n domain = domain,\n ip4 = ip4,\n ip6 = ip6,\n path = path,\n )\n }\n}\n\nimpl EndpointV2 {\n /// Generate endpoint url\n pub fn get_url(\u0026self, get_protocol: bool, supported_ip_v6: bool) -\u003e Option\u003cString\u003e {\n let protocol = match \u0026self.api.0[..] {\n \"WS2P\" | \"WS2PTOR\" =\u003e \"ws\",\n _ =\u003e \"http\",\n };\n\n let tls = match self.port {\n 443 =\u003e \"s\",\n _ =\u003e \"\",\n };\n let domain = if let Some(ref domain) = self.domain {\n domain.clone()\n } else if supported_ip_v6 \u0026\u0026 self.ip_v6.is_some() {\n let ip_v6 = self.ip_v6.unwrap();\n format!(\"{}\", ip_v6)\n } else if self.ip_v4.is_some() {\n let ip_v4 = self.ip_v4.unwrap();\n format!(\"{}\", ip_v4)\n } else {\n println!(\"DEBUG: endpoint_v2={:?}\", self);\n // Unreacheable endpoint\n return None;\n };\n let path = match self.path {\n Some(ref path_string) =\u003e path_string.clone(),\n None =\u003e String::new(),\n };\n if get_protocol {\n Some(format!(\n \"{}{}://{}:{}/{}\",\n protocol, tls, domain, self.port, path\n ))\n } else {\n Some(format!(\"{}:{}/{}\", domain, self.port, path))\n }\n }\n /// Generate from pest pair\n pub fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cEndpointV2, AddrParseError\u003e {\n let mut api_str = \"\";\n let mut api_version = 0;\n let mut network_features = EndpointV2NetworkFeatures(vec![0u8]);\n let mut api_features = ApiFeatures(vec![]);\n let mut ip_v4 = None;\n let mut ip_v6 = None;\n let mut domain = None;\n let mut port = 0;\n let mut path = None;\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::api_name =\u003e api_str = field.as_str(),\n Rule::api_version_inner =\u003e api_version = field.as_str().parse().unwrap(),\n Rule::http =\u003e network_features.0[0] |= 0b_0000_0001,\n Rule::ws =\u003e network_features.0[0] |= 0b_0000_0010,\n Rule::tls =\u003e network_features.0[0] |= 0b_0000_0100,\n Rule::tor =\u003e network_features.0[0] |= 0b_0000_1000,\n Rule::api_features_inner =\u003e {\n api_features = if field.as_str().len() == 1 {\n ApiFeatures(hex::decode(\u0026format!(\"0{}\", field.as_str())).unwrap())\n } else {\n ApiFeatures(hex::decode(field.as_str()).unwrap())\n };\n }\n Rule::port =\u003e port = field.as_str().parse().unwrap(),\n Rule::domain_name_inner =\u003e domain = Some(String::from(field.as_str())),\n Rule::ip4_inner =\u003e ip_v4 = Some(Ipv4Addr::from_str(field.as_str())?),\n Rule::ip6_inner =\u003e ip_v6 = Some(Ipv6Addr::from_str(field.as_str())?),\n Rule::path_inner =\u003e path = Some(String::from(field.as_str())),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", field.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n if network_features.is_empty() {\n network_features = EndpointV2NetworkFeatures(vec![]);\n }\n\n Ok(EndpointV2 {\n api: ApiName(String::from(api_str)),\n api_version,\n network_features,\n api_features,\n domain,\n ip_v4,\n ip_v6,\n port,\n path,\n })\n }\n /// parse from raw ascii format\n pub fn parse_from_raw(raw_endpoint: \u0026str) -\u003e Result\u003cEndpointEnum, TextDocumentParseError\u003e {\n let mut ep_v2_pairs = NetworkDocsParser::parse(Rule::endpoint_v2, raw_endpoint)?;\n Ok(EndpointEnum::V2(EndpointV2::from_pest_pair(\n ep_v2_pairs.next().unwrap(),\n )?))\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Endpoint\npub enum EndpointEnum {\n /// Endpoint v1\n V1(EndpointV1),\n /// Endpoint v2\n V2(EndpointV2),\n}\n\nimpl ToString for EndpointEnum {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.raw_endpoint.clone(),\n EndpointEnum::V2(ref ep) =\u003e ep.to_string(),\n }\n }\n}\n\nimpl EndpointEnum {\n /// Accessors providing API name\n pub fn api(\u0026self) -\u003e ApiName {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.api.clone(),\n EndpointEnum::V2(ref ep) =\u003e ep.api.clone(),\n }\n }\n pub fn version(\u0026self) -\u003e ApiVersion {\n match *self {\n EndpointEnum::V1(_) =\u003e ApiVersion(1),\n EndpointEnum::V2(_) =\u003e ApiVersion(2),\n }\n }\n /// Accessors providing node unique identifier\n pub fn node_uuid(\u0026self) -\u003e Option\u003cNodeId\u003e {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.node_id,\n EndpointEnum::V2(ref _ep) =\u003e unreachable!(),\n }\n }\n /// Accessors providing node public key\n pub fn pubkey(\u0026self) -\u003e PubKey {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.issuer,\n EndpointEnum::V2(ref _ep) =\u003e unreachable!(),\n }\n }\n /// Accessors providing port number\n pub fn port(\u0026self) -\u003e usize {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.port,\n EndpointEnum::V2(ref ep) =\u003e ep.port as usize,\n }\n }\n /// Accessors providing raw format\n pub fn raw(\u0026self) -\u003e String {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.raw_endpoint.clone(),\n _ =\u003e fatal_error!(\"Endpoint version is not supported !\"),\n }\n }\n /// Accessors providing endpoint accessibility status\n pub fn status(\u0026self) -\u003e u32 {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e ep.status,\n EndpointEnum::V2(ref _ep) =\u003e unreachable!(),\n }\n }\n /// Set status\n pub fn set_status(\u0026mut self, new_status: u32) {\n match *self {\n EndpointEnum::V1(ref mut ep) =\u003e ep.status = new_status,\n EndpointEnum::V2(ref _ep) =\u003e unreachable!(),\n }\n }\n /// Set last_check\n pub fn set_last_check(\u0026mut self, new_last_check: u64) {\n match *self {\n EndpointEnum::V1(ref mut ep) =\u003e ep.last_check = new_last_check,\n EndpointEnum::V2(ref _ep) =\u003e unreachable!(),\n }\n }\n /// Generate endpoint url\n pub fn get_url(\u0026self, get_protocol: bool, supported_ip_v6: bool) -\u003e Option\u003cString\u003e {\n match *self {\n EndpointEnum::V1(ref ep) =\u003e {\n let protocol = match \u0026ep.api.0[..] {\n \"WS2P\" | \"WS2PTOR\" =\u003e \"ws\",\n _ =\u003e \"http\",\n };\n let tls = match ep.port {\n 443 =\u003e \"s\",\n _ =\u003e \"\",\n };\n let path = match ep.path {\n Some(ref path_string) =\u003e path_string.clone(),\n None =\u003e String::new(),\n };\n if get_protocol {\n Some(format!(\n \"{}{}://{}:{}/{}\",\n protocol, tls, ep.host, ep.port, path\n ))\n } else {\n Some(format!(\"{}:{}/{}\", ep.host, ep.port, path))\n }\n }\n EndpointEnum::V2(ref ep_v2) =\u003e ep_v2.get_url(get_protocol, supported_ip_v6),\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use bincode::{deserialize, serialize};\n use maplit::hashset;\n\n #[inline]\n fn api_part_1() -\u003e ApiPart {\n ApiPart {\n name: ApiName(\"api1\".to_owned()),\n versions: hashset![ApiVersion(1)],\n }\n }\n\n #[test]\n fn test_api_part_contains() {\n let api_part = api_part_1();\n\n assert_eq!(\n true,\n api_part.contains(\u0026ApiName(\"api1\".to_owned()), ApiVersion(1))\n );\n\n assert_eq!(\n false,\n api_part.contains(\u0026ApiName(\"api1\".to_owned()), ApiVersion(2))\n );\n\n assert_eq!(\n false,\n api_part.contains(\u0026ApiName(\"api2\".to_owned()), ApiVersion(1))\n );\n }\n\n #[test]\n fn test_api_part_union_exist() {\n let api_part = api_part_1();\n\n assert_eq!(\n false,\n api_part.union_exist(\u0026ApiPart {\n name: ApiName(\"api2\".to_owned()),\n versions: hashset![ApiVersion(1)],\n })\n );\n\n assert_eq!(\n false,\n api_part.union_exist(\u0026ApiPart {\n name: ApiName(\"api1\".to_owned()),\n versions: hashset![ApiVersion(2), ApiVersion(3)],\n })\n );\n\n assert_eq!(\n true,\n api_part.union_exist(\u0026ApiPart {\n name: ApiName(\"api1\".to_owned()),\n versions: hashset![ApiVersion(1), ApiVersion(2)],\n })\n );\n }\n\n #[test]\n fn test_network_features() {\n assert_eq!(EndpointV2NetworkFeatures(vec![1u8]).http(), true);\n assert_eq!(EndpointV2NetworkFeatures(vec![2u8]).ws(), true);\n assert_eq!(EndpointV2NetworkFeatures(vec![4u8]).tls(), true);\n assert_eq!(EndpointV2NetworkFeatures(vec![4u8]).tor(), false);\n assert_eq!(EndpointV2NetworkFeatures(vec![8u8]).tls(), false);\n assert_eq!(EndpointV2NetworkFeatures(vec![8u8]).tor(), true);\n assert_eq!(EndpointV2NetworkFeatures(vec![12u8]).tls(), true);\n assert_eq!(EndpointV2NetworkFeatures(vec![12u8]).tor(), true);\n\n assert_eq!(\n EndpointV2NetworkFeatures(vec![1u8]).to_string().as_str(),\n \"HTTP \"\n );\n assert_eq!(\n EndpointV2NetworkFeatures(vec![2u8]).to_string().as_str(),\n \"WS \"\n );\n assert_eq!(\n EndpointV2NetworkFeatures(vec![4u8]).to_string().as_str(),\n \"S \"\n );\n assert_eq!(\n EndpointV2NetworkFeatures(vec![8u8]).to_string().as_str(),\n \"TOR \"\n );\n assert_eq!(\n EndpointV2NetworkFeatures(vec![12u8]).to_string().as_str(),\n \"S TOR \"\n );\n }\n fn test_parse_and_read_endpoint(str_endpoint: \u0026str, endpoint: EndpointV2) {\n assert_eq!(\n EndpointV2::parse_from_raw(str_endpoint),\n Ok(EndpointEnum::V2(endpoint.clone())),\n );\n let binary_endpoint = serialize(\u0026endpoint).expect(\"Fail to serialize endpoint !\");\n let endpoint2: EndpointV2 =\n deserialize(\u0026binary_endpoint).expect(\"Fail to deserialize endpoint !\");\n assert_eq!(endpoint, endpoint2,);\n assert_eq!(str_endpoint, endpoint.to_string());\n }\n\n #[test]\n fn test_parse_and_read_minimal_endpoint() {\n let str_endpoint = \"UNKNOWN_API 8080\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"UNKNOWN_API\")),\n api_version: 0,\n network_features: EndpointV2NetworkFeatures(vec![]),\n api_features: ApiFeatures(vec![]),\n ip_v4: None,\n ip_v6: None,\n domain: None,\n port: 8080u16,\n path: None,\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint);\n }\n\n #[test]\n fn test_parse_and_read_localhost_endpoint() {\n let str_endpoint = \"WS2P localhost 10900\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 0,\n network_features: EndpointV2NetworkFeatures(vec![]),\n api_features: ApiFeatures(vec![]),\n ip_v4: None,\n ip_v6: None,\n domain: Some(String::from(\"localhost\")),\n port: 10900u16,\n path: None,\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint.clone());\n // test get_url()\n assert_eq!(\n endpoint.get_url(true, false),\n Some(\"ws://localhost:10900/\".to_owned())\n );\n }\n\n #[test]\n fn test_parse_and_read_classic_v1_endpoint() {\n let str_endpoint = \"ES_CORE_API g1.data.duniter.fr 443\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"ES_CORE_API\")),\n api_version: 0,\n network_features: EndpointV2NetworkFeatures(vec![]),\n api_features: ApiFeatures(vec![]),\n ip_v4: None,\n ip_v6: None,\n domain: Some(String::from(\"g1.data.duniter.fr\")),\n port: 443u16,\n path: None,\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint);\n }\n\n #[test]\n fn test_parse_and_read_endpoint_with_host() {\n let str_endpoint = \"WS2P V2 S 0x7 g1.durs.ifee.fr 443 ws2p\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![4u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: None,\n ip_v6: None,\n domain: Some(String::from(\"g1.durs.ifee.fr\")),\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint.clone());\n // test get_url()\n assert_eq!(\n endpoint.get_url(true, false),\n Some(\"wss://g1.durs.ifee.fr:443/ws2p\".to_owned()),\n );\n }\n\n #[test]\n fn test_parse_and_read_endpoint_with_ipv4() {\n let str_endpoint = \"WS2P V2 S 0x7 84.16.72.210 443 ws2p\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![4u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: Some(Ipv4Addr::from_str(\"84.16.72.210\").unwrap()),\n ip_v6: None,\n domain: None,\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint);\n }\n\n #[test]\n fn test_parse_and_read_endpoint_with_ipv6() {\n let str_endpoint = \"WS2P V2 S 0x7 [2001:41d0:8:c5aa::1] 443 ws2p\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![4u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: None,\n ip_v6: Some(Ipv6Addr::from_str(\"2001:41d0:8:c5aa::1\").unwrap()),\n domain: None,\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint);\n }\n\n #[test]\n fn test_parse_and_read_endpoint_with_ipv4_and_ip_v6() {\n let str_endpoint = \"WS2P V2 S 0x7 5.135.188.170 [2001:41d0:8:c5aa::1] 443 ws2p\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![4u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: Some(Ipv4Addr::from_str(\"5.135.188.170\").unwrap()),\n ip_v6: Some(Ipv6Addr::from_str(\"2001:41d0:8:c5aa::1\").unwrap()),\n domain: None,\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint);\n }\n\n #[test]\n fn test_parse_and_read_endpoint_with_all_fields() {\n let str_endpoint =\n \"WS2P V2 S 0x7 5.135.188.170 [2001:41d0:8:c5aa::1] g1.dunitrust.org 443 ws2p\";\n let endpoint = EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![4u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: Some(Ipv4Addr::from_str(\"5.135.188.170\").unwrap()),\n ip_v6: Some(Ipv6Addr::from_str(\"2001:41d0:8:c5aa::1\").unwrap()),\n domain: Some(String::from(\"g1.dunitrust.org\")),\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n };\n test_parse_and_read_endpoint(str_endpoint, endpoint);\n }\n}\n","traces":[{"line":40,"address":null,"length":0,"stats":{"Line":2}},{"line":41,"address":null,"length":0,"stats":{"Line":2}},{"line":42,"address":null,"length":0,"stats":{"Line":2}},{"line":43,"address":null,"length":0,"stats":{"Line":2}},{"line":46,"address":null,"length":0,"stats":{"Line":1}},{"line":49,"address":null,"length":0,"stats":{"Line":2}},{"line":50,"address":null,"length":0,"stats":{"Line":2}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":53,"address":null,"length":0,"stats":{"Line":2}},{"line":54,"address":null,"length":0,"stats":{"Line":2}},{"line":55,"address":null,"length":0,"stats":{"Line":2}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":57,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":86,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":0}},{"line":140,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":0}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":0}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":1}},{"line":160,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":166,"address":null,"length":0,"stats":{"Line":1}},{"line":167,"address":null,"length":0,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":1}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":5277152,"length":1,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":197,"address":null,"length":0,"stats":{"Line":1}},{"line":203,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":206,"address":null,"length":0,"stats":{"Line":1}},{"line":207,"address":null,"length":0,"stats":{"Line":1}},{"line":208,"address":null,"length":0,"stats":{"Line":1}},{"line":218,"address":null,"length":0,"stats":{"Line":2}},{"line":219,"address":null,"length":0,"stats":{"Line":2}},{"line":220,"address":null,"length":0,"stats":{"Line":1}},{"line":222,"address":null,"length":0,"stats":{"Line":2}},{"line":223,"address":null,"length":0,"stats":{"Line":2}},{"line":224,"address":null,"length":0,"stats":{"Line":2}},{"line":226,"address":null,"length":0,"stats":{"Line":2}},{"line":227,"address":null,"length":0,"stats":{"Line":1}},{"line":229,"address":null,"length":0,"stats":{"Line":2}},{"line":230,"address":null,"length":0,"stats":{"Line":1}},{"line":232,"address":null,"length":0,"stats":{"Line":2}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":235,"address":null,"length":0,"stats":{"Line":2}},{"line":240,"address":null,"length":0,"stats":{"Line":3}},{"line":241,"address":null,"length":0,"stats":{"Line":3}},{"line":242,"address":null,"length":0,"stats":{"Line":3}},{"line":243,"address":null,"length":0,"stats":{"Line":2}},{"line":246,"address":null,"length":0,"stats":{"Line":2}},{"line":249,"address":null,"length":0,"stats":{"Line":0}},{"line":250,"address":null,"length":0,"stats":{"Line":0}},{"line":253,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":257,"address":null,"length":0,"stats":{"Line":2}},{"line":258,"address":null,"length":0,"stats":{"Line":2}},{"line":261,"address":null,"length":0,"stats":{"Line":2}},{"line":262,"address":null,"length":0,"stats":{"Line":2}},{"line":265,"address":null,"length":0,"stats":{"Line":2}},{"line":266,"address":null,"length":0,"stats":{"Line":2}},{"line":269,"address":null,"length":0,"stats":{"Line":2}},{"line":270,"address":null,"length":0,"stats":{"Line":2}},{"line":309,"address":null,"length":0,"stats":{"Line":2}},{"line":310,"address":null,"length":0,"stats":{"Line":2}},{"line":311,"address":null,"length":0,"stats":{"Line":2}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":313,"address":null,"length":0,"stats":{"Line":2}},{"line":315,"address":null,"length":0,"stats":{"Line":2}},{"line":316,"address":null,"length":0,"stats":{"Line":2}},{"line":317,"address":null,"length":0,"stats":{"Line":0}},{"line":318,"address":null,"length":0,"stats":{"Line":2}},{"line":320,"address":null,"length":0,"stats":{"Line":2}},{"line":321,"address":null,"length":0,"stats":{"Line":1}},{"line":322,"address":null,"length":0,"stats":{"Line":0}},{"line":323,"address":null,"length":0,"stats":{"Line":2}},{"line":325,"address":null,"length":0,"stats":{"Line":2}},{"line":326,"address":null,"length":0,"stats":{"Line":2}},{"line":327,"address":null,"length":0,"stats":{"Line":0}},{"line":328,"address":null,"length":0,"stats":{"Line":1}},{"line":330,"address":null,"length":0,"stats":{"Line":2}},{"line":332,"address":null,"length":0,"stats":{"Line":2}},{"line":333,"address":null,"length":0,"stats":{"Line":2}},{"line":334,"address":null,"length":0,"stats":{"Line":2}},{"line":335,"address":null,"length":0,"stats":{"Line":0}},{"line":336,"address":null,"length":0,"stats":{"Line":1}},{"line":338,"address":null,"length":0,"stats":{"Line":2}},{"line":339,"address":null,"length":0,"stats":{"Line":2}},{"line":340,"address":null,"length":0,"stats":{"Line":2}},{"line":341,"address":null,"length":0,"stats":{"Line":0}},{"line":342,"address":null,"length":0,"stats":{"Line":0}},{"line":343,"address":null,"length":0,"stats":{"Line":0}},{"line":344,"address":null,"length":0,"stats":{"Line":0}},{"line":351,"address":null,"length":0,"stats":{"Line":2}},{"line":352,"address":null,"length":0,"stats":{"Line":2}},{"line":353,"address":null,"length":0,"stats":{"Line":2}},{"line":354,"address":null,"length":0,"stats":{"Line":0}},{"line":357,"address":null,"length":0,"stats":{"Line":1}},{"line":358,"address":null,"length":0,"stats":{"Line":2}},{"line":359,"address":null,"length":0,"stats":{"Line":2}},{"line":361,"address":null,"length":0,"stats":{"Line":2}},{"line":362,"address":null,"length":0,"stats":{"Line":2}},{"line":363,"address":null,"length":0,"stats":{"Line":0}},{"line":364,"address":null,"length":0,"stats":{"Line":0}},{"line":365,"address":null,"length":0,"stats":{"Line":0}},{"line":366,"address":null,"length":0,"stats":{"Line":0}},{"line":367,"address":null,"length":0,"stats":{"Line":0}},{"line":368,"address":null,"length":0,"stats":{"Line":0}},{"line":369,"address":null,"length":0,"stats":{"Line":0}},{"line":370,"address":null,"length":0,"stats":{"Line":0}},{"line":372,"address":null,"length":0,"stats":{"Line":0}},{"line":374,"address":null,"length":0,"stats":{"Line":1}},{"line":375,"address":null,"length":0,"stats":{"Line":2}},{"line":376,"address":null,"length":0,"stats":{"Line":2}},{"line":378,"address":null,"length":0,"stats":{"Line":2}},{"line":379,"address":null,"length":0,"stats":{"Line":2}},{"line":381,"address":null,"length":0,"stats":{"Line":2}},{"line":383,"address":null,"length":0,"stats":{"Line":0}},{"line":384,"address":null,"length":0,"stats":{"Line":0}},{"line":388,"address":null,"length":0,"stats":{"Line":2}},{"line":389,"address":null,"length":0,"stats":{"Line":2}},{"line":390,"address":null,"length":0,"stats":{"Line":2}},{"line":391,"address":null,"length":0,"stats":{"Line":2}},{"line":392,"address":null,"length":0,"stats":{"Line":2}},{"line":393,"address":null,"length":0,"stats":{"Line":2}},{"line":394,"address":null,"length":0,"stats":{"Line":2}},{"line":395,"address":null,"length":0,"stats":{"Line":2}},{"line":396,"address":null,"length":0,"stats":{"Line":2}},{"line":397,"address":null,"length":0,"stats":{"Line":2}},{"line":398,"address":null,"length":0,"stats":{"Line":2}},{"line":399,"address":null,"length":0,"stats":{"Line":2}},{"line":400,"address":null,"length":0,"stats":{"Line":2}},{"line":401,"address":null,"length":0,"stats":{"Line":2}},{"line":402,"address":null,"length":0,"stats":{"Line":1}},{"line":403,"address":null,"length":0,"stats":{"Line":0}},{"line":404,"address":null,"length":0,"stats":{"Line":1}},{"line":405,"address":null,"length":0,"stats":{"Line":0}},{"line":406,"address":null,"length":0,"stats":{"Line":0}},{"line":407,"address":null,"length":0,"stats":{"Line":1}},{"line":408,"address":null,"length":0,"stats":{"Line":1}},{"line":409,"address":null,"length":0,"stats":{"Line":0}},{"line":410,"address":null,"length":0,"stats":{"Line":0}},{"line":413,"address":null,"length":0,"stats":{"Line":2}},{"line":414,"address":null,"length":0,"stats":{"Line":2}},{"line":415,"address":null,"length":0,"stats":{"Line":1}},{"line":416,"address":null,"length":0,"stats":{"Line":1}},{"line":417,"address":null,"length":0,"stats":{"Line":1}},{"line":418,"address":5291162,"length":1,"stats":{"Line":0}},{"line":421,"address":null,"length":0,"stats":{"Line":2}},{"line":422,"address":null,"length":0,"stats":{"Line":2}},{"line":425,"address":null,"length":0,"stats":{"Line":2}},{"line":426,"address":null,"length":0,"stats":{"Line":2}},{"line":427,"address":null,"length":0,"stats":{"Line":2}},{"line":428,"address":null,"length":0,"stats":{"Line":2}},{"line":429,"address":null,"length":0,"stats":{"Line":2}},{"line":430,"address":null,"length":0,"stats":{"Line":2}},{"line":431,"address":null,"length":0,"stats":{"Line":2}},{"line":432,"address":null,"length":0,"stats":{"Line":2}},{"line":433,"address":null,"length":0,"stats":{"Line":2}},{"line":434,"address":null,"length":0,"stats":{"Line":2}},{"line":438,"address":null,"length":0,"stats":{"Line":2}},{"line":439,"address":null,"length":0,"stats":{"Line":2}},{"line":440,"address":null,"length":0,"stats":{"Line":2}},{"line":441,"address":null,"length":0,"stats":{"Line":2}},{"line":456,"address":null,"length":0,"stats":{"Line":0}},{"line":457,"address":null,"length":0,"stats":{"Line":0}},{"line":458,"address":null,"length":0,"stats":{"Line":0}},{"line":459,"address":null,"length":0,"stats":{"Line":0}},{"line":466,"address":null,"length":0,"stats":{"Line":0}},{"line":467,"address":null,"length":0,"stats":{"Line":0}},{"line":468,"address":null,"length":0,"stats":{"Line":0}},{"line":469,"address":null,"length":0,"stats":{"Line":0}},{"line":472,"address":null,"length":0,"stats":{"Line":0}},{"line":473,"address":null,"length":0,"stats":{"Line":0}},{"line":479,"address":null,"length":0,"stats":{"Line":0}},{"line":480,"address":null,"length":0,"stats":{"Line":0}},{"line":481,"address":null,"length":0,"stats":{"Line":0}},{"line":486,"address":null,"length":0,"stats":{"Line":0}},{"line":487,"address":null,"length":0,"stats":{"Line":0}},{"line":488,"address":null,"length":0,"stats":{"Line":0}},{"line":493,"address":null,"length":0,"stats":{"Line":0}},{"line":494,"address":null,"length":0,"stats":{"Line":0}},{"line":495,"address":null,"length":0,"stats":{"Line":0}},{"line":496,"address":null,"length":0,"stats":{"Line":0}},{"line":500,"address":null,"length":0,"stats":{"Line":0}},{"line":501,"address":null,"length":0,"stats":{"Line":0}},{"line":502,"address":null,"length":0,"stats":{"Line":0}},{"line":503,"address":null,"length":0,"stats":{"Line":0}},{"line":507,"address":null,"length":0,"stats":{"Line":0}},{"line":508,"address":null,"length":0,"stats":{"Line":0}},{"line":509,"address":null,"length":0,"stats":{"Line":0}},{"line":514,"address":null,"length":0,"stats":{"Line":0}},{"line":515,"address":null,"length":0,"stats":{"Line":0}},{"line":516,"address":null,"length":0,"stats":{"Line":0}},{"line":521,"address":null,"length":0,"stats":{"Line":0}},{"line":522,"address":null,"length":0,"stats":{"Line":0}},{"line":523,"address":null,"length":0,"stats":{"Line":0}},{"line":528,"address":null,"length":0,"stats":{"Line":1}},{"line":529,"address":null,"length":0,"stats":{"Line":0}},{"line":530,"address":null,"length":0,"stats":{"Line":1}},{"line":531,"address":null,"length":0,"stats":{"Line":0}},{"line":532,"address":null,"length":0,"stats":{"Line":0}},{"line":533,"address":null,"length":0,"stats":{"Line":0}},{"line":535,"address":null,"length":0,"stats":{"Line":0}},{"line":536,"address":null,"length":0,"stats":{"Line":0}},{"line":537,"address":null,"length":0,"stats":{"Line":0}},{"line":539,"address":null,"length":0,"stats":{"Line":0}},{"line":540,"address":null,"length":0,"stats":{"Line":0}},{"line":541,"address":null,"length":0,"stats":{"Line":0}},{"line":543,"address":null,"length":0,"stats":{"Line":0}},{"line":544,"address":null,"length":0,"stats":{"Line":0}},{"line":546,"address":null,"length":0,"stats":{"Line":0}},{"line":548,"address":null,"length":0,"stats":{"Line":0}},{"line":549,"address":null,"length":0,"stats":{"Line":0}},{"line":552,"address":null,"length":0,"stats":{"Line":1}},{"line":564,"address":null,"length":0,"stats":{"Line":1}},{"line":566,"address":null,"length":0,"stats":{"Line":1}},{"line":567,"address":null,"length":0,"stats":{"Line":1}},{"line":572,"address":5387664,"length":1,"stats":{"Line":2}},{"line":573,"address":6024311,"length":1,"stats":{"Line":1}},{"line":575,"address":6024456,"length":1,"stats":{"Line":0}},{"line":577,"address":6024338,"length":1,"stats":{"Line":1}},{"line":580,"address":6024909,"length":1,"stats":{"Line":0}},{"line":582,"address":6024806,"length":1,"stats":{"Line":1}},{"line":585,"address":6025365,"length":1,"stats":{"Line":0}},{"line":587,"address":6025262,"length":1,"stats":{"Line":1}},{"line":592,"address":5387696,"length":1,"stats":{"Line":2}},{"line":593,"address":6025895,"length":1,"stats":{"Line":1}},{"line":595,"address":6026309,"length":1,"stats":{"Line":0}},{"line":597,"address":6026171,"length":1,"stats":{"Line":1}},{"line":598,"address":6025922,"length":1,"stats":{"Line":1}},{"line":599,"address":6025981,"length":1,"stats":{"Line":1}},{"line":603,"address":6027079,"length":1,"stats":{"Line":0}},{"line":605,"address":6026959,"length":1,"stats":{"Line":1}},{"line":606,"address":6026662,"length":1,"stats":{"Line":1}},{"line":607,"address":6026721,"length":1,"stats":{"Line":1}},{"line":611,"address":6027849,"length":1,"stats":{"Line":0}},{"line":613,"address":6027729,"length":1,"stats":{"Line":1}},{"line":614,"address":6027432,"length":1,"stats":{"Line":1}},{"line":615,"address":6027491,"length":1,"stats":{"Line":1}},{"line":621,"address":5387728,"length":1,"stats":{"Line":2}},{"line":622,"address":6028519,"length":1,"stats":{"Line":1}},{"line":623,"address":6028996,"length":1,"stats":{"Line":1}},{"line":624,"address":6029459,"length":1,"stats":{"Line":1}},{"line":625,"address":6029922,"length":1,"stats":{"Line":1}},{"line":626,"address":6030385,"length":1,"stats":{"Line":1}},{"line":627,"address":6030848,"length":1,"stats":{"Line":1}},{"line":628,"address":6031311,"length":1,"stats":{"Line":1}},{"line":629,"address":6031774,"length":1,"stats":{"Line":1}},{"line":631,"address":6032333,"length":1,"stats":{"Line":0}},{"line":632,"address":6032237,"length":1,"stats":{"Line":1}},{"line":635,"address":6032892,"length":1,"stats":{"Line":0}},{"line":636,"address":6032796,"length":1,"stats":{"Line":1}},{"line":639,"address":6033445,"length":1,"stats":{"Line":0}},{"line":640,"address":6033349,"length":1,"stats":{"Line":1}},{"line":643,"address":6033974,"length":1,"stats":{"Line":0}},{"line":644,"address":6033878,"length":1,"stats":{"Line":1}},{"line":647,"address":6034503,"length":1,"stats":{"Line":0}},{"line":648,"address":6034407,"length":1,"stats":{"Line":1}},{"line":652,"address":6022592,"length":1,"stats":{"Line":1}},{"line":653,"address":6022820,"length":1,"stats":{"Line":1}},{"line":654,"address":6022615,"length":1,"stats":{"Line":1}},{"line":655,"address":6022701,"length":1,"stats":{"Line":1}},{"line":657,"address":6023208,"length":1,"stats":{"Line":1}},{"line":659,"address":6023250,"length":1,"stats":{"Line":1}},{"line":660,"address":6023362,"length":1,"stats":{"Line":1}},{"line":661,"address":6023700,"length":1,"stats":{"Line":1}},{"line":665,"address":5387760,"length":1,"stats":{"Line":2}},{"line":666,"address":6035653,"length":1,"stats":{"Line":1}},{"line":667,"address":6036011,"length":1,"stats":{"Line":1}},{"line":668,"address":6035667,"length":1,"stats":{"Line":1}},{"line":670,"address":6035746,"length":1,"stats":{"Line":1}},{"line":671,"address":6035852,"length":1,"stats":{"Line":1}},{"line":672,"address":6035965,"length":1,"stats":{"Line":1}},{"line":673,"address":6035976,"length":1,"stats":{"Line":1}},{"line":674,"address":6035987,"length":1,"stats":{"Line":1}},{"line":676,"address":6035999,"length":1,"stats":{"Line":1}},{"line":678,"address":6036312,"length":1,"stats":{"Line":1}},{"line":682,"address":5387792,"length":1,"stats":{"Line":2}},{"line":683,"address":6036469,"length":1,"stats":{"Line":1}},{"line":684,"address":6036873,"length":1,"stats":{"Line":1}},{"line":685,"address":6036483,"length":1,"stats":{"Line":1}},{"line":687,"address":6036562,"length":1,"stats":{"Line":1}},{"line":688,"address":6036668,"length":1,"stats":{"Line":1}},{"line":689,"address":6036765,"length":1,"stats":{"Line":1}},{"line":690,"address":6036776,"length":1,"stats":{"Line":1}},{"line":691,"address":6036787,"length":1,"stats":{"Line":1}},{"line":693,"address":6036861,"length":1,"stats":{"Line":1}},{"line":695,"address":6037090,"length":1,"stats":{"Line":1}},{"line":697,"address":6037270,"length":1,"stats":{"Line":1}},{"line":698,"address":6037196,"length":1,"stats":{"Line":1}},{"line":699,"address":6037203,"length":1,"stats":{"Line":1}},{"line":704,"address":5387824,"length":1,"stats":{"Line":2}},{"line":705,"address":6037845,"length":1,"stats":{"Line":1}},{"line":706,"address":6038265,"length":1,"stats":{"Line":1}},{"line":707,"address":6037859,"length":1,"stats":{"Line":1}},{"line":709,"address":6037938,"length":1,"stats":{"Line":1}},{"line":710,"address":6038044,"length":1,"stats":{"Line":1}},{"line":711,"address":6038141,"length":1,"stats":{"Line":1}},{"line":712,"address":6038152,"length":1,"stats":{"Line":1}},{"line":713,"address":6038163,"length":1,"stats":{"Line":1}},{"line":715,"address":6038253,"length":1,"stats":{"Line":1}},{"line":717,"address":6038566,"length":1,"stats":{"Line":1}},{"line":721,"address":5387856,"length":1,"stats":{"Line":2}},{"line":722,"address":6038741,"length":1,"stats":{"Line":1}},{"line":723,"address":6039211,"length":1,"stats":{"Line":1}},{"line":724,"address":6038755,"length":1,"stats":{"Line":1}},{"line":726,"address":6038830,"length":1,"stats":{"Line":1}},{"line":727,"address":6038938,"length":1,"stats":{"Line":1}},{"line":728,"address":6039041,"length":1,"stats":{"Line":1}},{"line":729,"address":6039052,"length":1,"stats":{"Line":1}},{"line":730,"address":6039063,"length":1,"stats":{"Line":1}},{"line":732,"address":6039137,"length":1,"stats":{"Line":1}},{"line":734,"address":6039428,"length":1,"stats":{"Line":1}},{"line":736,"address":6039608,"length":1,"stats":{"Line":1}},{"line":737,"address":6039534,"length":1,"stats":{"Line":1}},{"line":738,"address":6039541,"length":1,"stats":{"Line":1}},{"line":743,"address":5387888,"length":1,"stats":{"Line":2}},{"line":744,"address":6040213,"length":1,"stats":{"Line":1}},{"line":745,"address":6040752,"length":1,"stats":{"Line":1}},{"line":746,"address":6040227,"length":1,"stats":{"Line":1}},{"line":748,"address":6040302,"length":1,"stats":{"Line":1}},{"line":749,"address":6040410,"length":1,"stats":{"Line":1}},{"line":750,"address":6040513,"length":1,"stats":{"Line":1}},{"line":751,"address":6040654,"length":1,"stats":{"Line":1}},{"line":752,"address":6040665,"length":1,"stats":{"Line":1}},{"line":754,"address":6040677,"length":1,"stats":{"Line":1}},{"line":756,"address":6041059,"length":1,"stats":{"Line":1}},{"line":760,"address":5387920,"length":1,"stats":{"Line":2}},{"line":761,"address":6041269,"length":1,"stats":{"Line":1}},{"line":762,"address":6041763,"length":1,"stats":{"Line":1}},{"line":763,"address":6041283,"length":1,"stats":{"Line":1}},{"line":765,"address":6041358,"length":1,"stats":{"Line":1}},{"line":766,"address":6041466,"length":1,"stats":{"Line":1}},{"line":767,"address":6041569,"length":1,"stats":{"Line":1}},{"line":768,"address":6041580,"length":1,"stats":{"Line":1}},{"line":769,"address":6041676,"length":1,"stats":{"Line":1}},{"line":771,"address":6041688,"length":1,"stats":{"Line":1}},{"line":773,"address":6042064,"length":1,"stats":{"Line":1}},{"line":777,"address":5387952,"length":1,"stats":{"Line":2}},{"line":778,"address":6042277,"length":1,"stats":{"Line":1}},{"line":779,"address":6042901,"length":1,"stats":{"Line":1}},{"line":780,"address":6042291,"length":1,"stats":{"Line":1}},{"line":782,"address":6042366,"length":1,"stats":{"Line":1}},{"line":783,"address":6042474,"length":1,"stats":{"Line":1}},{"line":784,"address":6042577,"length":1,"stats":{"Line":1}},{"line":785,"address":6042718,"length":1,"stats":{"Line":1}},{"line":786,"address":6042814,"length":1,"stats":{"Line":1}},{"line":788,"address":6042826,"length":1,"stats":{"Line":1}},{"line":790,"address":6043214,"length":1,"stats":{"Line":1}},{"line":794,"address":5387984,"length":1,"stats":{"Line":2}},{"line":796,"address":6043429,"length":1,"stats":{"Line":1}},{"line":797,"address":6044118,"length":1,"stats":{"Line":1}},{"line":798,"address":6043443,"length":1,"stats":{"Line":1}},{"line":800,"address":6043518,"length":1,"stats":{"Line":1}},{"line":801,"address":6043626,"length":1,"stats":{"Line":1}},{"line":802,"address":6043729,"length":1,"stats":{"Line":1}},{"line":803,"address":6043870,"length":1,"stats":{"Line":1}},{"line":804,"address":6043966,"length":1,"stats":{"Line":1}},{"line":806,"address":6044025,"length":1,"stats":{"Line":1}},{"line":808,"address":6044431,"length":1,"stats":{"Line":1}}],"covered":282,"coverable":399},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","network_head.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module defining the format of network heads and how to handle them.\n\nuse crate::network_head_v2::*;\nuse crate::network_head_v3::*;\nuse crate::{NodeFullId, NodeId};\nuse dubp_documents::blockstamp::*;\nuse dup_crypto::bases::BaseConvertionError;\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\nuse serde::{Deserialize, Serialize};\nuse serde_json;\nuse std::collections::HashMap;\nuse std::num::ParseIntError;\nuse std::ops::Deref;\nuse std::str::FromStr;\n\n#[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq, Serialize, Deserialize)]\n/// Network Head : Set of information on the current state of a node, the central information being the blockstamp of its current block (= the head of its blockchain).\npub enum NetworkHead {\n /// Head V2\n V2(Box\u003cNetworkHeadV2\u003e),\n /// head V3\n V3(Box\u003cNetworkHeadV3\u003e),\n}\n\nimpl ToString for NetworkHead {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e head_v2.deref().to_string(),\n _ =\u003e fatal_error!(\"NetworkHead version not supported !\"),\n }\n }\n}\n\n/// NetworkHeadParseErr parse error\n#[derive(Debug)]\npub enum NetworkHeadParseErr {\n /// BaseConvertionError\n BaseConvertionError(BaseConvertionError),\n /// ParseIntError\n ParseIntError(ParseIntError),\n /// BlockstampParseError\n BlockstampParseError(BlockstampParseError),\n /// NetworkHeadMessageParseErr\n NetworkHeadMessageParseErr(NetworkHeadMessageParseErr),\n /// InvalidMessageVersion\n InvalidMessageVersion(),\n /// InvalidStep\n InvalidStep(),\n /// InvalidStr\n InvalidStr(\u0026'static str),\n /// MissingField\n MissingField(\u0026'static str),\n}\n\nimpl From\u003cNetworkHeadMessageParseErr\u003e for NetworkHeadParseErr {\n fn from(e: NetworkHeadMessageParseErr) -\u003e Self {\n NetworkHeadParseErr::NetworkHeadMessageParseErr(e)\n }\n}\n\nimpl From\u003cBaseConvertionError\u003e for NetworkHeadParseErr {\n fn from(e: BaseConvertionError) -\u003e Self {\n NetworkHeadParseErr::BaseConvertionError(e)\n }\n}\n\nimpl From\u003cBlockstampParseError\u003e for NetworkHeadParseErr {\n fn from(e: BlockstampParseError) -\u003e Self {\n NetworkHeadParseErr::BlockstampParseError(e)\n }\n}\n\nimpl From\u003cParseIntError\u003e for NetworkHeadParseErr {\n fn from(e: ParseIntError) -\u003e Self {\n NetworkHeadParseErr::ParseIntError(e)\n }\n}\n\nimpl NetworkHead {\n /// Get HEAD version\n pub fn version(\u0026self) -\u003e u32 {\n match *self {\n NetworkHead::V2(_) =\u003e 2,\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Get HEAD blockstamp\n pub fn blockstamp(\u0026self) -\u003e Blockstamp {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e head_v2.message_v2.blockstamp(),\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Get pubkey of head issuer\n pub fn pubkey(\u0026self) -\u003e PubKey {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e match head_v2.message_v2 {\n NetworkHeadMessage::V2(ref head_message_v2) =\u003e head_message_v2.pubkey,\n _ =\u003e fatal_error!(\"This HEAD message version is not supported !\"),\n },\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Get uid of head issuer\n pub fn uid(\u0026self) -\u003e Option\u003cString\u003e {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e head_v2.uid(),\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Change uid of head issuer\n pub fn set_uid(\u0026mut self, uid: \u0026str) {\n match *self {\n NetworkHead::V2(ref mut head_v2) =\u003e head_v2.uid = Some(String::from(uid)),\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// return the HEAD Step\n pub fn step(\u0026self) -\u003e u32 {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e head_v2.step,\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Checks the validity of all head signatures\n pub fn verify(\u0026self) -\u003e bool {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e {\n self.pubkey()\n .verify(head_v2.message.to_string().as_bytes(), \u0026head_v2.sig)\n .is_ok()\n \u0026\u0026 self\n .pubkey()\n .verify(head_v2.message_v2.to_string().as_bytes(), \u0026head_v2.sig_v2)\n .is_ok()\n }\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Returns issuer node id\n pub fn node_uuid(\u0026self) -\u003e NodeId {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e head_v2.message_v2.node_uuid(),\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Returns issuer node full identifier\n pub fn node_full_id(\u0026self) -\u003e NodeFullId {\n NodeFullId(self.node_uuid(), self.pubkey())\n }\n /// Returns true only if this head is to replace the old head of the same issuer in the head cache (or if it's the 1st head of this issuer)\n pub fn apply(\u0026self, heads_cache: \u0026mut HashMap\u003cNodeFullId, NetworkHead\u003e) -\u003e bool {\n let heads_cache_copy = heads_cache.clone();\n if let Some(head) = heads_cache_copy.get(\u0026self.node_full_id()) {\n if self.blockstamp().id.0 \u003e head.blockstamp().id.0\n || (self.blockstamp().id.0 == head.blockstamp().id.0\n \u0026\u0026 self.version() \u003e= head.version()\n \u0026\u0026 self.step() \u003c head.step())\n {\n if let Some(head_mut) = heads_cache.get_mut(\u0026self.node_full_id()) {\n *head_mut = self.clone();\n true\n } else {\n false\n }\n } else {\n false\n }\n } else {\n heads_cache.insert(self.node_full_id(), self.clone());\n true\n }\n }\n /// Parse Json Head\n pub fn from_json_value(source: \u0026serde_json::Value) -\u003e Result\u003cNetworkHead, NetworkHeadParseErr\u003e {\n let message = NetworkHeadMessage::from_str(if let Some(str_msg) = source.get(\"message\") {\n if let Some(str_msg) = str_msg.as_str() {\n str_msg\n } else {\n return Err(NetworkHeadParseErr::InvalidStr(\"message\"));\n }\n } else {\n return Err(NetworkHeadParseErr::MissingField(\"message\"));\n })?;\n match message {\n NetworkHeadMessage::V2(_) =\u003e Ok(NetworkHead::V2(Box::new(NetworkHeadV2 {\n message,\n sig: Sig::Ed25519(ed25519::Signature::from_base64(\n if let Some(str_sig) = source.get(\"sig\") {\n if let Some(str_sig) = str_sig.as_str() {\n str_sig\n } else {\n return Err(NetworkHeadParseErr::InvalidStr(\"sig\"));\n }\n } else {\n return Err(NetworkHeadParseErr::MissingField(\"sigV2\"));\n },\n )?),\n message_v2: NetworkHeadMessage::from_str(\n if let Some(str_msg) = source.get(\"messageV2\") {\n if let Some(str_msg) = str_msg.as_str() {\n str_msg\n } else {\n return Err(NetworkHeadParseErr::InvalidStr(\"messageV2\"));\n }\n } else {\n return Err(NetworkHeadParseErr::MissingField(\"messageV2\"));\n },\n )?,\n sig_v2: Sig::Ed25519(ed25519::Signature::from_base64(\n if let Some(str_sig) = source.get(\"sigV2\") {\n if let Some(str_sig) = str_sig.as_str() {\n str_sig\n } else {\n return Err(NetworkHeadParseErr::InvalidStr(\"sigV2\"));\n }\n } else {\n return Err(NetworkHeadParseErr::MissingField(\"sigV2\"));\n },\n )?),\n step: if let Some(step) = source.get(\"step\") {\n if let Some(step) = step.as_u64() {\n step as u32\n } else {\n return Err(NetworkHeadParseErr::InvalidStep());\n }\n } else {\n return Err(NetworkHeadParseErr::MissingField(\"step\"));\n },\n uid: None,\n }))),\n _ =\u003e Err(NetworkHeadParseErr::InvalidMessageVersion()),\n }\n }\n /// To human readable string\n pub fn to_human_string(\u0026self, max_len: usize) -\u003e String {\n match *self {\n NetworkHead::V2(ref head_v2) =\u003e head_v2.deref().to_human_string(max_len),\n _ =\u003e fatal_error!(\"NetworkHead version not supported !\"),\n }\n }\n}\n","traces":[{"line":42,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":1}},{"line":114,"address":null,"length":0,"stats":{"Line":1}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":0}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":142,"address":null,"length":0,"stats":{"Line":1}},{"line":143,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":0}},{"line":148,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":0}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":0}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":169,"address":null,"length":0,"stats":{"Line":0}},{"line":170,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":176,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":0}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":182,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":1}},{"line":192,"address":null,"length":0,"stats":{"Line":1}},{"line":193,"address":null,"length":0,"stats":{"Line":1}},{"line":194,"address":null,"length":0,"stats":{"Line":1}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":198,"address":null,"length":0,"stats":{"Line":0}},{"line":199,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":1}},{"line":202,"address":null,"length":0,"stats":{"Line":1}},{"line":203,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":206,"address":null,"length":0,"stats":{"Line":1}},{"line":207,"address":null,"length":0,"stats":{"Line":1}},{"line":208,"address":null,"length":0,"stats":{"Line":0}},{"line":209,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":1}},{"line":216,"address":null,"length":0,"stats":{"Line":1}},{"line":217,"address":null,"length":0,"stats":{"Line":1}},{"line":218,"address":null,"length":0,"stats":{"Line":1}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":223,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":1}},{"line":227,"address":null,"length":0,"stats":{"Line":1}},{"line":228,"address":null,"length":0,"stats":{"Line":1}},{"line":229,"address":null,"length":0,"stats":{"Line":1}},{"line":230,"address":null,"length":0,"stats":{"Line":0}},{"line":231,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":0}},{"line":234,"address":null,"length":0,"stats":{"Line":0}},{"line":237,"address":null,"length":0,"stats":{"Line":1}},{"line":238,"address":null,"length":0,"stats":{"Line":1}},{"line":239,"address":null,"length":0,"stats":{"Line":1}},{"line":240,"address":null,"length":0,"stats":{"Line":0}},{"line":241,"address":null,"length":0,"stats":{"Line":0}},{"line":243,"address":null,"length":0,"stats":{"Line":0}},{"line":244,"address":null,"length":0,"stats":{"Line":0}},{"line":246,"address":null,"length":0,"stats":{"Line":1}},{"line":248,"address":null,"length":0,"stats":{"Line":0}},{"line":252,"address":null,"length":0,"stats":{"Line":0}},{"line":253,"address":null,"length":0,"stats":{"Line":0}},{"line":254,"address":null,"length":0,"stats":{"Line":0}},{"line":255,"address":null,"length":0,"stats":{"Line":0}}],"covered":32,"coverable":120},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","network_head_v2.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module defining the format of network heads v2 and how to handle them.\n\nuse crate::NodeId;\nuse dubp_documents::blockstamp::*;\nuse dup_crypto::bases::BaseConvertionError;\nuse dup_crypto::keys::*;\nuse durs_common_tools::fatal_error;\nuse serde::{Deserialize, Serialize};\nuse std::cmp::Ordering;\nuse std::num::ParseIntError;\nuse std::ops::Deref;\nuse std::str::FromStr;\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Head Message V2\npub struct NetworkHeadMessageV2 {\n /// API details\n pub api: String,\n /// Head version\n pub version: usize,\n /// Head pubkey\n pub pubkey: PubKey,\n /// Head blockstamp\n pub blockstamp: Blockstamp,\n /// Head node id\n pub node_uuid: NodeId,\n /// Issuer node software\n pub software: String,\n /// Issuer node soft version\n pub soft_version: String,\n /// Issuer node prefix\n pub prefix: usize,\n /// Issuer node free member room\n pub free_member_room: Option\u003cusize\u003e,\n /// Issuer node free mirror room\n pub free_mirror_room: Option\u003cusize\u003e,\n}\n\nimpl PartialOrd for NetworkHeadMessageV2 {\n fn partial_cmp(\u0026self, other: \u0026NetworkHeadMessageV2) -\u003e Option\u003cOrdering\u003e {\n Some(self.cmp(other))\n }\n}\n\nimpl Ord for NetworkHeadMessageV2 {\n fn cmp(\u0026self, other: \u0026NetworkHeadMessageV2) -\u003e Ordering {\n self.blockstamp.cmp(\u0026other.blockstamp)\n }\n}\n\nimpl NetworkHeadMessageV2 {\n /// To human readable string\n pub fn to_human_string(\u0026self, max_len: usize, uid: Option\u003cString\u003e) -\u003e String {\n let short_api = \u0026self.api[4..];\n\n if max_len \u003e 85 \u0026\u0026 uid.is_some() {\n format!(\n \"{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:14} {pre:3} [{api:5}] {mer:02}:{mir:02} {uid}\",\n node_id = self.node_uuid.to_string(),\n pubkey = self.pubkey.to_string(),\n blockstamp = self.blockstamp.to_string(),\n soft = self.software,\n ver = self.soft_version,\n pre = self.prefix,\n api = short_api,\n mer = self.free_member_room.unwrap_or(0),\n mir = self.free_mirror_room.unwrap_or(0),\n uid = uid.unwrap(),\n )\n } else if max_len \u003e 75 {\n format!(\n \"{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:14} {pre:3} [{api:5}] {mer:02}:{mir:02}\",\n node_id = self.node_uuid.to_string(),\n pubkey = self.pubkey.to_string(),\n blockstamp = self.blockstamp.to_string(),\n soft = self.software,\n ver = self.soft_version,\n pre = self.prefix,\n api = short_api,\n mer = self.free_member_room.unwrap_or(0),\n mir = self.free_mirror_room.unwrap_or(0),\n )\n } else if max_len \u003e 70 {\n format!(\n \"{node_id:8}-{pubkey:.8} {blockstamp:.16} {soft:7}:{ver:14} [{api:5}] {mer:02}:{mir:02}\",\n node_id = self.node_uuid.to_string(),\n pubkey = self.pubkey.to_string(),\n blockstamp = self.blockstamp.to_string(),\n soft = self.software,\n ver = self.soft_version,\n api = short_api,\n mer = self.free_member_room.unwrap_or(0),\n mir = self.free_mirror_room.unwrap_or(0),\n )\n } else if max_len \u003e 47 {\n format!(\n \"{node_id:8}-{pubkey:.8} {blockstamp:.16} [{api:5}] {mer:02}:{mir:02}\",\n node_id = self.node_uuid.to_string(),\n pubkey = self.pubkey.to_string(),\n blockstamp = self.blockstamp.to_string(),\n api = short_api,\n mer = self.free_member_room.unwrap_or(0),\n mir = self.free_mirror_room.unwrap_or(0),\n )\n } else if max_len \u003e 41 {\n format!(\n \"{node_id:8}-{pubkey:.8} {blockstamp:.16} [{api:5}]\",\n node_id = self.node_uuid.to_string(),\n pubkey = self.pubkey.to_string(),\n blockstamp = self.blockstamp.to_string(),\n api = short_api,\n )\n } else {\n String::from(\"Term width insufficient\")\n }\n }\n}\n\n#[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq, Hash, Serialize, Deserialize)]\n/// Head Message\npub enum NetworkHeadMessage {\n /// Head Message V2\n V2(NetworkHeadMessageV2),\n /// Do not use\n Other(),\n}\n\n/// NetworkHeadMessage parse error\n#[derive(Debug)]\npub enum NetworkHeadMessageParseErr {\n /// BaseConvertionError\n BaseConvertionError(BaseConvertionError),\n /// ParseIntError\n ParseIntError(ParseIntError),\n /// BlockstampParseError\n BlockstampParseError(BlockstampParseError),\n}\n\nimpl From\u003cBaseConvertionError\u003e for NetworkHeadMessageParseErr {\n fn from(e: BaseConvertionError) -\u003e Self {\n NetworkHeadMessageParseErr::BaseConvertionError(e)\n }\n}\n\nimpl From\u003cBlockstampParseError\u003e for NetworkHeadMessageParseErr {\n fn from(e: BlockstampParseError) -\u003e Self {\n NetworkHeadMessageParseErr::BlockstampParseError(e)\n }\n}\n\nimpl From\u003cParseIntError\u003e for NetworkHeadMessageParseErr {\n fn from(e: ParseIntError) -\u003e Self {\n NetworkHeadMessageParseErr::ParseIntError(e)\n }\n}\n\nimpl FromStr for NetworkHeadMessage {\n type Err = NetworkHeadMessageParseErr;\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n let source_array: Vec\u003c\u0026str\u003e = source.split(':').collect();\n Ok(NetworkHeadMessage::V2(NetworkHeadMessageV2 {\n api: source_array[0].to_string(),\n version: source_array[2].parse()?,\n pubkey: PubKey::Ed25519(ed25519::PublicKey::from_base58(\n \u0026source_array[3].to_string(),\n )?),\n blockstamp: Blockstamp::from_string(source_array[4])?,\n node_uuid: NodeId(u32::from_str_radix(source_array[5], 16)?),\n software: source_array[6].to_string(),\n soft_version: source_array[7].to_string(),\n prefix: source_array[8].parse()?,\n free_member_room: if let Some(field) = source_array.get(9) {\n Some(field.parse()?)\n } else {\n None\n },\n free_mirror_room: if let Some(field) = source_array.get(10) {\n Some(field.parse()?)\n } else {\n None\n },\n }))\n }\n}\n\nimpl NetworkHeadMessage {\n /// To human readable string\n pub fn to_human_string(\u0026self, max_len: usize, uid: Option\u003cString\u003e) -\u003e String {\n match *self {\n NetworkHeadMessage::V2(ref mess_v2) =\u003e mess_v2.deref().to_human_string(max_len, uid),\n _ =\u003e fatal_error!(\"NetworkHead version not supported !\"),\n }\n }\n /// Get head blockcstamp\n pub fn blockstamp(\u0026self) -\u003e Blockstamp {\n match *self {\n NetworkHeadMessage::V2(ref head_message_v2) =\u003e head_message_v2.blockstamp,\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Get head node id\n pub fn node_uuid(\u0026self) -\u003e NodeId {\n match *self {\n NetworkHeadMessage::V2(ref head_message_v2) =\u003e head_message_v2.node_uuid,\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n /// Get head issuer public key\n fn _pubkey(\u0026self) -\u003e PubKey {\n match *self {\n NetworkHeadMessage::V2(ref head_message_v2) =\u003e head_message_v2.pubkey,\n _ =\u003e fatal_error!(\"This HEAD version is not supported !\"),\n }\n }\n}\n\nimpl ToString for NetworkHeadMessageV2 {\n fn to_string(\u0026self) -\u003e String {\n match self.version {\n 1 =\u003e format!(\n \"{}:HEAD:1:{}:{}:{}:{}:{}:{}\",\n self.api,\n self.pubkey,\n self.blockstamp,\n self.node_uuid,\n self.software,\n self.soft_version,\n self.prefix\n ),\n 2 =\u003e format!(\n \"{}:HEAD:2:{}:{}:{}:{}:{}:{}:{}:{}\",\n self.api,\n self.pubkey,\n self.blockstamp,\n self.node_uuid,\n self.software,\n self.soft_version,\n self.prefix,\n self.free_member_room.unwrap(),\n self.free_mirror_room.unwrap()\n ),\n _ =\u003e fatal_error!(\"NetworkHeadMessage is wrongly parsed !\"),\n }\n }\n}\n\nimpl ToString for NetworkHeadMessage {\n fn to_string(\u0026self) -\u003e String {\n match *self {\n NetworkHeadMessage::V2(ref head_message) =\u003e head_message.to_string(),\n _ =\u003e fatal_error!(\"This HEADMessage version is not supported !\"),\n }\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\n/// Head V2\npub struct NetworkHeadV2 {\n /// Head V1 Message\n pub message: NetworkHeadMessage,\n /// signature of V1 Message\n pub sig: Sig,\n /// Head V2 Message\n pub message_v2: NetworkHeadMessage,\n /// signature of V2 Message\n pub sig_v2: Sig,\n /// Head step\n pub step: u32,\n /// Head issuer uid\n pub uid: Option\u003cString\u003e,\n}\n\nimpl ToString for NetworkHeadV2 {\n fn to_string(\u0026self) -\u003e String {\n self.message_v2.to_string()\n }\n}\n\nimpl PartialOrd for NetworkHeadV2 {\n fn partial_cmp(\u0026self, other: \u0026NetworkHeadV2) -\u003e Option\u003cOrdering\u003e {\n Some(self.cmp(other))\n }\n}\n\nimpl Ord for NetworkHeadV2 {\n fn cmp(\u0026self, other: \u0026NetworkHeadV2) -\u003e Ordering {\n self.message.cmp(\u0026other.message)\n }\n}\n\nimpl NetworkHeadV2 {\n /// To human readable string\n pub fn to_human_string(\u0026self, max_len: usize) -\u003e String {\n if max_len \u003e 2 {\n format!(\n \"{} {}\",\n self.step,\n self.message_v2\n .to_human_string(max_len - 2, self.uid.clone())\n )\n } else {\n String::from(\".\")\n }\n }\n /// Get uid of head issuer\n pub fn uid(\u0026self) -\u003e Option\u003cString\u003e {\n self.uid.clone()\n }\n}\n","traces":[{"line":55,"address":null,"length":0,"stats":{"Line":0}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":0}},{"line":72,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":0}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":0}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":85,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":0}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":0}},{"line":102,"address":null,"length":0,"stats":{"Line":0}},{"line":103,"address":null,"length":0,"stats":{"Line":0}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":0}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":0}},{"line":110,"address":null,"length":0,"stats":{"Line":0}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":113,"address":null,"length":0,"stats":{"Line":0}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":0}},{"line":116,"address":null,"length":0,"stats":{"Line":0}},{"line":117,"address":null,"length":0,"stats":{"Line":0}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":167,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":175,"address":null,"length":0,"stats":{"Line":1}},{"line":176,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":188,"address":null,"length":0,"stats":{"Line":1}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":1}},{"line":192,"address":null,"length":0,"stats":{"Line":1}},{"line":193,"address":null,"length":0,"stats":{"Line":1}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":1}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":205,"address":null,"length":0,"stats":{"Line":0}},{"line":206,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":227,"address":null,"length":0,"stats":{"Line":0}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":234,"address":null,"length":0,"stats":{"Line":1}},{"line":235,"address":null,"length":0,"stats":{"Line":1}},{"line":237,"address":null,"length":0,"stats":{"Line":1}},{"line":238,"address":null,"length":0,"stats":{"Line":1}},{"line":239,"address":null,"length":0,"stats":{"Line":1}},{"line":240,"address":null,"length":0,"stats":{"Line":1}},{"line":241,"address":null,"length":0,"stats":{"Line":1}},{"line":242,"address":null,"length":0,"stats":{"Line":1}},{"line":243,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":1}},{"line":247,"address":null,"length":0,"stats":{"Line":1}},{"line":248,"address":null,"length":0,"stats":{"Line":1}},{"line":249,"address":null,"length":0,"stats":{"Line":1}},{"line":250,"address":null,"length":0,"stats":{"Line":1}},{"line":251,"address":null,"length":0,"stats":{"Line":1}},{"line":252,"address":null,"length":0,"stats":{"Line":1}},{"line":253,"address":null,"length":0,"stats":{"Line":1}},{"line":254,"address":null,"length":0,"stats":{"Line":1}},{"line":255,"address":null,"length":0,"stats":{"Line":1}},{"line":257,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":1}},{"line":264,"address":null,"length":0,"stats":{"Line":0}},{"line":265,"address":null,"length":0,"stats":{"Line":1}},{"line":266,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":null,"length":0,"stats":{"Line":0}},{"line":290,"address":null,"length":0,"stats":{"Line":0}},{"line":295,"address":null,"length":0,"stats":{"Line":0}},{"line":296,"address":null,"length":0,"stats":{"Line":0}},{"line":301,"address":null,"length":0,"stats":{"Line":0}},{"line":302,"address":null,"length":0,"stats":{"Line":0}},{"line":308,"address":null,"length":0,"stats":{"Line":0}},{"line":309,"address":null,"length":0,"stats":{"Line":0}},{"line":310,"address":null,"length":0,"stats":{"Line":0}},{"line":312,"address":null,"length":0,"stats":{"Line":0}},{"line":313,"address":null,"length":0,"stats":{"Line":0}},{"line":314,"address":null,"length":0,"stats":{"Line":0}},{"line":316,"address":null,"length":0,"stats":{"Line":0}},{"line":321,"address":null,"length":0,"stats":{"Line":0}},{"line":322,"address":null,"length":0,"stats":{"Line":0}}],"covered":40,"coverable":136},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","network_head_v3.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module defining the format of network heads v3 and how to handle them.\n\nuse crate::network_head::NetworkHead;\nuse crate::*;\nuse base58::ToBase58;\nuse dubp_documents::blockstamp::Blockstamp;\nuse dubp_documents::{BlockHash, BlockNumber, ToStringObject};\nuse dup_crypto::keys::text_signable::TextSignable;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\nuse pest::iterators::Pair;\nuse pest::Parser;\nuse std::cmp::Ordering;\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Head V3\npub struct NetworkHeadV3 {\n /// Currency name\n pub currency_name: CurrencyName,\n /// WS2P Private configuration\n pub api_outgoing_conf: u8,\n /// WS2P Public configuration\n pub api_incoming_conf: u8,\n /// Issuer node free member rooms\n pub free_member_rooms: u8,\n /// Issuer node free mirror rooms\n pub free_mirror_rooms: u8,\n /// Issuer node id\n pub node_id: NodeId,\n /// Issuer pubkey\n pub pubkey: PubKey,\n /// Head blockstamp\n pub blockstamp: Blockstamp,\n /// Issuer node software\n pub software: String,\n /// Issuer node soft version\n pub soft_version: String,\n /// Issuer signature\n pub signature: Option\u003cSig\u003e,\n /// Head step\n pub step: u8,\n}\n\nimpl NetworkHeadV3 {\n /// From pest parser pair\n pub fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cNetworkHeadV3, TextDocumentParseError\u003e {\n let mut currency_str = \"\";\n let mut api_outgoing_conf = 0;\n let mut api_incoming_conf = 0;\n let mut free_member_rooms = 0;\n let mut free_mirror_rooms = 0;\n let mut node_id = NodeId(0);\n let mut pubkey = None;\n let mut blockstamp = None;\n let mut software = \"\";\n let mut soft_version = \"\";\n let mut signature = None;\n let mut step = 0;\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency_str = field.as_str(),\n Rule::api_outgoing_conf =\u003e api_outgoing_conf = field.as_str().parse().unwrap(),\n Rule::api_incoming_conf =\u003e api_incoming_conf = field.as_str().parse().unwrap(),\n Rule::free_member_rooms =\u003e free_member_rooms = field.as_str().parse().unwrap(),\n Rule::free_mirror_rooms =\u003e free_mirror_rooms = field.as_str().parse().unwrap(),\n Rule::node_id =\u003e node_id = NodeId(field.as_str().parse().unwrap()),\n Rule::pubkey =\u003e {\n pubkey = Some(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(field.as_str()).unwrap(),\n ))\n }\n Rule::blockstamp =\u003e {\n let mut inner_rules = field.into_inner(); // { block_id ~ \"-\" ~ hash }\n\n let block_id: \u0026str = inner_rules.next().unwrap().as_str();\n let block_hash: \u0026str = inner_rules.next().unwrap().as_str();\n blockstamp = Some(Blockstamp {\n id: BlockNumber(block_id.parse().unwrap()), // Grammar ensures that we have a digits string.\n hash: BlockHash(Hash::from_hex(block_hash).unwrap()), // Grammar ensures that we have an hexadecimal string.\n });\n }\n Rule::software =\u003e software = field.as_str(),\n Rule::soft_version =\u003e soft_version = field.as_str(),\n Rule::ed25519_sig =\u003e {\n signature = Some(Sig::Ed25519(\n ed25519::Signature::from_base64(field.as_str()).unwrap(),\n ))\n }\n Rule::step =\u003e step = field.as_str().parse().unwrap(),\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", field.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n\n Ok(NetworkHeadV3 {\n currency_name: CurrencyName(currency_str.to_owned()),\n api_outgoing_conf,\n api_incoming_conf,\n free_member_rooms,\n free_mirror_rooms,\n node_id,\n pubkey: pubkey.expect(\"Grammar must ensure that head v3 have valid issuer pubkey !\"),\n blockstamp: blockstamp\n .expect(\"Grammar must ensure that head v3 have valid blockstamp!\"),\n software: software.to_owned(),\n soft_version: soft_version.to_owned(),\n signature,\n step,\n })\n }\n}\n\nimpl PartialOrd for NetworkHeadV3 {\n fn partial_cmp(\u0026self, other: \u0026NetworkHeadV3) -\u003e Option\u003cOrdering\u003e {\n Some(self.cmp(other))\n }\n}\n\nimpl Ord for NetworkHeadV3 {\n fn cmp(\u0026self, other: \u0026NetworkHeadV3) -\u003e Ordering {\n self.blockstamp.cmp(\u0026other.blockstamp)\n }\n}\n\n/*impl\u003c'de\u003e BinSignable\u003c'de\u003e for NetworkHeadV3 {\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n self.pubkey\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n self.signature\n }\n fn set_signature(\u0026mut self, signature: Sig) {\n self.signature = Some(signature);\n }\n}*/\n\nimpl TextSignable for NetworkHeadV3 {\n fn as_signable_text(\u0026self) -\u003e String {\n format!(\n\"3:{currency}:{api_outgoing_conf}:{api_incoming_conf}:{free_member_rooms}:{free_mirror_rooms}:{node_id}:{pubkey}:{blockstamp}:{software}:{soft_version}\\n\",\n currency = self.currency_name,\n api_outgoing_conf = self.api_outgoing_conf,\n api_incoming_conf = self.api_incoming_conf,\n free_member_rooms = self.free_member_rooms,\n free_mirror_rooms = self.free_mirror_rooms,\n node_id = format!(\"{}\", self.node_id),\n pubkey = self.pubkey.to_base58(),\n blockstamp = self.blockstamp.to_string(),\n software = self.software,\n soft_version = self.soft_version,\n )\n }\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n self.pubkey\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n self.signature\n }\n fn set_signature(\u0026mut self, signature: Sig) {\n self.signature = Some(signature);\n }\n}\n\nimpl TextDocumentParser\u003cRule\u003e for NetworkHead {\n /// Type of document generated by the parser\n type DocumentType = NetworkHead;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cNetworkHead, TextDocumentParseError\u003e {\n let mut head_v3_pairs = NetworkDocsParser::parse(Rule::head_v3, doc)?;\n Ok(Self::from_versioned_pest_pair(\n 3,\n head_v3_pairs.next().unwrap(),\n )?)\n }\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n Self::from_versioned_pest_pair(3, pair)\n }\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cNetworkHead, TextDocumentParseError\u003e {\n match version {\n 3 =\u003e Ok(NetworkHead::V3(Box::new(NetworkHeadV3::from_pest_pair(\n pair,\n )?))),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Head V3 for json serializer\npub struct HeadV3Stringified {\n /// Head body\n pub body: String,\n /// Issuer signature\n pub signature: Option\u003cString\u003e,\n /// Head step\n pub step: u8,\n}\n\nimpl\u003c'a\u003e ToStringObject for NetworkHeadV3 {\n type StringObject = HeadV3Stringified;\n\n fn to_string_object(\u0026self) -\u003e Self::StringObject {\n let body = self.as_signable_text();\n let body_len = body.len();\n HeadV3Stringified {\n body: body.chars().take(body_len - 1).collect(),\n signature: if let Some(sig) = self.signature {\n Some(sig.to_base64())\n } else {\n None\n },\n step: self.step,\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::tests::keypair1;\n\n #[test]\n fn head_v3_sign_and_verify() {\n let keypair = keypair1();\n let mut head_v3 = NetworkHeadV3 {\n currency_name: CurrencyName(\"g1\".to_owned()),\n api_outgoing_conf: 0u8,\n api_incoming_conf: 0u8,\n free_mirror_rooms: 0u8,\n free_member_rooms: 0u8,\n node_id: NodeId(0),\n pubkey: PubKey::Ed25519(keypair.public_key()),\n blockstamp: Blockstamp::from_string(\n \"50-000005B1CEB4EC5245EF7E33101A330A1C9A358EC45A25FC13F78BB58C9E7370\",\n )\n .unwrap(),\n software: String::from(\"durs\"),\n soft_version: String::from(\"0.2.0-a1\"),\n signature: None,\n step: 0,\n };\n // Sign\n let sign_result = head_v3.sign(PrivKey::Ed25519(keypair.private_key()));\n if let Ok(head_v3_raw) = sign_result {\n println!(\"{}\", head_v3_raw);\n assert_eq!(\n NetworkHead::V3(Box::new(head_v3.clone())),\n NetworkHead::parse(\u0026head_v3_raw).expect(\"Fail to parse head v3 !\")\n )\n } else {\n panic!(\"fail to sign head v3 : {:?}\", sign_result.err().unwrap())\n }\n // Verify signature\n head_v3.verify().expect(\"HEADv3 : Invalid signature !\");\n }\n}\n","traces":[{"line":61,"address":null,"length":0,"stats":{"Line":1}},{"line":62,"address":null,"length":0,"stats":{"Line":1}},{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":1}},{"line":67,"address":null,"length":0,"stats":{"Line":1}},{"line":68,"address":null,"length":0,"stats":{"Line":1}},{"line":69,"address":null,"length":0,"stats":{"Line":1}},{"line":70,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":1}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":77,"address":null,"length":0,"stats":{"Line":1}},{"line":78,"address":null,"length":0,"stats":{"Line":1}},{"line":79,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":84,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":0}},{"line":88,"address":4635619,"length":1,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":4636020,"length":1,"stats":{"Line":1}},{"line":94,"address":4636127,"length":1,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":4636828,"length":1,"stats":{"Line":0}},{"line":109,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":1}},{"line":114,"address":null,"length":0,"stats":{"Line":1}},{"line":115,"address":null,"length":0,"stats":{"Line":1}},{"line":116,"address":null,"length":0,"stats":{"Line":1}},{"line":117,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":0}},{"line":134,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":157,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":1}},{"line":160,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":167,"address":null,"length":0,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":1}},{"line":173,"address":null,"length":0,"stats":{"Line":1}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":1}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":199,"address":null,"length":0,"stats":{"Line":1}},{"line":200,"address":null,"length":0,"stats":{"Line":1}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":203,"address":null,"length":0,"stats":{"Line":0}},{"line":204,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":0}},{"line":226,"address":null,"length":0,"stats":{"Line":0}},{"line":228,"address":null,"length":0,"stats":{"Line":0}},{"line":229,"address":null,"length":0,"stats":{"Line":0}},{"line":234,"address":null,"length":0,"stats":{"Line":0}},{"line":245,"address":5228848,"length":1,"stats":{"Line":2}},{"line":246,"address":5736775,"length":1,"stats":{"Line":1}},{"line":247,"address":5737092,"length":1,"stats":{"Line":1}},{"line":248,"address":5736833,"length":1,"stats":{"Line":1}},{"line":253,"address":5736886,"length":1,"stats":{"Line":1}},{"line":254,"address":5736897,"length":1,"stats":{"Line":1}},{"line":255,"address":5736976,"length":1,"stats":{"Line":1}},{"line":259,"address":5737030,"length":1,"stats":{"Line":1}},{"line":260,"address":5737057,"length":1,"stats":{"Line":1}},{"line":261,"address":5737084,"length":1,"stats":{"Line":1}},{"line":265,"address":5737412,"length":1,"stats":{"Line":1}},{"line":266,"address":5737591,"length":1,"stats":{"Line":1}},{"line":267,"address":5737654,"length":1,"stats":{"Line":1}},{"line":268,"address":5738064,"length":1,"stats":{"Line":1}},{"line":269,"address":5737835,"length":1,"stats":{"Line":1}},{"line":270,"address":5737933,"length":1,"stats":{"Line":1}},{"line":272,"address":5737950,"length":1,"stats":{"Line":0}},{"line":273,"address":5738445,"length":1,"stats":{"Line":0}},{"line":276,"address":5738759,"length":1,"stats":{"Line":1}}],"covered":89,"coverable":113},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","network_peer.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Module defining the format of network peer cards and how to handle them.\n\nuse crate::network_endpoint::*;\nuse crate::*;\nuse base58::ToBase58;\nuse dubp_documents::blockstamp::Blockstamp;\nuse dubp_documents::BlockNumber;\nuse dubp_documents::ToStringObject;\nuse dup_crypto::keys::text_signable::TextSignable;\nuse dup_crypto::keys::*;\nuse dup_currency_params::CurrencyName;\nuse pest::iterators::Pair;\nuse pest::Parser;\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Peer card V10\npub struct PeerCardV10 {\n /// Peer card Blockstamp\n pub blockstamp: Blockstamp,\n /// Peer card issuer\n pub issuer: PubKey,\n /// Peer card endpoints list\n pub endpoints: Vec\u003cEndpointEnum\u003e,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Peer card V11\npub struct PeerCardV11 {\n /// Currency name\n pub currency_name: CurrencyName,\n /// Peer card issuer\n pub issuer: PubKey,\n /// Issuer node id\n pub node_id: NodeId,\n /// Block number when the peer record was created\n pub created_on: BlockNumber,\n /// Peer card binary endpoints\n pub endpoints: Vec\u003cEndpointV2\u003e,\n /// Peer card string endpoints\n pub endpoints_str: Vec\u003cString\u003e,\n /// Signature\n pub sig: Option\u003cSig\u003e,\n}\n\nimpl PeerCardV11 {\n /// From pest parser pair\n pub fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cPeerCardV11, TextDocumentParseError\u003e {\n let mut currency_str = \"\";\n let mut node_id = NodeId(0);\n let mut issuer = None;\n let mut created_on = None;\n let mut endpoints = Vec::new();\n let mut sig = None;\n for field in pair.into_inner() {\n match field.as_rule() {\n Rule::currency =\u003e currency_str = field.as_str(),\n Rule::node_id =\u003e node_id = NodeId(field.as_str().parse().unwrap()),\n Rule::pubkey =\u003e {\n issuer = Some(PubKey::Ed25519(\n ed25519::PublicKey::from_base58(field.as_str()).unwrap(),\n ))\n }\n Rule::block_id =\u003e {\n created_on = Some(BlockNumber(field.as_str().parse().unwrap())); // Grammar ensures that we have a digits string.\n }\n Rule::endpoint_v2 =\u003e endpoints.push(EndpointV2::from_pest_pair(field)?),\n Rule::ed25519_sig =\u003e {\n sig = Some(Sig::Ed25519(\n ed25519::Signature::from_base64(field.as_str()).unwrap(),\n ))\n }\n _ =\u003e fatal_error!(\"unexpected rule: {:?}\", field.as_rule()), // Grammar ensures that we never reach this line\n }\n }\n let endpoints_len = endpoints.len();\n\n Ok(PeerCardV11 {\n currency_name: CurrencyName(currency_str.to_owned()),\n issuer: issuer.expect(\"Grammar must ensure that peer v11 have valid issuer pubkey !\"),\n node_id,\n created_on: created_on\n .expect(\"Grammar must ensure that peer v11 have valid field created_on !\"),\n endpoints,\n endpoints_str: Vec::with_capacity(endpoints_len),\n sig,\n })\n }\n}\n\n#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]\n/// identity document for jsonification\npub struct PeerCardV11Stringified {\n /// Currency name\n pub currency_name: String,\n /// Peer card issuer\n pub issuer: String,\n /// Issuer node id\n pub node_id: String,\n /// Block number when the peer record was created\n pub created_on: u32,\n /// Peer card string endpoints\n pub endpoints: Vec\u003cString\u003e,\n /// Signature\n pub sig: String,\n}\n\nimpl ToStringObject for PeerCardV11 {\n type StringObject = PeerCardV11Stringified;\n /// Transforms an object into a json object\n fn to_string_object(\u0026self) -\u003e PeerCardV11Stringified {\n let mut endpoints: Vec\u003cString\u003e = self.endpoints.iter().map(EndpointV2::to_string).collect();\n endpoints.extend_from_slice(\u0026self.endpoints_str);\n\n PeerCardV11Stringified {\n currency_name: self.currency_name.0.clone(),\n issuer: format!(\"{}\", self.issuer),\n node_id: format!(\"{}\", self.node_id),\n created_on: self.created_on.0,\n endpoints,\n sig: if let Some(sig) = self.sig {\n format!(\"{}\", sig)\n } else {\n \"\".to_owned()\n },\n }\n }\n}\n\nimpl TextSignable for PeerCardV11 {\n fn as_signable_text(\u0026self) -\u003e String {\n format!(\n \"11:{currency}:{node_id}:{pubkey}:{created_on}\\n{endpoinds}\\n{endpoints_str}{nl}\",\n currency = self.currency_name.0,\n node_id = format!(\"{}\", self.node_id),\n pubkey = self.issuer.to_base58(),\n created_on = self.created_on.0,\n endpoinds = self\n .endpoints\n .iter()\n .map(EndpointV2::to_string)\n .collect::\u003cVec\u003cString\u003e\u003e()\n .join(\"\\n\"),\n endpoints_str = self.endpoints_str.join(\"\\n\"),\n nl = if self.endpoints_str.is_empty() {\n \"\"\n } else {\n \"\\n\"\n },\n )\n }\n fn issuer_pubkey(\u0026self) -\u003e PubKey {\n self.issuer\n }\n fn signature(\u0026self) -\u003e Option\u003cSig\u003e {\n self.sig\n }\n fn set_signature(\u0026mut self, signature: Sig) {\n self.sig = Some(signature);\n }\n}\n\nimpl TextDocumentParser\u003cRule\u003e for PeerCard {\n type DocumentType = PeerCard;\n\n fn parse(doc: \u0026str) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n let mut peer_v11_pairs = NetworkDocsParser::parse(Rule::peer_v11, doc)?;\n Ok(Self::from_versioned_pest_pair(\n 11,\n peer_v11_pairs.next().unwrap(),\n )?)\n }\n\n #[inline]\n fn from_pest_pair(pair: Pair\u003cRule\u003e) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n Self::from_versioned_pest_pair(11, pair)\n }\n\n #[inline]\n fn from_versioned_pest_pair(\n version: u16,\n pair: Pair\u003cRule\u003e,\n ) -\u003e Result\u003cSelf::DocumentType, TextDocumentParseError\u003e {\n match version {\n 11 =\u003e Ok(PeerCard::V11(PeerCardV11::from_pest_pair(pair)?)),\n v =\u003e Err(TextDocumentParseError::UnexpectedVersion(format!(\n \"Unsupported version: {}\",\n v\n ))),\n }\n }\n}\n\nimpl PeerCardV11 {\n /// Convert to JSON String\n pub fn to_json_peer(\u0026self) -\u003e Result\u003cString, serde_json::Error\u003e {\n Ok(serde_json::to_string_pretty(\u0026JsonPeerCardV11 {\n version: 11,\n currency_name: \u0026self.currency_name.0,\n node_id: self.node_id,\n algorithm: self.issuer.algo(),\n pubkey: self.issuer.to_base58(),\n created_on: self.created_on.0,\n endpoints: self.endpoints.iter().map(EndpointV2::to_string).collect(),\n signature: if let Some(sig) = self.sig {\n Some(sig.to_base64())\n } else {\n None\n },\n })?)\n }\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Peer card V11 for JSON Serializer\npub struct JsonPeerCardV11\u003c'a\u003e {\n /// PeerCard version\n pub version: usize,\n /// Currency Name\n pub currency_name: \u0026'a str,\n /// Issuer node id\n pub node_id: NodeId,\n /// Issuer pubkey alogirithm\n pub algorithm: KeysAlgo,\n /// Issuer pubkey\n pub pubkey: String,\n /// Peer card creation blockstamp\n pub created_on: u32,\n /// Endpoints\n pub endpoints: Vec\u003cString\u003e,\n /// Signature\n pub signature: Option\u003cString\u003e,\n}\n\n#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]\n/// Peer card\npub enum PeerCard {\n /// Peer card V10\n V10(PeerCardV10),\n /// Peer card V11\n V11(PeerCardV11),\n}\n\nimpl PeerCard {\n /// Get peer card version\n pub fn version(\u0026self) -\u003e u32 {\n match *self {\n PeerCard::V10(ref _peer_v10) =\u003e 10,\n PeerCard::V11(ref _peer_v11) =\u003e 11,\n }\n }\n /// Get peer card created_on field\n pub fn created_on(\u0026self) -\u003e BlockNumber {\n match *self {\n PeerCard::V10(ref peer_v10) =\u003e peer_v10.blockstamp.id,\n PeerCard::V11(ref peer_v11) =\u003e peer_v11.created_on,\n }\n }\n /// Get peer card issuer\n pub fn issuer(\u0026self) -\u003e PubKey {\n match *self {\n PeerCard::V10(ref peer_v10) =\u003e peer_v10.issuer,\n PeerCard::V11(ref peer_v11) =\u003e peer_v11.issuer,\n }\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::tests::keypair1;\n use std::net::Ipv4Addr;\n use std::str::FromStr;\n\n fn create_endpoint_v2() -\u003e EndpointV2 {\n EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![1u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: None,\n ip_v6: None,\n domain: Some(String::from(\"g1.durs.ifee.fr\")),\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n }\n }\n fn create_second_endpoint_v2() -\u003e EndpointV2 {\n EndpointV2 {\n api: ApiName(String::from(\"WS2P\")),\n api_version: 2,\n network_features: EndpointV2NetworkFeatures(vec![1u8]),\n api_features: ApiFeatures(vec![7u8]),\n ip_v4: Some(Ipv4Addr::from_str(\"84.16.72.210\").unwrap()),\n ip_v6: None,\n domain: None,\n port: 443u16,\n path: Some(String::from(\"ws2p\")),\n }\n }\n\n #[test]\n fn peer_card_v11_sign_and_verify() {\n let keypair1 = keypair1();\n let mut peer_card_v11 = PeerCardV11 {\n currency_name: CurrencyName(String::from(\"g1\")),\n issuer: PubKey::Ed25519(keypair1.public_key()),\n node_id: NodeId(0),\n created_on: BlockNumber(50),\n endpoints: vec![create_endpoint_v2(), create_second_endpoint_v2()],\n endpoints_str: vec![],\n sig: None,\n };\n // Sign\n let sign_result = peer_card_v11.sign(PrivKey::Ed25519(keypair1.private_key()));\n if let Ok(peer_card_v11_raw) = sign_result {\n println!(\"{}\", peer_card_v11_raw);\n assert_eq!(\n PeerCard::V11(peer_card_v11.clone()),\n PeerCard::parse(\u0026peer_card_v11_raw).expect(\"Fail to parse peer card v11 !\")\n )\n } else {\n panic!(\"fail to sign peer card : {:?}\", sign_result.err().unwrap())\n }\n // Verify signature\n peer_card_v11\n .verify()\n .expect(\"Fail to verify PeerCardV11 !\");\n }\n}\n","traces":[{"line":62,"address":null,"length":0,"stats":{"Line":1}},{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":1}},{"line":67,"address":null,"length":0,"stats":{"Line":1}},{"line":68,"address":null,"length":0,"stats":{"Line":1}},{"line":69,"address":null,"length":0,"stats":{"Line":1}},{"line":70,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":0}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":1}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":5236487,"length":1,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":84,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":5237340,"length":1,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":0}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":127,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":133,"address":null,"length":0,"stats":{"Line":0}},{"line":135,"address":null,"length":0,"stats":{"Line":0}},{"line":145,"address":null,"length":0,"stats":{"Line":2}},{"line":146,"address":null,"length":0,"stats":{"Line":2}},{"line":148,"address":null,"length":0,"stats":{"Line":2}},{"line":149,"address":null,"length":0,"stats":{"Line":2}},{"line":150,"address":null,"length":0,"stats":{"Line":2}},{"line":151,"address":null,"length":0,"stats":{"Line":2}},{"line":152,"address":null,"length":0,"stats":{"Line":2}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":0}},{"line":156,"address":null,"length":0,"stats":{"Line":0}},{"line":157,"address":null,"length":0,"stats":{"Line":0}},{"line":158,"address":null,"length":0,"stats":{"Line":2}},{"line":159,"address":null,"length":0,"stats":{"Line":2}},{"line":160,"address":null,"length":0,"stats":{"Line":2}},{"line":161,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":2}},{"line":167,"address":null,"length":0,"stats":{"Line":2}},{"line":169,"address":null,"length":0,"stats":{"Line":2}},{"line":170,"address":null,"length":0,"stats":{"Line":2}},{"line":172,"address":null,"length":0,"stats":{"Line":2}},{"line":173,"address":null,"length":0,"stats":{"Line":2}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":189,"address":null,"length":0,"stats":{"Line":0}},{"line":190,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":1}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":199,"address":null,"length":0,"stats":{"Line":1}},{"line":200,"address":null,"length":0,"stats":{"Line":0}},{"line":201,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":0}},{"line":210,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":0}},{"line":212,"address":null,"length":0,"stats":{"Line":0}},{"line":213,"address":null,"length":0,"stats":{"Line":0}},{"line":214,"address":null,"length":0,"stats":{"Line":0}},{"line":215,"address":null,"length":0,"stats":{"Line":0}},{"line":216,"address":null,"length":0,"stats":{"Line":0}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":0}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":220,"address":null,"length":0,"stats":{"Line":0}},{"line":221,"address":null,"length":0,"stats":{"Line":0}},{"line":222,"address":null,"length":0,"stats":{"Line":0}},{"line":260,"address":null,"length":0,"stats":{"Line":0}},{"line":261,"address":null,"length":0,"stats":{"Line":0}},{"line":262,"address":null,"length":0,"stats":{"Line":0}},{"line":263,"address":null,"length":0,"stats":{"Line":0}},{"line":267,"address":null,"length":0,"stats":{"Line":0}},{"line":268,"address":null,"length":0,"stats":{"Line":0}},{"line":269,"address":null,"length":0,"stats":{"Line":0}},{"line":270,"address":null,"length":0,"stats":{"Line":0}},{"line":274,"address":null,"length":0,"stats":{"Line":0}},{"line":275,"address":null,"length":0,"stats":{"Line":0}},{"line":276,"address":null,"length":0,"stats":{"Line":0}},{"line":277,"address":null,"length":0,"stats":{"Line":0}},{"line":289,"address":5669280,"length":1,"stats":{"Line":1}},{"line":291,"address":5669297,"length":1,"stats":{"Line":1}},{"line":293,"address":5669370,"length":1,"stats":{"Line":1}},{"line":294,"address":5669463,"length":1,"stats":{"Line":1}},{"line":297,"address":5669583,"length":1,"stats":{"Line":1}},{"line":299,"address":5669657,"length":1,"stats":{"Line":1}},{"line":302,"address":5670096,"length":1,"stats":{"Line":1}},{"line":304,"address":5670113,"length":1,"stats":{"Line":1}},{"line":306,"address":5670186,"length":1,"stats":{"Line":1}},{"line":307,"address":5670288,"length":1,"stats":{"Line":1}},{"line":308,"address":5670388,"length":1,"stats":{"Line":1}},{"line":312,"address":5670552,"length":1,"stats":{"Line":1}},{"line":317,"address":5670992,"length":1,"stats":{"Line":2}},{"line":318,"address":5670999,"length":1,"stats":{"Line":1}},{"line":319,"address":5671492,"length":1,"stats":{"Line":1}},{"line":320,"address":5671060,"length":1,"stats":{"Line":1}},{"line":321,"address":5671113,"length":1,"stats":{"Line":1}},{"line":322,"address":5671195,"length":1,"stats":{"Line":1}},{"line":323,"address":5671206,"length":1,"stats":{"Line":1}},{"line":324,"address":5671227,"length":1,"stats":{"Line":1}},{"line":325,"address":5671430,"length":1,"stats":{"Line":1}},{"line":326,"address":5671484,"length":1,"stats":{"Line":1}},{"line":329,"address":5671740,"length":1,"stats":{"Line":1}},{"line":330,"address":5671904,"length":1,"stats":{"Line":1}},{"line":331,"address":5671967,"length":1,"stats":{"Line":1}},{"line":332,"address":5672313,"length":1,"stats":{"Line":1}},{"line":333,"address":5672148,"length":1,"stats":{"Line":1}},{"line":334,"address":5672210,"length":1,"stats":{"Line":1}},{"line":336,"address":5672227,"length":1,"stats":{"Line":0}},{"line":337,"address":5672694,"length":1,"stats":{"Line":0}},{"line":340,"address":5673010,"length":1,"stats":{"Line":1}}],"covered":78,"coverable":131},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","network-documents","src","url.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Define url type\n\nuse crate::host::Host;\nuse durs_common_tools::fatal_error;\nuse failure::Fail;\nuse std::str::FromStr;\nuse unwrap::unwrap;\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub struct UrlWithoutScheme {\n host: Host,\n port: Option\u003cu16\u003e,\n path: Option\u003cString\u003e,\n}\n\n#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]\npub enum UrlWithoutSchemeParseError {\n #[fail(display = \"Empty string.\")]\n EmptyStr,\n #[fail(display = \"Invalid host: {}.\", _0)]\n InvalidHost(url::ParseError),\n #[fail(display = \"Invalid URL: {}.\", _0)]\n InvalidUrl(url::ParseError),\n}\n\nimpl FromStr for UrlWithoutScheme {\n type Err = UrlWithoutSchemeParseError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n if source.is_empty() {\n Err(UrlWithoutSchemeParseError::EmptyStr)\n } else {\n let source_parts: Vec\u003c\u0026str\u003e = source.split('/').collect();\n let host_and_port = source_parts[0];\n let host_and_port_len = host_and_port.len();\n let source_parts2: Vec\u003c\u0026str\u003e = host_and_port.split(':').collect();\n\n let (host_len, port) = if source_parts2.len() \u003e= 2 {\n let may_port_str = unwrap!(source_parts2.last());\n if let Ok(port) = u16::from_str(*may_port_str) {\n let host_len = host_and_port_len - may_port_str.len() - 1;\n (host_len, Some(port))\n } else {\n (host_and_port_len, None)\n }\n } else {\n (host_and_port_len, None)\n };\n\n let path = if source_parts.len() \u003e= 2 {\n Some(String::from(\u0026source[host_and_port_len..]))\n } else {\n None\n };\n\n Ok(UrlWithoutScheme {\n host: Host::parse(\u0026host_and_port[..host_len])\n .map_err(UrlWithoutSchemeParseError::InvalidHost)?,\n port,\n path,\n })\n }\n }\n}\n\nimpl ToString for UrlWithoutScheme {\n fn to_string(\u0026self) -\u003e String {\n format!(\n \"{host}{port}{path}\",\n host = self.host,\n port = if let Some(port) = self.port {\n format!(\":{}\", port)\n } else {\n \"\".to_owned()\n },\n path = self.path()\n )\n }\n}\n\nimpl UrlWithoutScheme {\n pub fn path(\u0026self) -\u003e \u0026str {\n if let Some(ref path) = self.path {\n path.as_str()\n } else {\n \"\"\n }\n }\n pub fn tls(\u0026self) -\u003e bool {\n if let Some(port) = self.port {\n port == 443u16\n } else {\n true\n }\n }\n pub fn to_url_with_scheme(\u0026self, scheme: \u0026str) -\u003e Result\u003curl::Url, url::ParseError\u003e {\n let scheme = if self.tls() {\n format!(\"{}s\", scheme)\n } else {\n scheme.to_owned()\n };\n\n let url_str = format!(\n \"{scheme}://{url_without_scheme}\",\n scheme = scheme,\n url_without_scheme = self.to_string()\n );\n\n url::Url::parse(\u0026url_str)\n }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\npub enum Url {\n Url(url::Url),\n UrlWithoutScheme(UrlWithoutScheme),\n}\n\n#[derive(Clone, Copy, Debug, Fail, PartialEq, Eq)]\n#[fail(\n display = \"Invalid URL: {}. Invalid URL without scheme: {}.\",\n url_err, url_without_scheme_err\n)]\npub struct UrlParseError {\n url_err: url::ParseError,\n url_without_scheme_err: UrlWithoutSchemeParseError,\n}\n\nimpl FromStr for Url {\n type Err = UrlParseError;\n\n fn from_str(source: \u0026str) -\u003e Result\u003cSelf, Self::Err\u003e {\n match UrlWithoutScheme::from_str(source) {\n Ok(url_without_scheme) =\u003e Ok(Url::UrlWithoutScheme(url_without_scheme)),\n Err(url_without_scheme_err) =\u003e match url::Url::parse(source) {\n Ok(url) =\u003e Ok(Url::Url(url)),\n Err(url_err) =\u003e Err(UrlParseError {\n url_err,\n url_without_scheme_err,\n }),\n },\n }\n }\n}\n\nimpl Url {\n pub fn tls(\u0026self) -\u003e bool {\n match self {\n Url::Url(url) =\u003e match url.scheme() {\n \"https\" | \"wss\" =\u003e true,\n _ =\u003e false,\n },\n Url::UrlWithoutScheme(url_without_scheme) =\u003e url_without_scheme.tls(),\n }\n }\n pub fn path(\u0026self) -\u003e \u0026str {\n match self {\n Url::Url(url) =\u003e url.path(),\n Url::UrlWithoutScheme(url_without_scheme) =\u003e url_without_scheme.path(),\n }\n }\n pub fn to_listenable_addr(\n \u0026self,\n default_scheme: \u0026str,\n ) -\u003e std::io::Result\u003curl::HostAndPort\u003cString\u003e\u003e {\n self.to_listenable_addr_with_default_port(default_scheme, default_port)\n }\n pub fn to_listenable_addr_with_default_port\u003cF\u003e(\n \u0026self,\n default_scheme: \u0026str,\n default_port: F,\n ) -\u003e std::io::Result\u003curl::HostAndPort\u003cString\u003e\u003e\n where\n F: FnOnce(\u0026url::Url) -\u003e Result\u003cu16, ()\u003e,\n {\n match self {\n Url::Url(url) =\u003e Ok(url.with_default_port(default_port)?.to_owned()),\n Url::UrlWithoutScheme(url_without_scheme) =\u003e {\n match url_without_scheme.to_url_with_scheme(default_scheme) {\n Ok(url) =\u003e Ok(url.with_default_port(default_port)?.to_owned()),\n Err(e) =\u003e fatal_error!(\"Fail to convert UrlWithoutScheme to Url: {}\", e),\n }\n }\n }\n }\n\n pub fn to_url_string(\u0026self, default_scheme: \u0026str) -\u003e std::io::Result\u003cString\u003e {\n self.to_url_string_with_default_port(default_scheme, default_port)\n }\n\n pub fn to_url_string_with_default_port\u003cF\u003e(\n \u0026self,\n default_scheme: \u0026str,\n default_port: F,\n ) -\u003e std::io::Result\u003cString\u003e\n where\n F: FnOnce(\u0026url::Url) -\u003e Result\u003cu16, ()\u003e,\n {\n let url: url::Url = match self {\n Url::Url(url) =\u003e url.clone(),\n Url::UrlWithoutScheme(url_without_scheme) =\u003e {\n match url_without_scheme.to_url_with_scheme(default_scheme) {\n Ok(url) =\u003e url,\n Err(e) =\u003e fatal_error!(\"Fail to convert UrlWithoutScheme to Url: {}\", e),\n }\n }\n };\n\n let host_and_port: url::HostAndPort\u003cString\u003e =\n url.with_default_port(default_port)?.to_owned();\n\n Ok(format!(\n \"{scheme}://{host}:{port}{path}\",\n scheme = url.scheme(),\n host = host_and_port.host,\n port = host_and_port.port,\n path = url.path(),\n ))\n }\n}\n\nfn default_port(_: \u0026url::Url) -\u003e Result\u003cu16, ()\u003e {\n fatal_error!(\"Unknown url scheme !\")\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::*;\n\n #[test]\n fn parse_url_with_host_only() -\u003e Result\u003c(), url::ParseError\u003e {\n let host = Host::parse(\"g1.duniter.org\")?;\n\n let expected_url = Url::UrlWithoutScheme(UrlWithoutScheme {\n host,\n port: None,\n path: None,\n });\n\n assert_eq!(Ok(expected_url.clone()), Url::from_str(\"g1.duniter.org\"));\n\n assert_eq!(\n \"wss://g1.duniter.org:443/\".to_owned(),\n expected_url\n .to_url_string(\"ws\")\n .expect(\"Fail to get listen url\")\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_url_with_scheme_and_host() -\u003e Result\u003c(), url::ParseError\u003e {\n let url = Url::Url(url::Url::parse(\"wss://g1.duniter.org\")?);\n\n assert_eq!(\n \"wss://g1.duniter.org:443/\".to_owned(),\n url.to_url_string(\"ws\").expect(\"Fail to get listen url\")\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_url_with_host_and_port() -\u003e Result\u003c(), url::ParseError\u003e {\n let host = Host::parse(\"g1.duniter.org\")?;\n\n let expected_url = Url::UrlWithoutScheme(UrlWithoutScheme {\n host,\n port: Some(20901u16),\n path: None,\n });\n\n assert_eq!(\n Ok(expected_url.clone()),\n Url::from_str(\"g1.duniter.org:20901\")\n );\n\n assert_eq!(\n \"ws://g1.duniter.org:20901/\".to_owned(),\n expected_url\n .to_url_string(\"ws\")\n .expect(\"Fail to get listen url\")\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_url_with_scheme_and_host_and_port() -\u003e Result\u003c(), url::ParseError\u003e {\n let url = Url::Url(url::Url::parse(\"ws://g1.duniter.org:20901\")?);\n\n assert_eq!(\n \"ws://g1.duniter.org:20901/\".to_owned(),\n url.to_url_string(\"ws\").expect(\"Fail to get listen url\")\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_url_with_host_and_path() -\u003e Result\u003c(), url::ParseError\u003e {\n let host = Host::parse(\"g1.duniter.org\")?;\n\n assert_eq!(\n Ok(Url::UrlWithoutScheme(UrlWithoutScheme {\n host,\n port: None,\n path: Some(\"/gva/subscriptions\".to_owned()),\n })),\n Url::from_str(\"g1.duniter.org/gva/subscriptions\")\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_url_with_host_and_port_and_path() -\u003e Result\u003c(), url::ParseError\u003e {\n let host = Host::parse(\"g1.duniter.org\")?;\n\n assert_eq!(\n Ok(Url::UrlWithoutScheme(UrlWithoutScheme {\n host,\n port: Some(20901u16),\n path: Some(\"/gva/subscriptions\".to_owned()),\n })),\n Url::from_str(\"g1.duniter.org:20901/gva/subscriptions\")\n );\n\n Ok(())\n }\n\n #[test]\n fn parse_url_with_scheme_and_host_and_path() -\u003e Result\u003c(), url::ParseError\u003e {\n match Url::from_str(\"ws://g1.duniter.org/gva/subscriptions\") {\n Ok(url) =\u003e match url {\n Url::Url(_) =\u003e {}\n _ =\u003e panic!(\"expected Url::Url, found other variant !\"),\n },\n Err(e) =\u003e panic!(\"Fail to parse url: {} !\", e),\n }\n\n Ok(())\n }\n}\n","traces":[{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":46,"address":null,"length":0,"stats":{"Line":0}},{"line":47,"address":null,"length":0,"stats":{"Line":0}},{"line":48,"address":null,"length":0,"stats":{"Line":1}},{"line":49,"address":null,"length":0,"stats":{"Line":1}},{"line":50,"address":null,"length":0,"stats":{"Line":1}},{"line":51,"address":null,"length":0,"stats":{"Line":1}},{"line":53,"address":null,"length":0,"stats":{"Line":1}},{"line":54,"address":null,"length":0,"stats":{"Line":1}},{"line":55,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":null,"length":0,"stats":{"Line":1}},{"line":57,"address":null,"length":0,"stats":{"Line":1}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":1}},{"line":61,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":66,"address":null,"length":0,"stats":{"Line":1}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":1}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":85,"address":null,"length":0,"stats":{"Line":1}},{"line":86,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":0}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":0}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":1}},{"line":114,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":0}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":124,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":148,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":0}},{"line":153,"address":null,"length":0,"stats":{"Line":0}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":162,"address":null,"length":0,"stats":{"Line":0}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":0}},{"line":165,"address":null,"length":0,"stats":{"Line":0}},{"line":166,"address":null,"length":0,"stats":{"Line":0}},{"line":168,"address":null,"length":0,"stats":{"Line":0}},{"line":171,"address":null,"length":0,"stats":{"Line":0}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":181,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":0}},{"line":193,"address":null,"length":0,"stats":{"Line":0}},{"line":194,"address":null,"length":0,"stats":{"Line":0}},{"line":195,"address":null,"length":0,"stats":{"Line":0}},{"line":196,"address":null,"length":0,"stats":{"Line":0}},{"line":202,"address":null,"length":0,"stats":{"Line":1}},{"line":203,"address":null,"length":0,"stats":{"Line":1}},{"line":206,"address":null,"length":0,"stats":{"Line":1}},{"line":214,"address":null,"length":0,"stats":{"Line":1}},{"line":215,"address":null,"length":0,"stats":{"Line":1}},{"line":216,"address":null,"length":0,"stats":{"Line":1}},{"line":217,"address":null,"length":0,"stats":{"Line":1}},{"line":218,"address":null,"length":0,"stats":{"Line":1}},{"line":219,"address":null,"length":0,"stats":{"Line":0}},{"line":224,"address":null,"length":0,"stats":{"Line":0}},{"line":225,"address":null,"length":0,"stats":{"Line":1}},{"line":227,"address":null,"length":0,"stats":{"Line":1}},{"line":229,"address":null,"length":0,"stats":{"Line":1}},{"line":230,"address":null,"length":0,"stats":{"Line":0}},{"line":231,"address":null,"length":0,"stats":{"Line":1}},{"line":232,"address":null,"length":0,"stats":{"Line":1}},{"line":237,"address":4659520,"length":1,"stats":{"Line":0}},{"line":238,"address":4659542,"length":1,"stats":{"Line":0}},{"line":247,"address":5940912,"length":1,"stats":{"Line":2}},{"line":248,"address":5966158,"length":1,"stats":{"Line":1}},{"line":250,"address":5967630,"length":1,"stats":{"Line":1}},{"line":251,"address":5967574,"length":1,"stats":{"Line":1}},{"line":252,"address":5967608,"length":1,"stats":{"Line":1}},{"line":253,"address":5967618,"length":1,"stats":{"Line":1}},{"line":256,"address":5966541,"length":1,"stats":{"Line":1}},{"line":258,"address":5967165,"length":1,"stats":{"Line":0}},{"line":259,"address":5967068,"length":1,"stats":{"Line":1}},{"line":260,"address":5967095,"length":1,"stats":{"Line":1}},{"line":265,"address":5967541,"length":1,"stats":{"Line":1}},{"line":269,"address":5940960,"length":1,"stats":{"Line":2}},{"line":270,"address":5968094,"length":1,"stats":{"Line":1}},{"line":272,"address":5968555,"length":1,"stats":{"Line":0}},{"line":273,"address":5968948,"length":1,"stats":{"Line":1}},{"line":274,"address":5968488,"length":1,"stats":{"Line":1}},{"line":277,"address":5968928,"length":1,"stats":{"Line":1}},{"line":281,"address":5941008,"length":1,"stats":{"Line":2}},{"line":282,"address":5969134,"length":1,"stats":{"Line":1}},{"line":284,"address":5970616,"length":1,"stats":{"Line":1}},{"line":285,"address":5970550,"length":1,"stats":{"Line":1}},{"line":286,"address":5970584,"length":1,"stats":{"Line":1}},{"line":287,"address":5970604,"length":1,"stats":{"Line":1}},{"line":290,"address":5969666,"length":1,"stats":{"Line":0}},{"line":291,"address":5969517,"length":1,"stats":{"Line":1}},{"line":292,"address":5969621,"length":1,"stats":{"Line":1}},{"line":295,"address":5970141,"length":1,"stats":{"Line":0}},{"line":296,"address":5970044,"length":1,"stats":{"Line":1}},{"line":297,"address":5970071,"length":1,"stats":{"Line":1}},{"line":302,"address":5970517,"length":1,"stats":{"Line":1}},{"line":306,"address":5941056,"length":1,"stats":{"Line":2}},{"line":307,"address":5971070,"length":1,"stats":{"Line":1}},{"line":309,"address":5971531,"length":1,"stats":{"Line":0}},{"line":310,"address":5971924,"length":1,"stats":{"Line":1}},{"line":311,"address":5971464,"length":1,"stats":{"Line":1}},{"line":314,"address":5971904,"length":1,"stats":{"Line":1}},{"line":318,"address":5941104,"length":1,"stats":{"Line":2}},{"line":319,"address":5972110,"length":1,"stats":{"Line":1}},{"line":321,"address":5972834,"length":1,"stats":{"Line":0}},{"line":322,"address":5972513,"length":1,"stats":{"Line":1}},{"line":323,"address":5973231,"length":1,"stats":{"Line":1}},{"line":324,"address":5973265,"length":1,"stats":{"Line":1}},{"line":325,"address":5972481,"length":1,"stats":{"Line":1}},{"line":327,"address":5972789,"length":1,"stats":{"Line":1}},{"line":330,"address":5973213,"length":1,"stats":{"Line":1}},{"line":334,"address":5941152,"length":1,"stats":{"Line":2}},{"line":335,"address":5973502,"length":1,"stats":{"Line":1}},{"line":337,"address":5974226,"length":1,"stats":{"Line":0}},{"line":338,"address":5973905,"length":1,"stats":{"Line":1}},{"line":339,"address":5974623,"length":1,"stats":{"Line":1}},{"line":340,"address":5974657,"length":1,"stats":{"Line":1}},{"line":341,"address":5973873,"length":1,"stats":{"Line":1}},{"line":343,"address":5974181,"length":1,"stats":{"Line":1}},{"line":346,"address":5974605,"length":1,"stats":{"Line":1}},{"line":350,"address":5941200,"length":1,"stats":{"Line":2}},{"line":351,"address":5974894,"length":1,"stats":{"Line":1}},{"line":352,"address":5974953,"length":1,"stats":{"Line":1}},{"line":353,"address":5975043,"length":1,"stats":{"Line":1}},{"line":354,"address":5975080,"length":1,"stats":{"Line":0}},{"line":356,"address":5975113,"length":1,"stats":{"Line":0}},{"line":359,"address":5975448,"length":1,"stats":{"Line":1}}],"covered":112,"coverable":159},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","rules-engine","src","lib.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Rules engine\n\n#![deny(\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces\n)]\n\npub mod rule;\n\nuse failure::Fail;\nuse rayon::prelude::*;\nuse rule::{Rule, RuleError, RuleNumber};\nuse std::collections::BTreeMap;\nuse std::fmt::Debug;\n\n#[derive(Copy, Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]\npub struct ProtocolVersion(pub usize);\n\nimpl std::fmt::Display for ProtocolVersion {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"{}\", self.0)\n }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq, Hash)]\npub struct ProtocolRules(pub Vec\u003cRulesGroup\u003e);\n\nimpl From\u003cVec\u003cusize\u003e\u003e for ProtocolRules {\n fn from(rules_numbers: Vec\u003cusize\u003e) -\u003e Self {\n ProtocolRules(vec![RulesGroup::Ser(\n rules_numbers.into_iter().map(RuleNumber).collect(),\n )])\n }\n}\n\nimpl From\u003cVec\u003cRulesGroup\u003e\u003e for ProtocolRules {\n fn from(rules_groups: Vec\u003cRulesGroup\u003e) -\u003e Self {\n ProtocolRules(rules_groups)\n }\n}\n\n#[derive(Clone, Debug, PartialEq, Eq)]\n/// Protocol\npub struct Protocol(BTreeMap\u003cProtocolVersion, ProtocolRules\u003e);\n\nimpl Protocol {\n /// Create new protocol\n /// protocol_versions: Dictionary of rules to be applied for each version of the protocol (rules will be applied in the order provided)\n pub fn new(protocol_versions: BTreeMap\u003cProtocolVersion, ProtocolRules\u003e) -\u003e Self {\n Protocol(protocol_versions)\n }\n\n /// Get specific protocol version\n pub fn get(\u0026self, protocol_version: ProtocolVersion) -\u003e Option\u003c\u0026ProtocolRules\u003e {\n self.0.get(\u0026protocol_version)\n }\n}\n\n/// Rules groups\n#[derive(Debug, Clone, PartialEq, Eq, Hash)]\npub enum RulesGroup {\n /// In serial\n Ser(Vec\u003cRuleNumber\u003e),\n /// In parallel\n Par(Vec\u003cRulesGroup\u003e),\n}\n\nimpl RulesGroup {\n #[inline]\n /// Create singleton rules group\n pub fn s1(rule_number: usize) -\u003e Self {\n RulesGroup::Ser(vec![RuleNumber(rule_number)])\n }\n #[inline]\n /// Create parallel set of rules\n pub fn pr(rules_numbers: Vec\u003cusize\u003e) -\u003e Self {\n RulesGroup::Par(rules_numbers.into_iter().map(RulesGroup::s1).collect())\n }\n}\n\n/// Rules engine\npub struct RulesEngine\u003cD: Debug + Sync, E: Eq + Fail + PartialEq\u003e {\n /// All rules\n all_rules: BTreeMap\u003cRuleNumber, Rule\u003cD, E\u003e\u003e,\n}\n\nimpl\u003cD: Debug + Sync, E: Eq + Fail + PartialEq\u003e RulesEngine\u003cD, E\u003e {\n /// Create new rules engine\n pub fn new(all_rules: BTreeMap\u003cRuleNumber, Rule\u003cD, E\u003e\u003e) -\u003e Self {\n RulesEngine { all_rules }\n }\n\n fn apply_rules_group_ref(\n \u0026self,\n protocol_version: ProtocolVersion,\n rules_group: RulesGroup,\n rule_datas: \u0026D,\n ) -\u003e Result\u003c(), EngineError\u003cE\u003e\u003e {\n match rules_group {\n RulesGroup::Ser(rules_numbers) =\u003e rules_numbers\n .into_iter()\n .map(|rule_number| self.apply_rule_ref(protocol_version, rule_number, rule_datas))\n .collect(),\n RulesGroup::Par(rules_group) =\u003e rules_group\n .into_par_iter()\n .map(|rg| self.apply_rules_group_ref(protocol_version, rg, rule_datas))\n .collect(),\n }\n }\n\n fn apply_rule_ref(\n \u0026self,\n protocol_version: ProtocolVersion,\n rule_number: RuleNumber,\n rule_datas: \u0026D,\n ) -\u003e Result\u003c(), EngineError\u003cE\u003e\u003e {\n if let Some(rule) = self.all_rules.get(\u0026rule_number) {\n rule.execute(protocol_version, rule_number, rule_datas)\n } else {\n Err(EngineError::RuleNotExist {\n rule_number,\n protocol_version,\n })\n }\n }\n\n fn apply_rule_mut(\n \u0026self,\n protocol_version: ProtocolVersion,\n rule_number: RuleNumber,\n rule_datas: \u0026mut D,\n ) -\u003e Result\u003c(), EngineError\u003cE\u003e\u003e {\n if let Some(rule) = self.all_rules.get(\u0026rule_number) {\n rule.execute_mut(protocol_version, rule_number, rule_datas)\n } else {\n Err(EngineError::RuleNotExist {\n rule_number,\n protocol_version,\n })\n }\n }\n\n /// Apply a specific version of the protocol\n pub fn apply_protocol(\n \u0026self,\n protocol: Protocol,\n protocol_version: ProtocolVersion,\n rule_datas: \u0026mut D,\n ) -\u003e Result\u003c(), EngineError\u003cE\u003e\u003e {\n if let Some(protocol_rules) = protocol.get(protocol_version) {\n for rules_group in \u0026protocol_rules.0 {\n let result: Result\u003c(), EngineError\u003cE\u003e\u003e = match rules_group {\n RulesGroup::Ser(rules_numbers) =\u003e rules_numbers\n .iter()\n .map(|rule_number| {\n self.apply_rule_mut(protocol_version, *rule_number, rule_datas)\n })\n .collect(),\n RulesGroup::Par(rules_group) =\u003e rules_group\n .par_iter()\n .map(|rg| {\n self.apply_rules_group_ref(protocol_version, rg.clone(), rule_datas)\n })\n .collect(),\n };\n if let Err(err) = result {\n return Err(err);\n }\n }\n\n Ok(())\n } else {\n Err(EngineError::ProtocolVersionNotExist { protocol_version })\n }\n }\n}\n\n/// Protocol error\n#[derive(Debug, Eq, Fail, PartialEq)]\npub enum EngineError\u003cE: Eq + Fail + PartialEq\u003e {\n #[fail(display = \"{}\", _0)]\n /// Rule Error\n RuleError(RuleError\u003cE\u003e),\n #[fail(display = \"protocol V{} not exist\", protocol_version)]\n /// The protocol version does not exist\n ProtocolVersionNotExist {\n /// Protocole version\n protocol_version: ProtocolVersion,\n },\n #[fail(\n display = \"Rule n°{} not exist (require by protocol V{})\",\n rule_number, protocol_version\n )]\n /// A rule required by the protocol version does not exist\n RuleNotExist {\n /// Rule number\n rule_number: RuleNumber,\n /// Protocole version\n protocol_version: ProtocolVersion,\n },\n #[fail(\n display = \"Rule n°{} is mutable and called in parallel in the V{} protocol, this is prohibited.\n A rule can be mutable or called in parallel but not both at the same time.\",\n rule_number, protocol_version\n )]\n /// Calling a mutable rule in a part executed in parallel\n MutRuleInPar {\n /// Rule number\n rule_number: RuleNumber,\n /// Protocole version\n protocol_version: ProtocolVersion,\n },\n #[fail(\n display = \"Rule n°{} does not exist in a version less than or equal to the protocol version (V{})\",\n rule_number, protocol_version\n )]\n /// Calling a rule too recent\n RuleTooRecent {\n /// Rule number\n rule_number: RuleNumber,\n /// Protocole version\n protocol_version: ProtocolVersion,\n },\n}\n\n#[cfg(test)]\nmod tests {\n\n use super::rule::*;\n use super::*;\n use maplit::btreemap;\n\n #[derive(Debug)]\n struct Datas {\n i: usize,\n }\n\n #[derive(Debug, Eq, Fail, PartialEq)]\n #[fail(display = \"\")]\n struct Error {}\n\n fn r2_v1(datas: \u0026mut Datas) -\u003e Result\u003c(), Error\u003e {\n if datas.i == 0 {\n datas.i += 1;\n Ok(())\n } else {\n Err(Error {})\n }\n }\n\n fn r3_v2(datas: \u0026Datas) -\u003e Result\u003c(), Error\u003e {\n if datas.i == 1 {\n Ok(())\n } else {\n Err(Error {})\n }\n }\n\n fn get_test_engine() -\u003e RulesEngine\u003cDatas, Error\u003e {\n let all_rules: BTreeMap\u003cRuleNumber, Rule\u003cDatas, Error\u003e\u003e = btreemap![\n RuleNumber(2) =\u003e Rule::new(RuleNumber(2), btreemap![\n ProtocolVersion(1) =\u003e RuleFn::RefMut(r2_v1),\n ]).expect(\"Fail to create rule n°2\"),\n RuleNumber(3) =\u003e Rule::new(RuleNumber(3), btreemap![\n ProtocolVersion(2) =\u003e RuleFn::Ref(r3_v2),\n ]).expect(\"Fail to create rule n°2\"),\n ];\n\n RulesEngine::new(all_rules)\n }\n\n #[test]\n fn rule_without_impl() {\n if let Err(err) = Rule::\u003cDatas, Error\u003e::new(RuleNumber(1), btreemap![]) {\n assert_eq!(\n RuleWithoutImpl {\n rule_number: RuleNumber(1),\n },\n err,\n )\n } else {\n panic!(\"Rule creation must be fail\")\n }\n\n println!(\"{}\", ProtocolVersion(1));\n println!(\"{}\", RuleNumber(1));\n }\n\n #[test]\n fn protocol_empty() -\u003e Result\u003c(), EngineError\u003cError\u003e\u003e {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol_empty: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e Vec::\u003cusize\u003e::with_capacity(0).into()\n ]);\n\n engine.apply_protocol(protocol_empty, ProtocolVersion(1), \u0026mut datas)\n }\n\n #[test]\n fn protocol_version_not_exist() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol_empty: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e Vec::\u003cusize\u003e::with_capacity(0).into()\n ]);\n\n assert_eq!(\n Err(EngineError::ProtocolVersionNotExist {\n protocol_version: ProtocolVersion(2),\n }),\n engine.apply_protocol(protocol_empty, ProtocolVersion(2), \u0026mut datas)\n )\n }\n\n #[test]\n fn rule_not_exist() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e vec![1usize].into()\n ]);\n\n assert_eq!(\n Err(EngineError::RuleNotExist {\n rule_number: RuleNumber(1),\n protocol_version: ProtocolVersion(1)\n }),\n engine.apply_protocol(protocol, ProtocolVersion(1), \u0026mut datas)\n );\n\n let mut datas = Datas { i: 0 };\n\n let protocol_par: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e vec![RulesGroup::pr(vec![1usize])].into()\n ]);\n\n assert_eq!(\n Err(EngineError::RuleNotExist {\n rule_number: RuleNumber(1),\n protocol_version: ProtocolVersion(1)\n }),\n engine.apply_protocol(protocol_par, ProtocolVersion(1), \u0026mut datas)\n );\n }\n\n #[test]\n fn rule_fail() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 1 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e vec![2usize].into()\n ]);\n\n assert_eq!(\n Err(EngineError::RuleError(RuleError {\n rule_number: RuleNumber(2),\n cause: Error {},\n })),\n engine.apply_protocol(protocol, ProtocolVersion(1), \u0026mut datas)\n )\n }\n\n #[test]\n fn par_rule_fail() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(2) =\u003e vec![RulesGroup::pr(vec![3usize])].into()\n ]);\n\n assert_eq!(\n Err(EngineError::RuleError(RuleError {\n rule_number: RuleNumber(3),\n cause: Error {},\n })),\n engine.apply_protocol(protocol, ProtocolVersion(2), \u0026mut datas)\n )\n }\n\n #[test]\n fn rule_too_recent() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e vec![2usize, 3].into()\n ]);\n\n assert_eq!(\n Err(EngineError::RuleTooRecent {\n protocol_version: ProtocolVersion(1),\n rule_number: RuleNumber(3),\n }),\n engine.apply_protocol(protocol, ProtocolVersion(1), \u0026mut datas)\n )\n }\n\n #[test]\n fn par_rule_too_recent() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(1) =\u003e vec![RulesGroup::pr(vec![3])].into()\n ]);\n\n assert_eq!(\n Err(EngineError::RuleTooRecent {\n protocol_version: ProtocolVersion(1),\n rule_number: RuleNumber(3),\n }),\n engine.apply_protocol(protocol, ProtocolVersion(1), \u0026mut datas)\n )\n }\n\n #[test]\n fn mut_rule_in_par_protocol() {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 1 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(2) =\u003e vec![RulesGroup::pr(vec![2usize, 3])].into()\n ]);\n\n assert_eq!(\n Err(EngineError::MutRuleInPar {\n protocol_version: ProtocolVersion(2),\n rule_number: RuleNumber(2),\n }),\n engine.apply_protocol(protocol, ProtocolVersion(2), \u0026mut datas)\n )\n }\n\n #[test]\n fn protocol_success() -\u003e Result\u003c(), EngineError\u003cError\u003e\u003e {\n let engine = get_test_engine();\n\n let mut datas = Datas { i: 0 };\n\n let protocol: Protocol = Protocol::new(btreemap![\n ProtocolVersion(2) =\u003e vec![2usize, 3].into()\n ]);\n\n engine.apply_protocol(protocol, ProtocolVersion(2), \u0026mut datas)\n }\n}\n","traces":[{"line":39,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":1}},{"line":49,"address":null,"length":0,"stats":{"Line":1}},{"line":50,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":null,"length":0,"stats":{"Line":1}},{"line":57,"address":null,"length":0,"stats":{"Line":1}},{"line":68,"address":null,"length":0,"stats":{"Line":1}},{"line":69,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":2}},{"line":118,"address":null,"length":0,"stats":{"Line":1}},{"line":119,"address":null,"length":0,"stats":{"Line":2}},{"line":123,"address":null,"length":0,"stats":{"Line":0}},{"line":130,"address":null,"length":0,"stats":{"Line":2}},{"line":136,"address":null,"length":0,"stats":{"Line":2}},{"line":137,"address":null,"length":0,"stats":{"Line":2}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":1}},{"line":140,"address":null,"length":0,"stats":{"Line":1}},{"line":141,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":0}},{"line":155,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":157,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":169,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":1}},{"line":173,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":2}},{"line":175,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":0}},{"line":180,"address":null,"length":0,"stats":{"Line":3}},{"line":181,"address":null,"length":0,"stats":{"Line":2}},{"line":183,"address":null,"length":0,"stats":{"Line":0}},{"line":185,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":190,"address":null,"length":0,"stats":{"Line":1}},{"line":191,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":1}},{"line":261,"address":4308656,"length":1,"stats":{"Line":1}},{"line":262,"address":4308665,"length":1,"stats":{"Line":1}},{"line":263,"address":4308676,"length":1,"stats":{"Line":1}},{"line":264,"address":4308712,"length":1,"stats":{"Line":1}},{"line":266,"address":4308719,"length":1,"stats":{"Line":1}},{"line":270,"address":4308768,"length":1,"stats":{"Line":1}},{"line":271,"address":4308776,"length":1,"stats":{"Line":1}},{"line":272,"address":4308786,"length":1,"stats":{"Line":1}},{"line":274,"address":4308793,"length":1,"stats":{"Line":1}},{"line":278,"address":4308816,"length":1,"stats":{"Line":1}},{"line":279,"address":4308826,"length":1,"stats":{"Line":1}},{"line":280,"address":4308866,"length":1,"stats":{"Line":1}},{"line":281,"address":4308902,"length":1,"stats":{"Line":1}},{"line":283,"address":4309160,"length":1,"stats":{"Line":1}},{"line":284,"address":4309199,"length":1,"stats":{"Line":1}},{"line":288,"address":4309483,"length":1,"stats":{"Line":1}},{"line":292,"address":4310144,"length":1,"stats":{"Line":2}},{"line":293,"address":4310151,"length":1,"stats":{"Line":1}},{"line":294,"address":4310305,"length":1,"stats":{"Line":1}},{"line":301,"address":4310724,"length":1,"stats":{"Line":0}},{"line":304,"address":4310764,"length":1,"stats":{"Line":1}},{"line":305,"address":4310917,"length":1,"stats":{"Line":1}},{"line":309,"address":4311104,"length":1,"stats":{"Line":2}},{"line":310,"address":4311114,"length":1,"stats":{"Line":1}},{"line":312,"address":4311154,"length":1,"stats":{"Line":1}},{"line":314,"address":4311168,"length":1,"stats":{"Line":1}},{"line":315,"address":4311175,"length":1,"stats":{"Line":1}},{"line":318,"address":4311341,"length":1,"stats":{"Line":1}},{"line":322,"address":4311488,"length":1,"stats":{"Line":2}},{"line":323,"address":4311495,"length":1,"stats":{"Line":1}},{"line":325,"address":4311522,"length":1,"stats":{"Line":1}},{"line":327,"address":4311539,"length":1,"stats":{"Line":1}},{"line":328,"address":4311546,"length":1,"stats":{"Line":1}},{"line":331,"address":4311806,"length":1,"stats":{"Line":1}},{"line":335,"address":4311727,"length":1,"stats":{"Line":1}},{"line":340,"address":4312272,"length":1,"stats":{"Line":2}},{"line":341,"address":4312279,"length":1,"stats":{"Line":1}},{"line":343,"address":4312306,"length":1,"stats":{"Line":1}},{"line":345,"address":4312326,"length":1,"stats":{"Line":1}},{"line":346,"address":4312333,"length":1,"stats":{"Line":1}},{"line":349,"address":4312638,"length":1,"stats":{"Line":1}},{"line":354,"address":4312550,"length":1,"stats":{"Line":1}},{"line":357,"address":4313045,"length":1,"stats":{"Line":1}},{"line":359,"address":4313065,"length":1,"stats":{"Line":1}},{"line":360,"address":4313072,"length":1,"stats":{"Line":1}},{"line":363,"address":4313495,"length":1,"stats":{"Line":1}},{"line":368,"address":4313407,"length":1,"stats":{"Line":1}},{"line":373,"address":4314016,"length":1,"stats":{"Line":2}},{"line":374,"address":4314023,"length":1,"stats":{"Line":1}},{"line":376,"address":4314050,"length":1,"stats":{"Line":1}},{"line":378,"address":4314067,"length":1,"stats":{"Line":1}},{"line":379,"address":4314074,"length":1,"stats":{"Line":1}},{"line":382,"address":4314361,"length":1,"stats":{"Line":1}},{"line":387,"address":4314282,"length":1,"stats":{"Line":1}},{"line":392,"address":4314832,"length":1,"stats":{"Line":2}},{"line":393,"address":4314839,"length":1,"stats":{"Line":1}},{"line":395,"address":4314866,"length":1,"stats":{"Line":1}},{"line":397,"address":4314883,"length":1,"stats":{"Line":1}},{"line":398,"address":4314890,"length":1,"stats":{"Line":1}},{"line":401,"address":4315313,"length":1,"stats":{"Line":1}},{"line":406,"address":4315234,"length":1,"stats":{"Line":1}},{"line":411,"address":4315808,"length":1,"stats":{"Line":2}},{"line":412,"address":4315815,"length":1,"stats":{"Line":1}},{"line":414,"address":4315842,"length":1,"stats":{"Line":1}},{"line":416,"address":4315859,"length":1,"stats":{"Line":1}},{"line":417,"address":4315866,"length":1,"stats":{"Line":1}},{"line":420,"address":4316160,"length":1,"stats":{"Line":1}},{"line":425,"address":4316081,"length":1,"stats":{"Line":1}},{"line":430,"address":4316640,"length":1,"stats":{"Line":2}},{"line":431,"address":4316647,"length":1,"stats":{"Line":1}},{"line":433,"address":4316674,"length":1,"stats":{"Line":1}},{"line":435,"address":4316691,"length":1,"stats":{"Line":1}},{"line":436,"address":4316698,"length":1,"stats":{"Line":1}},{"line":439,"address":4317121,"length":1,"stats":{"Line":1}},{"line":444,"address":4317042,"length":1,"stats":{"Line":1}},{"line":449,"address":4317616,"length":1,"stats":{"Line":2}},{"line":450,"address":4317623,"length":1,"stats":{"Line":1}},{"line":452,"address":4317650,"length":1,"stats":{"Line":1}},{"line":454,"address":4317667,"length":1,"stats":{"Line":1}},{"line":455,"address":4317674,"length":1,"stats":{"Line":1}},{"line":458,"address":4318105,"length":1,"stats":{"Line":1}},{"line":463,"address":4318026,"length":1,"stats":{"Line":1}},{"line":468,"address":4318608,"length":1,"stats":{"Line":2}},{"line":469,"address":4318618,"length":1,"stats":{"Line":1}},{"line":471,"address":4318658,"length":1,"stats":{"Line":1}},{"line":473,"address":4318672,"length":1,"stats":{"Line":1}},{"line":474,"address":4318679,"length":1,"stats":{"Line":1}},{"line":477,"address":4318879,"length":1,"stats":{"Line":1}}],"covered":132,"coverable":141},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","rules-engine","src","rule.rs"],"content":"// Copyright (C) 2019 Éloïs SANCHEZ\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Rules engine : rules\n\nuse crate::{EngineError, ProtocolVersion};\nuse failure::Fail;\nuse std::collections::BTreeMap;\nuse std::fmt::Debug;\n\n#[derive(Copy, Clone, Debug, Ord, PartialEq, PartialOrd, Eq, Hash)]\n/// Rule number\npub struct RuleNumber(pub usize);\n\nimpl std::fmt::Display for RuleNumber {\n fn fmt(\u0026self, f: \u0026mut std::fmt::Formatter) -\u003e std::fmt::Result {\n write!(f, \"{}\", self.0)\n }\n}\n\n/// Rule error\n#[derive(Debug, Eq, Fail, PartialEq)]\n#[fail(display = \"An error occurred with rule n°{} : {}\", rule_number, cause)]\npub struct RuleError\u003cE: Eq + Fail + PartialEq\u003e {\n /// Rule number\n pub rule_number: RuleNumber,\n /// Cause of the error\n pub cause: E,\n}\n\n/// Rule immutable execution function\npub type RuleFnRef\u003cD, E\u003e = fn(\u0026D) -\u003e Result\u003c(), E\u003e;\n\n/// Rule mutable execution function\npub type RuleFnRefMut\u003cD, E\u003e = fn(\u0026mut D) -\u003e Result\u003c(), E\u003e;\n\n/// Rule execution function\npub enum RuleFn\u003cD, E\u003e {\n Ref(RuleFnRef\u003cD, E\u003e),\n RefMut(RuleFnRefMut\u003cD, E\u003e),\n}\n\n#[derive(Debug, Copy, Clone, Eq, Fail, PartialEq)]\n#[fail(\n display = \"Fatal error: rules-engine: try to create rule n°{} without implementation !\",\n rule_number\n)]\npub struct RuleWithoutImpl {\n pub rule_number: RuleNumber,\n}\n\n/// Rule\npub struct Rule\u003cD: Debug, E: Eq + Fail + PartialEq\u003e {\n /// Dictionary of the different versions of the rule execution function\n rule_versions: BTreeMap\u003cProtocolVersion, RuleFn\u003cD, E\u003e\u003e,\n}\n\nimpl\u003cD: Debug, E: Eq + Fail + PartialEq\u003e Rule\u003cD, E\u003e {\n /// Create new rule\n pub fn new(\n rule_number: RuleNumber,\n rule_versions: BTreeMap\u003cProtocolVersion, RuleFn\u003cD, E\u003e\u003e,\n ) -\u003e Result\u003cSelf, RuleWithoutImpl\u003e {\n if rule_versions.is_empty() {\n Err(RuleWithoutImpl { rule_number })\n } else {\n Ok(Rule { rule_versions })\n }\n }\n /// Executes the correct version of the rule\n pub fn execute(\n \u0026self,\n protocol_version: ProtocolVersion,\n rule_number: RuleNumber,\n rule_datas: \u0026D,\n ) -\u003e Result\u003c(), EngineError\u003cE\u003e\u003e {\n let rule_opt: Option\u003c(\u0026ProtocolVersion, \u0026RuleFn\u003cD, E\u003e)\u003e =\n self.rule_versions.range(..=protocol_version).last();\n if let Some((_, rule_fn)) = rule_opt {\n match rule_fn {\n RuleFn::Ref(rule_fn_ref) =\u003e rule_fn_ref(rule_datas).map_err(|err| {\n EngineError::RuleError(RuleError {\n rule_number,\n cause: err,\n })\n }),\n RuleFn::RefMut(_) =\u003e Err(EngineError::MutRuleInPar {\n rule_number,\n protocol_version,\n }),\n }\n } else {\n Err(EngineError::RuleTooRecent {\n rule_number,\n protocol_version,\n })\n }\n }\n /// Executes the correct version of the rule\n pub fn execute_mut(\n \u0026self,\n protocol_version: ProtocolVersion,\n rule_number: RuleNumber,\n rule_datas: \u0026mut D,\n ) -\u003e Result\u003c(), EngineError\u003cE\u003e\u003e {\n let rule_opt: Option\u003c(\u0026ProtocolVersion, \u0026RuleFn\u003cD, E\u003e)\u003e =\n self.rule_versions.range(..=protocol_version).last();\n if let Some((_, rule_fn)) = rule_opt {\n match rule_fn {\n RuleFn::Ref(rule_fn_ref) =\u003e rule_fn_ref(rule_datas),\n RuleFn::RefMut(rule_fn_ref_mut) =\u003e rule_fn_ref_mut(rule_datas),\n }\n .map_err(|err| {\n EngineError::RuleError(RuleError {\n rule_number,\n cause: err,\n })\n })\n } else {\n Err(EngineError::RuleTooRecent {\n rule_number,\n protocol_version,\n })\n }\n }\n}\n","traces":[{"line":28,"address":null,"length":0,"stats":{"Line":1}},{"line":29,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":77,"address":null,"length":0,"stats":{"Line":1}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":1}},{"line":83,"address":null,"length":0,"stats":{"Line":2}},{"line":89,"address":null,"length":0,"stats":{"Line":0}},{"line":90,"address":null,"length":0,"stats":{"Line":2}},{"line":91,"address":null,"length":0,"stats":{"Line":2}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":3}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":0}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":0}},{"line":105,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":2}},{"line":126,"address":null,"length":0,"stats":{"Line":1}},{"line":127,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":0}},{"line":131,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":1}},{"line":133,"address":null,"length":0,"stats":{"Line":1}},{"line":134,"address":null,"length":0,"stats":{"Line":1}}],"covered":29,"coverable":36},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","data","mod.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide data structures to manage web of trusts.\n//! `LegacyWebOfTrust` is almost a translation of the legacy C++ coden while\n//! `RustyWebOfTrust` is a brand new implementation with a more \"rusty\" style.\n\npub mod rusty;\n\nuse serde::de::{self, Deserialize, DeserializeOwned, Deserializer, Visitor};\nuse serde::{Serialize, Serializer};\nuse std::fmt::{self, Debug};\n\n/// Wrapper for a node id.\n#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]\npub struct NodeId(pub usize);\n\nimpl Serialize for NodeId {\n fn serialize\u003cS\u003e(\u0026self, serializer: S) -\u003e Result\u003cS::Ok, S::Error\u003e\n where\n S: Serializer,\n {\n serializer.serialize_u32(self.0 as u32)\n }\n}\n\nstruct NodeIdVisitor;\n\nimpl\u003c'de\u003e Visitor\u003c'de\u003e for NodeIdVisitor {\n type Value = NodeId;\n\n fn expecting(\u0026self, formatter: \u0026mut fmt::Formatter) -\u003e fmt::Result {\n formatter.write_str(\"an integer between -2^31 and 2^31\")\n }\n\n fn visit_u8\u003cE\u003e(self, value: u8) -\u003e Result\u003cNodeId, E\u003e\n where\n E: de::Error,\n {\n Ok(NodeId(value as usize))\n }\n\n fn visit_u32\u003cE\u003e(self, value: u32) -\u003e Result\u003cNodeId, E\u003e\n where\n E: de::Error,\n {\n Ok(NodeId(value as usize))\n }\n\n fn visit_u64\u003cE\u003e(self, value: u64) -\u003e Result\u003cNodeId, E\u003e\n where\n E: de::Error,\n {\n use std::usize;\n if value \u003e= usize::MIN as u64 \u0026\u0026 value \u003c= usize::MAX as u64 {\n Ok(NodeId(value as usize))\n } else {\n Err(E::custom(format!(\"u32 out of range: {}\", value)))\n }\n }\n}\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for NodeId {\n fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cNodeId, D::Error\u003e\n where\n D: Deserializer\u003c'de\u003e,\n {\n deserializer.deserialize_u32(NodeIdVisitor)\n }\n}\n\n/// Results of a certification, with the current certification count\n/// of the destination as parameter.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum NewLinkResult {\n /// Certification worked.\n Ok(usize),\n /// All available certifications has been used.\n AllCertificationsUsed(usize),\n /// Unknown source.\n UnknownSource(),\n /// Unknown target.\n UnknownTarget(),\n /// Self linking is forbidden.\n SelfLinkingForbidden(),\n}\n\n/// Results of a certification removal, with the current certification count\n/// of the destination as parameter.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum RemLinkResult {\n /// Certification has been removed.\n Removed(usize),\n /// Requested certification doesn't exist.\n UnknownCert(usize),\n /// Unknown source.\n UnknownSource(),\n /// Unknown target.\n UnknownTarget(),\n}\n\n/// Results of a certification test.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub enum HasLinkResult {\n /// Both nodes are known, here is the result.\n Link(bool),\n /// Unknown source.\n UnknownSource(),\n /// Unknown target.\n UnknownTarget(),\n}\n\n/// Trait for a Web Of Trust.\n/// Allow to provide other implementations of the `WoT` logic instead of the legacy C++\n/// translated one.\npub trait WebOfTrust: Clone + Debug + Default + DeserializeOwned + Send + Serialize + Sync {\n /// Create a new Web of Trust with the maximum of links a node can issue.\n fn new(max_links: usize) -\u003e Self;\n\n /// Get the maximum number of links per user.\n fn get_max_link(\u0026self) -\u003e usize;\n\n /// Set the maximum number of links per user.\n fn set_max_link(\u0026mut self, max_link: usize);\n\n /// Add a new node.\n fn add_node(\u0026mut self) -\u003e NodeId;\n\n /// Remove the last node.\n /// Returns `None` if the WoT was empty, otherwise new top node id.\n fn rem_node(\u0026mut self) -\u003e Option\u003cNodeId\u003e;\n\n /// Get the size of the WoT.\n fn size(\u0026self) -\u003e usize;\n\n /// Check if given node is enabled.\n /// Returns `None` if this node doesn't exist.\n fn is_enabled(\u0026self, id: NodeId) -\u003e Option\u003cbool\u003e;\n\n /// Set the enabled state of given node.\n /// Returns `Null` if this node doesn't exist, `enabled` otherwise.\n fn set_enabled(\u0026mut self, id: NodeId, enabled: bool) -\u003e Option\u003cbool\u003e;\n\n /// Get enabled node array.\n fn get_enabled(\u0026self) -\u003e Vec\u003cNodeId\u003e;\n\n /// Get disabled node array.\n fn get_disabled(\u0026self) -\u003e Vec\u003cNodeId\u003e;\n\n /// Try to add a link from the source to the target.\n fn add_link(\u0026mut self, source: NodeId, target: NodeId) -\u003e NewLinkResult;\n\n /// Try to remove a link from the source to the target.\n fn rem_link(\u0026mut self, source: NodeId, target: NodeId) -\u003e RemLinkResult;\n\n /// Test if there is a link from the source to the target.\n fn has_link(\u0026self, source: NodeId, target: NodeId) -\u003e HasLinkResult;\n\n /// Get the list of links source for this target.\n /// Returns `None` if this node doesn't exist.\n fn get_links_source(\u0026self, target: NodeId) -\u003e Option\u003cVec\u003cNodeId\u003e\u003e;\n\n /// Get the number of issued links by a node.\n /// Returns `None` if this node doesn't exist.\n fn issued_count(\u0026self, id: NodeId) -\u003e Option\u003cusize\u003e;\n\n /// Test if a node is a sentry.\n fn is_sentry(\u0026self, node: NodeId, sentry_requirement: usize) -\u003e Option\u003cbool\u003e;\n\n /// Get sentries array.\n fn get_sentries(\u0026self, sentry_requirement: usize) -\u003e Vec\u003cNodeId\u003e;\n\n /// Get non sentries array.\n fn get_non_sentries(\u0026self, sentry_requirement: usize) -\u003e Vec\u003cNodeId\u003e;\n}\n","traces":[{"line":31,"address":4756432,"length":1,"stats":{"Line":0}},{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":0}},{"line":45,"address":null,"length":0,"stats":{"Line":0}},{"line":52,"address":null,"length":0,"stats":{"Line":0}},{"line":55,"address":4757392,"length":1,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":67,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":0}},{"line":69,"address":null,"length":0,"stats":{"Line":0}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":76,"address":4757200,"length":1,"stats":{"Line":0}},{"line":80,"address":null,"length":0,"stats":{"Line":0}}],"covered":0,"coverable":13},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","data","rusty.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Experimental implementation of the Web of Trust in a more \"rusty\" style.\n\nuse super::{HasLinkResult, NewLinkResult, RemLinkResult};\nuse crate::NodeId;\nuse crate::WebOfTrust;\nuse rayon::prelude::*;\nuse std::collections::HashSet;\n\n/// A node in the `WoT` graph.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\nstruct Node {\n /// Is this node enabled ?\n enabled: bool,\n /// Set of links this node is the target.\n links_source: HashSet\u003cNodeId\u003e,\n /// Number of links the node issued.\n issued_count: usize,\n}\n\nimpl Node {\n /// Create a new node.\n pub fn new() -\u003e Node {\n Node {\n enabled: true,\n links_source: HashSet::new(),\n issued_count: 0,\n }\n }\n}\n\n/// A more idiomatic implementation of a Web of Trust.\n#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]\npub struct RustyWebOfTrust {\n /// List of nodes in the WoT.\n nodes: Vec\u003cNode\u003e,\n /// Maximum number of links a node can issue.\n max_links: usize,\n}\n\nimpl Default for RustyWebOfTrust {\n fn default() -\u003e RustyWebOfTrust {\n RustyWebOfTrust {\n nodes: Vec::new(),\n max_links: 4_000_000_000,\n }\n }\n}\n\nimpl WebOfTrust for RustyWebOfTrust {\n fn new(max_links: usize) -\u003e RustyWebOfTrust {\n RustyWebOfTrust {\n nodes: vec![],\n max_links,\n }\n }\n\n fn get_max_link(\u0026self) -\u003e usize {\n self.max_links\n }\n\n fn set_max_link(\u0026mut self, max_links: usize) {\n self.max_links = max_links;\n }\n\n fn add_node(\u0026mut self) -\u003e NodeId {\n self.nodes.push(Node::new());\n NodeId(self.nodes.len() - 1)\n }\n\n fn rem_node(\u0026mut self) -\u003e Option\u003cNodeId\u003e {\n self.nodes.pop();\n\n if !self.nodes.is_empty() {\n Some(NodeId(self.nodes.len() - 1))\n } else {\n None\n }\n }\n\n fn size(\u0026self) -\u003e usize {\n self.nodes.len()\n }\n\n fn is_enabled(\u0026self, id: NodeId) -\u003e Option\u003cbool\u003e {\n self.nodes.get(id.0).map(|n| n.enabled)\n }\n\n fn set_enabled(\u0026mut self, id: NodeId, enabled: bool) -\u003e Option\u003cbool\u003e {\n self.nodes\n .get_mut(id.0)\n .map(|n| n.enabled = enabled)\n .map(|_| enabled)\n }\n\n fn get_enabled(\u0026self) -\u003e Vec\u003cNodeId\u003e {\n self.nodes\n .par_iter()\n .enumerate()\n .filter(|\u0026(_, n)| n.enabled)\n .map(|(i, _)| NodeId(i))\n .collect()\n }\n\n fn get_disabled(\u0026self) -\u003e Vec\u003cNodeId\u003e {\n self.nodes\n .par_iter()\n .enumerate()\n .filter(|\u0026(_, n)| !n.enabled)\n .map(|(i, _)| NodeId(i))\n .collect()\n }\n\n fn add_link(\u0026mut self, source: NodeId, target: NodeId) -\u003e NewLinkResult {\n if source == target {\n NewLinkResult::SelfLinkingForbidden()\n } else if source.0 \u003e= self.size() {\n NewLinkResult::UnknownSource()\n } else if target.0 \u003e= self.size() {\n NewLinkResult::UnknownTarget()\n } else if self.nodes[source.0].issued_count \u003e= self.max_links {\n NewLinkResult::AllCertificationsUsed(self.nodes[target.0].links_source.len())\n } else {\n self.nodes[source.0].issued_count += 1;\n self.nodes[target.0].links_source.insert(source);\n NewLinkResult::Ok(self.nodes[target.0].links_source.len())\n }\n }\n\n fn rem_link(\u0026mut self, source: NodeId, target: NodeId) -\u003e RemLinkResult {\n if source.0 \u003e= self.size() {\n RemLinkResult::UnknownSource()\n } else if target.0 \u003e= self.size() {\n RemLinkResult::UnknownTarget()\n } else if !self.nodes[target.0].links_source.contains(\u0026source) {\n RemLinkResult::UnknownCert(self.nodes[target.0].links_source.len())\n } else {\n self.nodes[source.0].issued_count -= 1;\n self.nodes[target.0].links_source.remove(\u0026source);\n RemLinkResult::Removed(self.nodes[target.0].links_source.len())\n }\n }\n\n fn has_link(\u0026self, source: NodeId, target: NodeId) -\u003e HasLinkResult {\n if source.0 \u003e= self.size() {\n HasLinkResult::UnknownSource()\n } else if target.0 \u003e= self.size() {\n HasLinkResult::UnknownTarget()\n } else {\n HasLinkResult::Link(self.nodes[target.0].links_source.contains(\u0026source))\n }\n }\n\n fn get_links_source(\u0026self, target: NodeId) -\u003e Option\u003cVec\u003cNodeId\u003e\u003e {\n self.nodes\n .get(target.0)\n .map(|n| n.links_source.iter().cloned().collect())\n }\n\n fn issued_count(\u0026self, id: NodeId) -\u003e Option\u003cusize\u003e {\n self.nodes.get(id.0).map(|n| n.issued_count)\n }\n\n fn is_sentry(\u0026self, node: NodeId, sentry_requirement: usize) -\u003e Option\u003cbool\u003e {\n if node.0 \u003e= self.size() {\n return None;\n }\n\n let node = \u0026self.nodes[node.0];\n\n Some(\n node.enabled\n \u0026\u0026 node.issued_count \u003e= sentry_requirement\n \u0026\u0026 node.links_source.len() \u003e= sentry_requirement,\n )\n }\n\n fn get_sentries(\u0026self, sentry_requirement: usize) -\u003e Vec\u003cNodeId\u003e {\n self.nodes\n .par_iter()\n .enumerate()\n .filter(|\u0026(_, n)| {\n n.enabled\n \u0026\u0026 n.issued_count \u003e= sentry_requirement\n \u0026\u0026 n.links_source.len() \u003e= sentry_requirement\n })\n .map(|(i, _)| NodeId(i))\n .collect()\n }\n\n fn get_non_sentries(\u0026self, sentry_requirement: usize) -\u003e Vec\u003cNodeId\u003e {\n self.nodes\n .par_iter()\n .enumerate()\n .filter(|\u0026(_, n)| {\n n.enabled\n \u0026\u0026 (n.issued_count \u003c sentry_requirement\n || n.links_source.len() \u003c sentry_requirement)\n })\n .map(|(i, _)| NodeId(i))\n .collect()\n }\n}\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::tests::generic_wot_test;\n\n #[test]\n fn wot_tests() {\n generic_wot_test::\u003cRustyWebOfTrust\u003e();\n }\n}\n","traces":[{"line":37,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":null,"length":0,"stats":{"Line":0}},{"line":58,"address":null,"length":0,"stats":{"Line":0}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":67,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":77,"address":null,"length":0,"stats":{"Line":1}},{"line":80,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":1}},{"line":85,"address":null,"length":0,"stats":{"Line":1}},{"line":86,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":0}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":2}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":2}},{"line":110,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":1}},{"line":129,"address":null,"length":0,"stats":{"Line":1}},{"line":131,"address":null,"length":0,"stats":{"Line":1}},{"line":133,"address":null,"length":0,"stats":{"Line":1}},{"line":135,"address":null,"length":0,"stats":{"Line":1}},{"line":136,"address":null,"length":0,"stats":{"Line":1}},{"line":137,"address":null,"length":0,"stats":{"Line":0}},{"line":138,"address":null,"length":0,"stats":{"Line":1}},{"line":139,"address":null,"length":0,"stats":{"Line":1}},{"line":140,"address":null,"length":0,"stats":{"Line":1}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":147,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":0}},{"line":151,"address":null,"length":0,"stats":{"Line":0}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":0}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":169,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":1}},{"line":174,"address":null,"length":0,"stats":{"Line":0}},{"line":175,"address":null,"length":0,"stats":{"Line":0}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":0}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":188,"address":null,"length":0,"stats":{"Line":0}},{"line":192,"address":null,"length":0,"stats":{"Line":1}},{"line":193,"address":null,"length":0,"stats":{"Line":1}},{"line":205,"address":null,"length":0,"stats":{"Line":1}},{"line":206,"address":null,"length":0,"stats":{"Line":1}},{"line":225,"address":4222432,"length":1,"stats":{"Line":2}},{"line":226,"address":4227249,"length":1,"stats":{"Line":1}}],"covered":60,"coverable":72},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","lib.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! `wot` is a crate making \"Web of Trust\" computations for\n//! the [Duniter] project.\n//!\n//! [Duniter]: https://duniter.org/\n//!\n//! It defines a trait representing a Web of Trust and allow to do calculations on it.\n//!\n//! It also contains an \"legacy\" implementation translated from the original C++ code.\n//!\n//! Web of Trust tests are translated from [duniter/wot Javascript test][js-tests].\n//!\n//! [js-tests]: https://github.com/duniter/wot/blob/master/wotcpp/webOfTrust.cpp\n\n#![deny(\n missing_docs,\n missing_debug_implementations,\n missing_copy_implementations,\n trivial_casts,\n trivial_numeric_casts,\n unsafe_code,\n unstable_features,\n unused_import_braces,\n unused_qualifications\n)]\n\n#[macro_use]\nextern crate serde_derive;\n#[macro_use]\nextern crate log;\n\npub mod data;\npub mod operations;\n\npub use crate::data::{NodeId, WebOfTrust};\n\n#[cfg(test)]\nmod tests {\n use super::*;\n use crate::data::*;\n use crate::operations::centrality::*;\n use crate::operations::distance::*;\n use crate::operations::file::*;\n use crate::operations::path::*;\n\n /// Test translated from https://github.com/duniter/wot/blob/master/tests/test.js\n ///\n /// Clone and file tests are not included in this generic test and should be done in\n /// the implementation test.\n pub fn generic_wot_test\u003cW\u003e()\n where\n W: WebOfTrust + Sync,\n {\n let centralities_calculator = UlrikBrandesCentralityCalculator {};\n let distance_calculator = RustyDistanceCalculator {};\n let path_finder = RustyPathFinder {};\n let mut wot = W::new(3);\n\n // should have an initial size of 0\n assert_eq!(wot.size(), 0);\n\n // should return `None()` if testing `is_enabled()` with out-of-bounds node\n assert_eq!(wot.is_enabled(NodeId(0)), None);\n assert_eq!(wot.is_enabled(NodeId(23)), None);\n\n // should give nomber 0 if we add a node\n // - add a node\n assert_eq!(wot.add_node(), NodeId(0));\n assert_eq!(wot.size(), 1);\n assert_eq!(wot.get_disabled().len(), 0);\n\n // - add another\n assert_eq!(wot.add_node(), NodeId(1));\n assert_eq!(wot.size(), 2);\n assert_eq!(wot.get_disabled().len(), 0);\n\n // - add 10 nodes\n for i in 0..10 {\n assert_eq!(wot.add_node(), NodeId(i + 2));\n }\n\n assert_eq!(wot.size(), 12);\n\n // shouldn't be able to self cert\n assert_eq!(\n wot.add_link(NodeId(0), NodeId(0)),\n NewLinkResult::SelfLinkingForbidden()\n );\n\n // should add certs only in the boundaries of max_cert\n assert_eq!(wot.add_link(NodeId(0), NodeId(1)), NewLinkResult::Ok(1));\n assert_eq!(wot.add_link(NodeId(0), NodeId(2)), NewLinkResult::Ok(1));\n assert_eq!(wot.add_link(NodeId(0), NodeId(3)), NewLinkResult::Ok(1));\n assert_eq!(\n wot.add_link(NodeId(0), NodeId(4)),\n NewLinkResult::AllCertificationsUsed(0)\n );\n\n assert_eq!(wot.get_max_link(), 3);\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(1)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(2)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(3)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(4)),\n HasLinkResult::Link(false)\n );\n\n wot.set_max_link(4);\n assert_eq!(wot.get_max_link(), 4);\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(4)),\n HasLinkResult::Link(false)\n );\n wot.add_link(NodeId(0), NodeId(4));\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(4)),\n HasLinkResult::Link(true)\n );\n wot.rem_link(NodeId(0), NodeId(1));\n wot.rem_link(NodeId(0), NodeId(2));\n wot.rem_link(NodeId(0), NodeId(3));\n wot.rem_link(NodeId(0), NodeId(4));\n\n // false when not linked + test out of bounds\n assert_eq!(\n wot.has_link(NodeId(0), NodeId(6)),\n HasLinkResult::Link(false)\n );\n assert_eq!(\n wot.has_link(NodeId(23), NodeId(0)),\n HasLinkResult::UnknownSource()\n );\n assert_eq!(\n wot.has_link(NodeId(2), NodeId(53)),\n HasLinkResult::UnknownTarget()\n );\n\n // created nodes should be enabled\n assert_eq!(wot.is_enabled(NodeId(0)), Some(true));\n assert_eq!(wot.is_enabled(NodeId(1)), Some(true));\n assert_eq!(wot.is_enabled(NodeId(2)), Some(true));\n assert_eq!(wot.is_enabled(NodeId(3)), Some(true));\n assert_eq!(wot.is_enabled(NodeId(11)), Some(true));\n\n // should be able to disable some nodes\n assert_eq!(wot.set_enabled(NodeId(0), false), Some(false));\n assert_eq!(wot.set_enabled(NodeId(1), false), Some(false));\n assert_eq!(wot.set_enabled(NodeId(2), false), Some(false));\n assert_eq!(wot.get_disabled().len(), 3);\n assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));\n\n // node 0 and 2 should be disabled\n assert_eq!(wot.is_enabled(NodeId(0)), Some(false));\n assert_eq!(wot.is_enabled(NodeId(1)), Some(true));\n assert_eq!(wot.is_enabled(NodeId(2)), Some(false));\n assert_eq!(wot.is_enabled(NodeId(3)), Some(true));\n // - set enabled again\n assert_eq!(wot.set_enabled(NodeId(0), true), Some(true));\n assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));\n assert_eq!(wot.set_enabled(NodeId(2), true), Some(true));\n assert_eq!(wot.set_enabled(NodeId(1), true), Some(true));\n assert_eq!(wot.get_disabled().len(), 0);\n\n // should not exist a link from 2 to 0\n assert_eq!(\n wot.has_link(NodeId(2), NodeId(0)),\n HasLinkResult::Link(false)\n );\n\n // should be able to add some links, cert count is returned\n assert_eq!(wot.add_link(NodeId(2), NodeId(0)), NewLinkResult::Ok(1));\n assert_eq!(wot.add_link(NodeId(4), NodeId(0)), NewLinkResult::Ok(2));\n assert_eq!(wot.add_link(NodeId(5), NodeId(0)), NewLinkResult::Ok(3));\n\n // should exist new links\n /* WoT is:\n *\n * 2 --\u003e 0\n * 4 --\u003e 0\n * 5 --\u003e 0\n */\n\n assert_eq!(\n wot.has_link(NodeId(2), NodeId(0)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(4), NodeId(0)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(5), NodeId(0)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(2), NodeId(1)),\n HasLinkResult::Link(false)\n );\n\n // should be able to remove some links\n assert_eq!(\n wot.rem_link(NodeId(4), NodeId(0)),\n RemLinkResult::Removed(2)\n );\n /*\n * WoT is now:\n *\n * 2 --\u003e 0\n * 5 --\u003e 0\n */\n\n // should exist less links\n assert_eq!(\n wot.has_link(NodeId(2), NodeId(0)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(4), NodeId(0)),\n HasLinkResult::Link(false)\n );\n assert_eq!(\n wot.has_link(NodeId(5), NodeId(0)),\n HasLinkResult::Link(true)\n );\n assert_eq!(\n wot.has_link(NodeId(2), NodeId(1)),\n HasLinkResult::Link(false)\n );\n\n // should successfully use distance rule\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 1,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n );\n // =\u003e no because 2,4,5 have certified him\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n );\n // =\u003e no because only member 2 has 2 certs, and has certified him\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 3,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n );\n // =\u003e no because no member has issued 3 certifications\n\n // - we add links from member 3\n assert_eq!(wot.add_link(NodeId(3), NodeId(1)), NewLinkResult::Ok(1));\n assert_eq!(wot.add_link(NodeId(3), NodeId(2)), NewLinkResult::Ok(1));\n /*\n * WoT is now:\n *\n * 2 --\u003e 0\n * 5 --\u003e 0\n * 3 --\u003e 1\n * 3 --\u003e 2\n */\n assert_eq!(wot.size(), 12);\n assert_eq!(wot.get_sentries(1).len(), 1);\n assert_eq!(wot.get_sentries(1)[0], NodeId(2));\n assert_eq!(wot.get_sentries(2).len(), 0);\n assert_eq!(wot.get_sentries(3).len(), 0);\n assert_eq!(wot.get_non_sentries(1).len(), 11); // 12 - 1\n assert_eq!(wot.get_non_sentries(2).len(), 12); // 12 - 0\n assert_eq!(wot.get_non_sentries(3).len(), 12); // 12 - 0\n assert_eq!(\n path_finder.find_paths(\u0026wot, NodeId(3), NodeId(0), 1).len(),\n 0\n ); // KO\n assert_eq!(\n path_finder.find_paths(\u0026wot, NodeId(3), NodeId(0), 2).len(),\n 1\n ); // It exists 3 -\u003e 2 -\u003e 0\n assert!(path_finder\n .find_paths(\u0026wot, NodeId(3), NodeId(0), 2)\n .contains(\u0026vec![NodeId(3), NodeId(2), NodeId(0)]));\n\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 1,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : 2 -\u003e 0\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : 2 -\u003e 0\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 3,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : no stry \\w 3 lnk\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 2,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : 2 -\u003e 0\n\n wot.add_link(NodeId(1), NodeId(3));\n wot.add_link(NodeId(2), NodeId(3));\n\n assert_eq!(wot.size(), 12);\n assert_eq!(wot.get_sentries(1).len(), 3);\n assert_eq!(wot.get_sentries(1)[0], NodeId(1));\n assert_eq!(wot.get_sentries(1)[1], NodeId(2));\n assert_eq!(wot.get_sentries(1)[2], NodeId(3));\n\n assert_eq!(wot.get_sentries(2).len(), 1);\n assert_eq!(wot.get_sentries(2)[0], NodeId(3));\n assert_eq!(wot.get_sentries(3).len(), 0);\n assert_eq!(wot.get_non_sentries(1).len(), 9); // 12 - 3\n assert_eq!(wot.get_non_sentries(2).len(), 11); // 12 - 1\n assert_eq!(wot.get_non_sentries(3).len(), 12); // 12 - 0\n assert_eq!(\n path_finder.find_paths(\u0026wot, NodeId(3), NodeId(0), 1).len(),\n 0\n ); // KO\n assert_eq!(\n path_finder.find_paths(\u0026wot, NodeId(3), NodeId(0), 2).len(),\n 1\n ); // It exists 3 -\u003e 2 -\u003e 0\n assert!(path_finder\n .find_paths(\u0026wot, NodeId(3), NodeId(0), 2)\n .contains(\u0026vec![NodeId(3), NodeId(2), NodeId(0)]));\n\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 1,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(true)\n ); // KO : No path 3 -\u003e 0\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(true)\n ); // KO : No path 3 -\u003e 0\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 3,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : no stry \\w 3 lnk\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 2,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : 3 -\u003e 2 -\u003e 0\n\n // should have 12 nodes\n assert_eq!(wot.size(), 12);\n\n // delete top node (return new top node id)\n assert_eq!(wot.rem_node(), Some(NodeId(10)));\n\n // should have 11 nodes\n assert_eq!(wot.size(), 11);\n\n // should work with member 3 disabled\n // - with member 3 disabled (non-member)\n assert_eq!(wot.set_enabled(NodeId(3), false), Some(false));\n assert_eq!(wot.get_disabled().len(), 1);\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n ); // OK : Disabled\n\n let file_formater = BinaryFileFormater {};\n\n // Write wot in file\n assert_eq!(\n file_formater\n .to_file(\n \u0026wot,\n \u0026[0b0000_0000, 0b0000_0001, 0b0000_0001, 0b0000_0000],\n \"test.wot\"\n )\n .unwrap(),\n ()\n );\n\n let (wot2, blockstamp2) = file_formater.from_file::\u003cW\u003e(\"test.wot\", 3).unwrap();\n\n // Read wot from file\n {\n assert_eq!(\n blockstamp2,\n vec![0b0000_0000, 0b0000_0001, 0b0000_0001, 0b0000_0000]\n );\n assert_eq!(wot.size(), wot2.size());\n assert_eq!(\n wot.get_non_sentries(1).len(),\n wot2.get_non_sentries(1).len()\n );\n assert_eq!(wot.get_disabled().len(), wot2.get_disabled().len());\n assert_eq!(wot2.get_disabled().len(), 1);\n assert_eq!(wot2.is_enabled(NodeId(3)), Some(false));\n assert_eq!(\n distance_calculator.is_outdistanced(\n \u0026wot2,\n WotDistanceParameters {\n node: NodeId(0),\n sentry_requirement: 2,\n step_max: 1,\n x_percent: 1.0,\n },\n ),\n Some(false)\n );\n }\n\n // Read g1_genesis wot\n let (wot3, blockstamp3) = file_formater\n .from_file::\u003cW\u003e(\"tests/g1_genesis.bin\", 100)\n .unwrap();\n assert_eq!(\n blockstamp3,\n vec![\n 57, 57, 45, 48, 48, 48, 48, 49, 50, 65, 68, 52, 57, 54, 69, 67, 65, 53, 54, 68, 69,\n 48, 66, 56, 69, 53, 68, 54, 70, 55, 52, 57, 66, 55, 67, 66, 69, 55, 56, 53, 53, 51,\n 69, 54, 51, 56, 53, 51, 51, 51, 65, 52, 52, 69, 48, 52, 51, 55, 55, 69, 70, 70, 67,\n 67, 65, 53, 51,\n ]\n );\n\n // Check g1_genesis wot members_count\n let members_count = wot3.get_enabled().len() as u64;\n assert_eq!(members_count, 59);\n\n // Test compute_distance in g1_genesis wot\n assert_eq!(\n distance_calculator.compute_distance(\n \u0026wot3,\n WotDistanceParameters {\n node: NodeId(37),\n sentry_requirement: 3,\n step_max: 5,\n x_percent: 0.8,\n },\n ),\n Some(WotDistance {\n sentries: 48,\n success: 48,\n success_at_border: 3,\n reached: 51,\n reached_at_border: 3,\n outdistanced: false,\n },)\n );\n\n // Test betweenness centralities computation in g1_genesis wot\n let centralities = centralities_calculator.betweenness_centralities(\u0026wot3);\n assert_eq!(centralities.len(), 59);\n assert_eq!(\n centralities,\n vec![\n 148, 30, 184, 11, 60, 51, 40, 115, 24, 140, 47, 69, 16, 34, 94, 126, 151, 0, 34,\n 133, 20, 103, 38, 144, 73, 523, 124, 23, 47, 17, 9, 64, 77, 281, 6, 105, 54, 0,\n 111, 21, 6, 2, 0, 1, 47, 59, 28, 236, 0, 0, 0, 0, 60, 6, 0, 1, 8, 33, 169,\n ]\n );\n\n // Test stress centralities computation in g1_genesis wot\n let stress_centralities = centralities_calculator.stress_centralities(\u0026wot3);\n assert_eq!(stress_centralities.len(), 59);\n assert_eq!(\n stress_centralities,\n vec![\n 848, 240, 955, 80, 416, 203, 290, 645, 166, 908, 313, 231, 101, 202, 487, 769, 984,\n 0, 154, 534, 105, 697, 260, 700, 496, 1726, 711, 160, 217, 192, 89, 430, 636, 1276,\n 41, 420, 310, 0, 357, 125, 50, 15, 0, 12, 275, 170, 215, 1199, 0, 0, 0, 0, 201, 31,\n 0, 9, 55, 216, 865,\n ]\n );\n\n // Test distance stress centralities computation in g1_genesis wot\n let distance_stress_centralities =\n centralities_calculator.distance_stress_centralities(\u0026wot3, 5);\n assert_eq!(distance_stress_centralities.len(), 59);\n assert_eq!(\n distance_stress_centralities,\n vec![\n 848, 240, 955, 80, 416, 203, 290, 645, 166, 908, 313, 231, 101, 202, 487, 769, 984,\n 0, 154, 534, 105, 697, 260, 700, 496, 1726, 711, 160, 217, 192, 89, 430, 636, 1276,\n 41, 420, 310, 0, 357, 125, 50, 15, 0, 12, 275, 170, 215, 1199, 0, 0, 0, 0, 201, 31,\n 0, 9, 55, 216, 865,\n ]\n );\n }\n}\n","traces":[{"line":1,"address":4228614,"length":1,"stats":{"Line":0}},{"line":64,"address":4295136,"length":1,"stats":{"Line":1}},{"line":71,"address":4295149,"length":1,"stats":{"Line":1}},{"line":74,"address":4295192,"length":1,"stats":{"Line":1}},{"line":77,"address":4295483,"length":1,"stats":{"Line":1}},{"line":78,"address":4296199,"length":1,"stats":{"Line":1}},{"line":82,"address":4296712,"length":1,"stats":{"Line":1}},{"line":83,"address":4297200,"length":1,"stats":{"Line":1}},{"line":84,"address":4297489,"length":1,"stats":{"Line":1}},{"line":87,"address":4298240,"length":1,"stats":{"Line":1}},{"line":88,"address":4298728,"length":1,"stats":{"Line":1}},{"line":89,"address":4299017,"length":1,"stats":{"Line":1}},{"line":92,"address":4299760,"length":1,"stats":{"Line":1}},{"line":93,"address":4300031,"length":1,"stats":{"Line":1}},{"line":96,"address":4300054,"length":1,"stats":{"Line":1}},{"line":99,"address":4301171,"length":1,"stats":{"Line":1}},{"line":100,"address":4300849,"length":1,"stats":{"Line":1}},{"line":105,"address":4301612,"length":1,"stats":{"Line":1}},{"line":106,"address":4302164,"length":1,"stats":{"Line":1}},{"line":107,"address":4302716,"length":1,"stats":{"Line":1}},{"line":108,"address":4303379,"length":1,"stats":{"Line":1}},{"line":109,"address":4303268,"length":1,"stats":{"Line":1}},{"line":113,"address":4303828,"length":1,"stats":{"Line":1}},{"line":114,"address":4304396,"length":1,"stats":{"Line":1}},{"line":115,"address":4304101,"length":1,"stats":{"Line":1}},{"line":118,"address":4304921,"length":1,"stats":{"Line":1}},{"line":119,"address":4304837,"length":1,"stats":{"Line":1}},{"line":122,"address":4305446,"length":1,"stats":{"Line":1}},{"line":123,"address":4305362,"length":1,"stats":{"Line":1}},{"line":126,"address":4305971,"length":1,"stats":{"Line":1}},{"line":127,"address":4305887,"length":1,"stats":{"Line":1}},{"line":131,"address":4306425,"length":1,"stats":{"Line":1}},{"line":132,"address":4306440,"length":1,"stats":{"Line":1}},{"line":133,"address":4307008,"length":1,"stats":{"Line":1}},{"line":134,"address":4306713,"length":1,"stats":{"Line":1}},{"line":137,"address":4307449,"length":1,"stats":{"Line":1}},{"line":138,"address":4307604,"length":1,"stats":{"Line":1}},{"line":139,"address":4307520,"length":1,"stats":{"Line":1}},{"line":142,"address":4308045,"length":1,"stats":{"Line":1}},{"line":143,"address":4308116,"length":1,"stats":{"Line":1}},{"line":144,"address":4308187,"length":1,"stats":{"Line":1}},{"line":145,"address":4308258,"length":1,"stats":{"Line":1}},{"line":148,"address":4308413,"length":1,"stats":{"Line":1}},{"line":149,"address":4308329,"length":1,"stats":{"Line":1}},{"line":152,"address":4308938,"length":1,"stats":{"Line":1}},{"line":153,"address":4308854,"length":1,"stats":{"Line":1}},{"line":156,"address":4309463,"length":1,"stats":{"Line":1}},{"line":157,"address":4309379,"length":1,"stats":{"Line":1}},{"line":162,"address":4309904,"length":1,"stats":{"Line":1}},{"line":163,"address":4310409,"length":1,"stats":{"Line":1}},{"line":164,"address":4310914,"length":1,"stats":{"Line":1}},{"line":165,"address":4311419,"length":1,"stats":{"Line":1}},{"line":166,"address":4311924,"length":1,"stats":{"Line":1}},{"line":169,"address":4312429,"length":1,"stats":{"Line":1}},{"line":170,"address":4312936,"length":1,"stats":{"Line":1}},{"line":171,"address":4313443,"length":1,"stats":{"Line":1}},{"line":172,"address":4313966,"length":1,"stats":{"Line":1}},{"line":173,"address":4314498,"length":1,"stats":{"Line":1}},{"line":176,"address":4315008,"length":1,"stats":{"Line":1}},{"line":177,"address":4315513,"length":1,"stats":{"Line":1}},{"line":178,"address":4316018,"length":1,"stats":{"Line":1}},{"line":179,"address":4316523,"length":1,"stats":{"Line":1}},{"line":181,"address":4317028,"length":1,"stats":{"Line":1}},{"line":182,"address":4317538,"length":1,"stats":{"Line":1}},{"line":183,"address":4318048,"length":1,"stats":{"Line":1}},{"line":184,"address":4318558,"length":1,"stats":{"Line":1}},{"line":185,"address":4319084,"length":1,"stats":{"Line":1}},{"line":188,"address":4319700,"length":1,"stats":{"Line":1}},{"line":189,"address":4319616,"length":1,"stats":{"Line":1}},{"line":194,"address":4320141,"length":1,"stats":{"Line":1}},{"line":195,"address":4320693,"length":1,"stats":{"Line":1}},{"line":196,"address":4321245,"length":1,"stats":{"Line":1}},{"line":206,"address":4321881,"length":1,"stats":{"Line":1}},{"line":207,"address":4321797,"length":1,"stats":{"Line":1}},{"line":210,"address":4322406,"length":1,"stats":{"Line":1}},{"line":211,"address":4322322,"length":1,"stats":{"Line":1}},{"line":214,"address":4322931,"length":1,"stats":{"Line":1}},{"line":215,"address":4322847,"length":1,"stats":{"Line":1}},{"line":218,"address":4323456,"length":1,"stats":{"Line":1}},{"line":219,"address":4323372,"length":1,"stats":{"Line":1}},{"line":224,"address":4324008,"length":1,"stats":{"Line":1}},{"line":225,"address":4323897,"length":1,"stats":{"Line":1}},{"line":236,"address":4324533,"length":1,"stats":{"Line":1}},{"line":237,"address":4324449,"length":1,"stats":{"Line":1}},{"line":240,"address":4325058,"length":1,"stats":{"Line":1}},{"line":241,"address":4324974,"length":1,"stats":{"Line":1}},{"line":244,"address":4325583,"length":1,"stats":{"Line":1}},{"line":245,"address":4325499,"length":1,"stats":{"Line":1}},{"line":248,"address":4326108,"length":1,"stats":{"Line":1}},{"line":249,"address":4326024,"length":1,"stats":{"Line":1}},{"line":254,"address":4326677,"length":1,"stats":{"Line":1}},{"line":255,"address":4326641,"length":1,"stats":{"Line":1}},{"line":257,"address":4326561,"length":1,"stats":{"Line":1}},{"line":258,"address":4326549,"length":1,"stats":{"Line":1}},{"line":267,"address":4327246,"length":1,"stats":{"Line":1}},{"line":268,"address":4327210,"length":1,"stats":{"Line":1}},{"line":270,"address":4327130,"length":1,"stats":{"Line":1}},{"line":271,"address":4327118,"length":1,"stats":{"Line":1}},{"line":280,"address":4327815,"length":1,"stats":{"Line":1}},{"line":281,"address":4327779,"length":1,"stats":{"Line":1}},{"line":283,"address":4327699,"length":1,"stats":{"Line":1}},{"line":284,"address":4327687,"length":1,"stats":{"Line":1}},{"line":295,"address":4328256,"length":1,"stats":{"Line":1}},{"line":296,"address":4328808,"length":1,"stats":{"Line":1}},{"line":305,"address":4329368,"length":1,"stats":{"Line":1}},{"line":306,"address":4329662,"length":1,"stats":{"Line":1}},{"line":307,"address":4330426,"length":1,"stats":{"Line":1}},{"line":308,"address":4330963,"length":1,"stats":{"Line":1}},{"line":309,"address":4331516,"length":1,"stats":{"Line":1}},{"line":310,"address":4332069,"length":1,"stats":{"Line":1}},{"line":311,"address":4332622,"length":1,"stats":{"Line":1}},{"line":312,"address":4333175,"length":1,"stats":{"Line":1}},{"line":313,"address":4333830,"length":1,"stats":{"Line":1}},{"line":314,"address":4333707,"length":1,"stats":{"Line":1}},{"line":317,"address":4334432,"length":1,"stats":{"Line":1}},{"line":318,"address":4334309,"length":1,"stats":{"Line":1}},{"line":321,"address":4334935,"length":1,"stats":{"Line":1}},{"line":322,"address":4334911,"length":1,"stats":{"Line":1}},{"line":323,"address":4335061,"length":1,"stats":{"Line":1}},{"line":325,"address":4335451,"length":1,"stats":{"Line":1}},{"line":326,"address":4335415,"length":1,"stats":{"Line":1}},{"line":328,"address":4335335,"length":1,"stats":{"Line":1}},{"line":329,"address":4335323,"length":1,"stats":{"Line":1}},{"line":337,"address":4336020,"length":1,"stats":{"Line":1}},{"line":338,"address":4335984,"length":1,"stats":{"Line":1}},{"line":340,"address":4335904,"length":1,"stats":{"Line":1}},{"line":341,"address":4335892,"length":1,"stats":{"Line":1}},{"line":349,"address":4336589,"length":1,"stats":{"Line":1}},{"line":350,"address":4336553,"length":1,"stats":{"Line":1}},{"line":352,"address":4336473,"length":1,"stats":{"Line":1}},{"line":353,"address":4336461,"length":1,"stats":{"Line":1}},{"line":361,"address":4337158,"length":1,"stats":{"Line":1}},{"line":362,"address":4337122,"length":1,"stats":{"Line":1}},{"line":364,"address":4337042,"length":1,"stats":{"Line":1}},{"line":365,"address":4337030,"length":1,"stats":{"Line":1}},{"line":374,"address":4337599,"length":1,"stats":{"Line":1}},{"line":375,"address":4337670,"length":1,"stats":{"Line":1}},{"line":377,"address":4337749,"length":1,"stats":{"Line":1}},{"line":378,"address":4338043,"length":1,"stats":{"Line":1}},{"line":379,"address":4338807,"length":1,"stats":{"Line":1}},{"line":380,"address":4339344,"length":1,"stats":{"Line":1}},{"line":381,"address":4339882,"length":1,"stats":{"Line":1}},{"line":383,"address":4340420,"length":1,"stats":{"Line":1}},{"line":384,"address":4340973,"length":1,"stats":{"Line":1}},{"line":385,"address":4341510,"length":1,"stats":{"Line":1}},{"line":386,"address":4342063,"length":1,"stats":{"Line":1}},{"line":387,"address":4342616,"length":1,"stats":{"Line":1}},{"line":388,"address":4343169,"length":1,"stats":{"Line":1}},{"line":389,"address":4343824,"length":1,"stats":{"Line":1}},{"line":390,"address":4343701,"length":1,"stats":{"Line":1}},{"line":393,"address":4344426,"length":1,"stats":{"Line":1}},{"line":394,"address":4344303,"length":1,"stats":{"Line":1}},{"line":397,"address":4344929,"length":1,"stats":{"Line":1}},{"line":398,"address":4344905,"length":1,"stats":{"Line":1}},{"line":399,"address":4345055,"length":1,"stats":{"Line":1}},{"line":401,"address":4345445,"length":1,"stats":{"Line":1}},{"line":402,"address":4345409,"length":1,"stats":{"Line":1}},{"line":404,"address":4345329,"length":1,"stats":{"Line":1}},{"line":405,"address":4345317,"length":1,"stats":{"Line":1}},{"line":413,"address":4346014,"length":1,"stats":{"Line":1}},{"line":414,"address":4345978,"length":1,"stats":{"Line":1}},{"line":416,"address":4345898,"length":1,"stats":{"Line":1}},{"line":417,"address":4345886,"length":1,"stats":{"Line":1}},{"line":425,"address":4346583,"length":1,"stats":{"Line":1}},{"line":426,"address":4346547,"length":1,"stats":{"Line":1}},{"line":428,"address":4346467,"length":1,"stats":{"Line":1}},{"line":429,"address":4346455,"length":1,"stats":{"Line":1}},{"line":437,"address":4347152,"length":1,"stats":{"Line":1}},{"line":438,"address":4347116,"length":1,"stats":{"Line":1}},{"line":440,"address":4347036,"length":1,"stats":{"Line":1}},{"line":441,"address":4347024,"length":1,"stats":{"Line":1}},{"line":451,"address":4347601,"length":1,"stats":{"Line":1}},{"line":454,"address":4347882,"length":1,"stats":{"Line":1}},{"line":457,"address":4348605,"length":1,"stats":{"Line":1}},{"line":461,"address":4348878,"length":1,"stats":{"Line":1}},{"line":462,"address":4349612,"length":1,"stats":{"Line":1}},{"line":463,"address":4350272,"length":1,"stats":{"Line":1}},{"line":464,"address":4350236,"length":1,"stats":{"Line":1}},{"line":466,"address":4350156,"length":1,"stats":{"Line":1}},{"line":467,"address":4350144,"length":1,"stats":{"Line":1}},{"line":479,"address":4350797,"length":1,"stats":{"Line":1}},{"line":480,"address":4350713,"length":1,"stats":{"Line":1}},{"line":490,"address":4351238,"length":1,"stats":{"Line":1}},{"line":494,"address":4351494,"length":1,"stats":{"Line":1}},{"line":496,"address":4351376,"length":1,"stats":{"Line":1}},{"line":498,"address":4351974,"length":1,"stats":{"Line":1}},{"line":499,"address":4352593,"length":1,"stats":{"Line":0}},{"line":500,"address":4352308,"length":1,"stats":{"Line":1}},{"line":501,"address":4352586,"length":1,"stats":{"Line":1}},{"line":503,"address":4353168,"length":1,"stats":{"Line":1}},{"line":504,"address":4353812,"length":1,"stats":{"Line":1}},{"line":505,"address":4354344,"length":1,"stats":{"Line":1}},{"line":506,"address":4354977,"length":1,"stats":{"Line":1}},{"line":507,"address":4354941,"length":1,"stats":{"Line":1}},{"line":509,"address":4354861,"length":1,"stats":{"Line":1}},{"line":510,"address":4354849,"length":1,"stats":{"Line":1}},{"line":521,"address":4355418,"length":1,"stats":{"Line":1}},{"line":524,"address":4355926,"length":1,"stats":{"Line":1}},{"line":526,"address":4355556,"length":1,"stats":{"Line":1}},{"line":535,"address":4356414,"length":1,"stats":{"Line":1}},{"line":536,"address":4356500,"length":1,"stats":{"Line":1}},{"line":539,"address":4357058,"length":1,"stats":{"Line":1}},{"line":540,"address":4356832,"length":1,"stats":{"Line":1}},{"line":542,"address":4356744,"length":1,"stats":{"Line":1}},{"line":543,"address":4356732,"length":1,"stats":{"Line":1}},{"line":560,"address":4357523,"length":1,"stats":{"Line":1}},{"line":561,"address":4357538,"length":1,"stats":{"Line":1}},{"line":562,"address":4358689,"length":1,"stats":{"Line":1}},{"line":564,"address":4357842,"length":1,"stats":{"Line":1}},{"line":572,"address":4359188,"length":1,"stats":{"Line":1}},{"line":573,"address":4359203,"length":1,"stats":{"Line":1}},{"line":574,"address":4360354,"length":1,"stats":{"Line":1}},{"line":576,"address":4359507,"length":1,"stats":{"Line":1}},{"line":586,"address":4360858,"length":1,"stats":{"Line":1}},{"line":587,"address":4360873,"length":1,"stats":{"Line":1}},{"line":588,"address":4361982,"length":1,"stats":{"Line":1}},{"line":590,"address":4361165,"length":1,"stats":{"Line":1}}],"covered":215,"coverable":217},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","operations","centrality.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide a trait and implementations to find paths between nodes.\n\nuse crate::data::NodeId;\nuse crate::data::WebOfTrust;\nuse std::collections::{HashMap, VecDeque};\n\n/// Find paths between 2 nodes of a `WebOfTrust`.\npub trait CentralitiesCalculator\u003cT: WebOfTrust\u003e {\n /// Compute betweenness centrality of all members.\n fn betweenness_centralities(\u0026self, wot: \u0026T) -\u003e Vec\u003cu64\u003e;\n /// Compute stress centrality of all members.\n fn stress_centralities(\u0026self, wot: \u0026T) -\u003e Vec\u003cu64\u003e;\n /// Compute distance stress centrality of all members.\n fn distance_stress_centralities(\u0026self, wot: \u0026T, step_max: usize) -\u003e Vec\u003cu64\u003e;\n}\n\n/// An implementation based on \"Ulrik brandes\" algo.\n#[derive(Debug, Clone, Copy)]\npub struct UlrikBrandesCentralityCalculator;\n\nimpl\u003cT: WebOfTrust\u003e CentralitiesCalculator\u003cT\u003e for UlrikBrandesCentralityCalculator {\n fn betweenness_centralities(\u0026self, wot: \u0026T) -\u003e Vec\u003cu64\u003e {\n let wot_size = wot.size();\n let mut centralities = vec![0.0; wot_size];\n let enabled_nodes = wot.get_enabled();\n\n // The source of any path belongs to enabled_nodes\n for s in enabled_nodes.clone() {\n let mut stack: Vec\u003cNodeId\u003e = Vec::with_capacity(wot_size);\n let mut paths: HashMap\u003cNodeId, Vec\u003cNodeId\u003e\u003e = HashMap::with_capacity(wot_size);\n let mut sigma = vec![0.0; wot_size];\n let mut d: Vec\u003cisize\u003e = vec![-1; wot_size];\n let mut q: VecDeque\u003cNodeId\u003e = VecDeque::with_capacity(wot_size);\n\n sigma[s.0] = 1.0;\n d[s.0] = 0;\n q.push_back(s);\n while !q.is_empty() {\n let v = q.pop_front().unwrap();\n stack.push(v);\n for w in wot.get_links_source(v).expect(\"v don't have any source !\") {\n // w found for the first time ?\n if d[w.0] \u003c 0 {\n q.push_back(w);\n d[w.0] = d[v.0] + 1;\n }\n // Shortest path to w via v\n if d[w.0] == d[v.0] + 1 {\n sigma[w.0] += sigma[v.0];\n paths.entry(w).or_insert_with(Vec::new).push(v);\n }\n }\n }\n let mut delta = vec![0.0; wot_size];\n // stack returns vertices in order of non-increasing distance from s\n while !stack.is_empty() {\n let w = stack.pop().unwrap();\n if paths.contains_key(\u0026w) {\n for v in paths.get(\u0026w).expect(\"Not found w in p !\") {\n if enabled_nodes.contains(\u0026w) {\n delta[v.0] += (sigma[v.0] / sigma[w.0]) * (1.0 + delta[w.0]);\n } else {\n // If w not in enabled_nodes, no path can end at w\n delta[v.0] += (sigma[v.0] / sigma[w.0]) * delta[w.0];\n }\n }\n }\n if w != s {\n centralities[w.0] += delta[w.0];\n }\n }\n }\n centralities.into_iter().map(|c| c as u64).collect()\n }\n fn stress_centralities(\u0026self, wot: \u0026T) -\u003e Vec\u003cu64\u003e {\n let wot_size = wot.size();\n let mut centralities = vec![0.0; wot_size];\n let enabled_nodes = wot.get_enabled();\n\n // The source of any path belongs to enabled_nodes\n for s in enabled_nodes.clone() {\n let mut stack: Vec\u003cNodeId\u003e = Vec::with_capacity(wot_size);\n let mut paths: HashMap\u003cNodeId, Vec\u003cNodeId\u003e\u003e = HashMap::with_capacity(wot_size);\n let mut sigma = vec![0.0; wot_size];\n let mut d: Vec\u003cisize\u003e = vec![-1; wot_size];\n let mut q: VecDeque\u003cNodeId\u003e = VecDeque::with_capacity(wot_size);\n\n sigma[s.0] = 1.0;\n d[s.0] = 0;\n q.push_back(s);\n while !q.is_empty() {\n let v = q.pop_front().unwrap();\n stack.push(v);\n for w in wot.get_links_source(v).expect(\"v don't have any source !\") {\n // w found for the first time ?\n if d[w.0] \u003c 0 {\n q.push_back(w);\n d[w.0] = d[v.0] + 1;\n }\n // Shortest path to w via v\n if d[w.0] == d[v.0] + 1 {\n sigma[w.0] += sigma[v.0];\n paths.entry(w).or_insert_with(Vec::new).push(v);\n }\n }\n }\n let mut delta = vec![0.0; wot_size];\n // stack returns vertices in order of non-increasing distance from s\n while !stack.is_empty() {\n let w = stack.pop().unwrap();\n if paths.contains_key(\u0026w) {\n for v in paths.get(\u0026w).expect(\"Not found w in p !\") {\n if enabled_nodes.contains(\u0026w) {\n delta[v.0] += sigma[v.0] * (1.0 + (delta[w.0] / sigma[w.0]));\n } else {\n // If w not in enabled_nodes, no path can end at w\n delta[v.0] += sigma[v.0] * (delta[w.0] / sigma[w.0]);\n }\n }\n }\n if w != s {\n centralities[w.0] += delta[w.0];\n }\n }\n }\n centralities.into_iter().map(|c| c as u64).collect()\n }\n fn distance_stress_centralities(\u0026self, wot: \u0026T, step_max: usize) -\u003e Vec\u003cu64\u003e {\n let wot_size = wot.size();\n let mut centralities = vec![0.0; wot_size];\n let enabled_nodes = wot.get_enabled();\n\n // The source of any path belongs to enabled_nodes\n for s in enabled_nodes.clone() {\n let mut stack: Vec\u003cNodeId\u003e = Vec::with_capacity(wot_size);\n let mut paths: HashMap\u003cNodeId, Vec\u003cNodeId\u003e\u003e = HashMap::with_capacity(wot_size);\n let mut sigma = vec![0.0; wot_size];\n let mut d: Vec\u003cisize\u003e = vec![-1; wot_size];\n let mut q: VecDeque\u003cNodeId\u003e = VecDeque::with_capacity(wot_size);\n\n sigma[s.0] = 1.0;\n d[s.0] = 0;\n q.push_back(s);\n while !q.is_empty() {\n let v = q.pop_front().unwrap();\n stack.push(v);\n if d[v.0] \u003c step_max as isize {\n for w in wot.get_links_source(v).expect(\"v don't have any source !\") {\n // w found for the first time ?\n if d[w.0] \u003c 0 {\n q.push_back(w);\n d[w.0] = d[v.0] + 1;\n }\n // Shortest path to w via v\n if d[w.0] == d[v.0] + 1 {\n sigma[w.0] += sigma[v.0];\n paths.entry(w).or_insert_with(Vec::new).push(v);\n }\n }\n }\n }\n let mut delta = vec![0.0; wot_size];\n // stack returns vertices in order of non-increasing distance from s\n while !stack.is_empty() {\n let w = stack.pop().unwrap();\n if paths.contains_key(\u0026w) {\n for v in paths.get(\u0026w).expect(\"Not found w in p !\") {\n if enabled_nodes.contains(\u0026w) {\n delta[v.0] += sigma[v.0] * (1.0 + (delta[w.0] / sigma[w.0]));\n } else {\n // If w not in enabled_nodes, no path can end at w\n delta[v.0] += sigma[v.0] * (delta[w.0] / sigma[w.0]);\n }\n }\n }\n if w != s {\n centralities[w.0] += delta[w.0];\n }\n }\n }\n centralities.into_iter().map(|c| c as u64).collect()\n }\n}\n","traces":[{"line":37,"address":null,"length":0,"stats":{"Line":1}},{"line":38,"address":null,"length":0,"stats":{"Line":1}},{"line":39,"address":null,"length":0,"stats":{"Line":1}},{"line":40,"address":null,"length":0,"stats":{"Line":1}},{"line":43,"address":null,"length":0,"stats":{"Line":1}},{"line":44,"address":null,"length":0,"stats":{"Line":1}},{"line":45,"address":null,"length":0,"stats":{"Line":1}},{"line":46,"address":null,"length":0,"stats":{"Line":1}},{"line":47,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":1}},{"line":50,"address":null,"length":0,"stats":{"Line":1}},{"line":51,"address":null,"length":0,"stats":{"Line":1}},{"line":52,"address":null,"length":0,"stats":{"Line":1}},{"line":53,"address":null,"length":0,"stats":{"Line":1}},{"line":54,"address":null,"length":0,"stats":{"Line":1}},{"line":55,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":null,"length":0,"stats":{"Line":1}},{"line":58,"address":null,"length":0,"stats":{"Line":1}},{"line":59,"address":null,"length":0,"stats":{"Line":1}},{"line":60,"address":null,"length":0,"stats":{"Line":1}},{"line":63,"address":null,"length":0,"stats":{"Line":1}},{"line":64,"address":null,"length":0,"stats":{"Line":1}},{"line":65,"address":null,"length":0,"stats":{"Line":1}},{"line":69,"address":null,"length":0,"stats":{"Line":1}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":1}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":77,"address":null,"length":0,"stats":{"Line":0}},{"line":79,"address":null,"length":0,"stats":{"Line":0}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":84,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":2}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":96,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":104,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":1}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":109,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":112,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":1}},{"line":116,"address":null,"length":0,"stats":{"Line":1}},{"line":117,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":1}},{"line":124,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":1}},{"line":126,"address":null,"length":0,"stats":{"Line":1}},{"line":127,"address":null,"length":0,"stats":{"Line":1}},{"line":128,"address":null,"length":0,"stats":{"Line":1}},{"line":129,"address":null,"length":0,"stats":{"Line":1}},{"line":130,"address":null,"length":0,"stats":{"Line":0}},{"line":132,"address":null,"length":0,"stats":{"Line":0}},{"line":136,"address":null,"length":0,"stats":{"Line":1}},{"line":137,"address":null,"length":0,"stats":{"Line":1}},{"line":141,"address":null,"length":0,"stats":{"Line":2}},{"line":143,"address":null,"length":0,"stats":{"Line":1}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":145,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":149,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":157,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":1}},{"line":160,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":165,"address":null,"length":0,"stats":{"Line":1}},{"line":166,"address":null,"length":0,"stats":{"Line":1}},{"line":167,"address":null,"length":0,"stats":{"Line":1}},{"line":170,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":1}},{"line":177,"address":null,"length":0,"stats":{"Line":1}},{"line":179,"address":null,"length":0,"stats":{"Line":1}},{"line":180,"address":null,"length":0,"stats":{"Line":1}},{"line":181,"address":null,"length":0,"stats":{"Line":1}},{"line":182,"address":null,"length":0,"stats":{"Line":1}},{"line":183,"address":null,"length":0,"stats":{"Line":1}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":185,"address":null,"length":0,"stats":{"Line":0}},{"line":187,"address":null,"length":0,"stats":{"Line":0}},{"line":191,"address":null,"length":0,"stats":{"Line":1}},{"line":192,"address":null,"length":0,"stats":{"Line":1}},{"line":196,"address":null,"length":0,"stats":{"Line":2}}],"covered":100,"coverable":106},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","operations","distance.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide a trait and implementations to compute distances.\n\nuse crate::data::NodeId;\nuse crate::data::WebOfTrust;\nuse rayon::prelude::*;\nuse std::collections::HashSet;\n\n/// Paramters for `WoT` distance calculations\n#[derive(Debug, Copy, Clone, PartialEq)]\npub struct WotDistanceParameters {\n /// Node from where distances are calculated.\n pub node: NodeId,\n /// Links count received AND issued to be a sentry.\n pub sentry_requirement: u32,\n /// Currency parameter.\n pub step_max: u32,\n /// Currency parameter.\n pub x_percent: f64,\n}\n\n/// Results of `WebOfTrust::compute_distance`.\n#[derive(Debug, Copy, Clone, PartialEq, Eq)]\npub struct WotDistance {\n /// Sentries count\n pub sentries: u32,\n /// Success count\n pub success: u32,\n /// Succes at border count\n pub success_at_border: u32,\n /// Reached count\n pub reached: u32,\n /// Reached at border count\n pub reached_at_border: u32,\n /// Is the node outdistanced ?\n pub outdistanced: bool,\n}\n\n/// Compute distance between nodes of a `WebOfTrust`.\npub trait DistanceCalculator\u003cT: WebOfTrust\u003e {\n /// Compute distance between a node and the network.\n /// Returns `None` if this node doesn't exist.\n fn compute_distance(\u0026self, wot: \u0026T, params: WotDistanceParameters) -\u003e Option\u003cWotDistance\u003e;\n\n /// Test if a node is outdistanced in the network.\n /// Returns `Node` if this node doesn't exist.\n fn is_outdistanced(\u0026self, wot: \u0026T, params: WotDistanceParameters) -\u003e Option\u003cbool\u003e;\n}\n\n/// Calculate distances between 2 members in a `WebOfTrust`.\n#[derive(Debug, Clone, Copy)]\npub struct RustyDistanceCalculator;\n\nimpl\u003cT: WebOfTrust + Sync\u003e DistanceCalculator\u003cT\u003e for RustyDistanceCalculator {\n fn compute_distance(\u0026self, wot: \u0026T, params: WotDistanceParameters) -\u003e Option\u003cWotDistance\u003e {\n let WotDistanceParameters {\n node,\n sentry_requirement,\n step_max,\n x_percent,\n } = params;\n\n if node.0 \u003e= wot.size() {\n return None;\n }\n\n let mut area = HashSet::new();\n area.insert(node);\n let mut border = HashSet::new();\n border.insert(node);\n\n for _ in 0..step_max {\n border = border\n .par_iter()\n .map(|\u0026id| {\n wot.get_links_source(id)\n .unwrap()\n .iter()\n .filter(|source| !area.contains(source))\n .cloned()\n .collect::\u003cHashSet\u003c_\u003e\u003e()\n })\n .reduce(HashSet::new, |mut acc, sources| {\n for source in sources {\n acc.insert(source);\n }\n acc\n });\n area.extend(border.iter());\n }\n\n let sentries: Vec\u003c_\u003e = wot.get_sentries(sentry_requirement as usize);\n let mut success = area.iter().filter(|n| sentries.contains(n)).count() as u32;\n let success_at_border = border.iter().filter(|n| sentries.contains(n)).count() as u32;\n let mut sentries = sentries.len() as u32;\n if wot.is_sentry(node, sentry_requirement as usize).unwrap() {\n sentries -= 1;\n success -= 1;\n }\n\n Some(WotDistance {\n sentries,\n reached: area.len() as u32 - 1,\n reached_at_border: border.len() as u32,\n success,\n success_at_border,\n outdistanced: f64::from(success) \u003c x_percent * f64::from(sentries),\n })\n }\n\n fn is_outdistanced(\u0026self, wot: \u0026T, params: WotDistanceParameters) -\u003e Option\u003cbool\u003e {\n Self::compute_distance(\u0026self, wot, params).map(|result| result.outdistanced)\n }\n}\n","traces":[{"line":69,"address":null,"length":0,"stats":{"Line":1}},{"line":70,"address":null,"length":0,"stats":{"Line":0}},{"line":71,"address":null,"length":0,"stats":{"Line":1}},{"line":72,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":74,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":0}},{"line":77,"address":null,"length":0,"stats":{"Line":1}},{"line":78,"address":null,"length":0,"stats":{"Line":0}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":82,"address":null,"length":0,"stats":{"Line":1}},{"line":83,"address":null,"length":0,"stats":{"Line":1}},{"line":84,"address":null,"length":0,"stats":{"Line":1}},{"line":86,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":0}},{"line":89,"address":null,"length":0,"stats":{"Line":2}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":0}},{"line":92,"address":null,"length":0,"stats":{"Line":0}},{"line":93,"address":null,"length":0,"stats":{"Line":2}},{"line":94,"address":null,"length":0,"stats":{"Line":0}},{"line":95,"address":null,"length":0,"stats":{"Line":0}},{"line":97,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}},{"line":99,"address":null,"length":0,"stats":{"Line":1}},{"line":101,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":107,"address":null,"length":0,"stats":{"Line":2}},{"line":108,"address":null,"length":0,"stats":{"Line":2}},{"line":109,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":0}},{"line":112,"address":null,"length":0,"stats":{"Line":0}},{"line":115,"address":null,"length":0,"stats":{"Line":1}},{"line":116,"address":null,"length":0,"stats":{"Line":1}},{"line":117,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":1}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":125,"address":null,"length":0,"stats":{"Line":1}},{"line":126,"address":null,"length":0,"stats":{"Line":2}}],"covered":34,"coverable":44},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","operations","file.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide a trait and implementation to read and write `WebOfTrust` to disk.\n\nuse crate::data::NodeId;\nuse durs_common_tools::fatal_error;\nuse std::fs;\nuse std::fs::File;\nuse std::io;\nuse std::io::prelude::*;\n\nuse byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};\n\nuse crate::data::WebOfTrust;\n\n/// Results of `WebOfTrust` parsing from binary file.\n#[derive(Debug)]\npub enum WotParseError {\n /// FailToOpenFile\n FailToOpenFile(io::Error),\n\n /// IOError\n IOError(io::Error),\n}\n\nimpl From\u003cio::Error\u003e for WotParseError {\n fn from(e: io::Error) -\u003e WotParseError {\n WotParseError::IOError(e)\n }\n}\n\n/// Results of `WebOfTrust` writing to binary file.\n#[derive(Debug)]\npub enum WotWriteError {\n /// WrongWotSize\n WrongWotSize(),\n\n /// FailToCreateFile\n FailToCreateFile(io::Error),\n\n /// FailToWriteInFile\n FailToWriteInFile(io::Error),\n}\n\nimpl From\u003cio::Error\u003e for WotWriteError {\n fn from(e: io::Error) -\u003e WotWriteError {\n WotWriteError::FailToWriteInFile(e)\n }\n}\n\n/// Provide Read/Write functions for `WebOfTrust` objects.\npub trait FileFormater {\n /// Try to read a `WebOfTrust` from a file.\n fn from_file\u003cT: WebOfTrust\u003e(\n \u0026self,\n path: \u0026str,\n max_links: usize,\n ) -\u003e Result\u003c(T, Vec\u003cu8\u003e), WotParseError\u003e;\n\n /// Tru to write a `WebOfTrust` in a file.\n fn to_file\u003cT: WebOfTrust\u003e(\u0026self, wot: \u0026T, data: \u0026[u8], path: \u0026str)\n -\u003e Result\u003c(), WotWriteError\u003e;\n}\n\n/// Read and write WebOfTrust in a binary format.\n#[derive(Debug, Clone, Copy)]\npub struct BinaryFileFormater;\n\nimpl FileFormater for BinaryFileFormater {\n /// Try to read a `WebOfTrust` from a file.\n fn from_file\u003cT: WebOfTrust\u003e(\n \u0026self,\n path: \u0026str,\n max_links: usize,\n ) -\u003e Result\u003c(T, Vec\u003cu8\u003e), WotParseError\u003e {\n let mut wot = T::new(max_links);\n\n let file_size = fs::metadata(path).expect(\"fail to read wot file !\").len();\n let mut file_pointing_to_blockstamp_size: Vec\u003cu8\u003e = vec![0; file_size as usize];\n match File::open(path) {\n Ok(mut file) =\u003e {\n file.read_exact(\u0026mut file_pointing_to_blockstamp_size.as_mut_slice())?;\n }\n Err(e) =\u003e return Err(WotParseError::FailToOpenFile(e)),\n };\n // Read up to 4 bytes (blockstamp_size)\n let mut file_pointing_to_blockstamp = file_pointing_to_blockstamp_size.split_off(4);\n // Get blockstamp size\n let mut buf = \u0026file_pointing_to_blockstamp_size[..];\n let blockstamp_size = buf.read_u32::\u003cBigEndian\u003e().unwrap();\n // Read up to blockstamp_size bytes (blockstamp)\n let mut file_pointing_to_nodes_count =\n file_pointing_to_blockstamp.split_off(blockstamp_size as usize);\n // Read up to 4 bytes (nodes_count)\n let mut file_pointing_to_nodes_states = file_pointing_to_nodes_count.split_off(4);\n // Read nodes_count\n let mut buf = \u0026file_pointing_to_nodes_count[..];\n let nodes_count = buf.read_u32::\u003cBigEndian\u003e().unwrap();\n // Calcule nodes_state size\n let nodes_states_size = match nodes_count % 8 {\n 0 =\u003e nodes_count / 8,\n _ =\u003e (nodes_count / 8) + 1,\n };\n // Read up to nodes_states_size bytes (nodes_states)\n let file_pointing_to_links =\n file_pointing_to_nodes_states.split_off(nodes_states_size as usize);\n let count_total_bytes_read = file_pointing_to_links.len()\n + nodes_states_size as usize\n + 4\n + blockstamp_size as usize\n + 4;\n if count_total_bytes_read != file_size as usize {\n fatal_error!(\"not read all wot file !\");\n }\n // Apply nodes state\n let mut count_remaining_nodes = nodes_count;\n for byte in file_pointing_to_nodes_states {\n let mut byte_integer = u8::from_be(byte);\n let mut factor: u8 = 128;\n for _i in 0..8 {\n if count_remaining_nodes \u003e 0 {\n wot.add_node();\n if byte_integer \u003e= factor {\n byte_integer -= factor;\n } else {\n let _test = wot.set_enabled(\n NodeId((nodes_count - count_remaining_nodes) as usize),\n false,\n );\n }\n count_remaining_nodes -= 1;\n }\n factor /= 2;\n }\n }\n // Apply links\n let mut buffer_3b: Vec\u003cu8\u003e = Vec::with_capacity(3);\n let mut count_bytes = 0;\n let mut remaining_links: u8 = 0;\n let mut target: u32 = 0;\n for byte in file_pointing_to_links {\n if remaining_links == 0 {\n target += 1;\n remaining_links = u8::from_be(byte);\n count_bytes = 0;\n } else {\n buffer_3b.push(byte);\n if count_bytes % 3 == 2 {\n let mut buf = \u0026buffer_3b.clone()[..];\n let source = buf.read_u24::\u003cBigEndian\u003e().expect(\"fail to parse source\");\n wot.add_link(NodeId(source as usize), NodeId((target - 1) as usize));\n remaining_links -= 1;\n buffer_3b.clear();\n }\n count_bytes += 1;\n }\n }\n if count_bytes % 3 != 0 {\n fatal_error!(\"not read all wot file !\");\n }\n Ok((wot, file_pointing_to_blockstamp))\n }\n\n /// Try to write a `WebOfTrust` in a file.\n fn to_file\u003cT: WebOfTrust\u003e(\n \u0026self,\n wot: \u0026T,\n data: \u0026[u8],\n path: \u0026str,\n ) -\u003e Result\u003c(), WotWriteError\u003e {\n let mut buffer: Vec\u003cu8\u003e = Vec::new();\n // Write blockstamp size\n let blockstamp_size = data.len() as u32;\n let mut bytes: Vec\u003cu8\u003e = Vec::with_capacity(4);\n bytes.write_u32::\u003cBigEndian\u003e(blockstamp_size).unwrap();\n buffer.append(\u0026mut bytes);\n // Write blockstamp\n buffer.append(\u0026mut data.to_vec());\n // Write nodes_count\n let nodes_count = wot.size() as u32;\n let mut bytes: Vec\u003cu8\u003e = Vec::with_capacity(4);\n bytes.write_u32::\u003cBigEndian\u003e(nodes_count).unwrap();\n buffer.append(\u0026mut bytes);\n // Write enable state by groups of 8\n let mut enable_states: u8 = 0;\n let mut factor: u8 = 128;\n for n in 0..nodes_count {\n match wot.is_enabled(NodeId(n as usize)) {\n Some(enable) =\u003e {\n if enable {\n enable_states += factor;\n }\n }\n None =\u003e {\n return Err(WotWriteError::WrongWotSize());\n }\n }\n if n % 8 == 7 {\n factor = 128;\n let mut tmp_buf = Vec::with_capacity(1);\n tmp_buf.write_u8(enable_states).unwrap();\n buffer.append(\u0026mut tmp_buf);\n enable_states = 0;\n } else {\n factor /= 2;\n }\n }\n // nodes_states padding\n if nodes_count % 8 != 7 {\n let mut tmp_buf = Vec::with_capacity(1);\n tmp_buf.write_u8(enable_states).unwrap();\n buffer.append(\u0026mut tmp_buf);\n }\n // Write links\n for n in 0..nodes_count {\n if let Some(sources) = wot.get_links_source(NodeId(n as usize)) {\n // Write sources_counts\n let mut bytes = Vec::with_capacity(1);\n bytes.write_u8(sources.len() as u8).unwrap();\n buffer.append(\u0026mut bytes);\n for source in \u0026sources {\n // Write source\n let mut bytes: Vec\u003cu8\u003e = Vec::with_capacity(3);\n bytes.write_u24::\u003cBigEndian\u003e(source.0 as u32).unwrap();\n buffer.append(\u0026mut bytes);\n }\n };\n }\n // Create or open file\n let mut file = match File::create(path) {\n Ok(file) =\u003e file,\n Err(e) =\u003e return Err(WotWriteError::FailToCreateFile(e)),\n };\n // Write buffer in file\n file.write_all(\u0026buffer)?;\n\n Ok(())\n }\n}\n","traces":[{"line":40,"address":null,"length":0,"stats":{"Line":0}},{"line":41,"address":null,"length":0,"stats":{"Line":0}},{"line":59,"address":null,"length":0,"stats":{"Line":0}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":1}},{"line":91,"address":null,"length":0,"stats":{"Line":1}},{"line":92,"address":null,"length":0,"stats":{"Line":1}},{"line":93,"address":null,"length":0,"stats":{"Line":1}},{"line":94,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":97,"address":null,"length":0,"stats":{"Line":0}},{"line":100,"address":null,"length":0,"stats":{"Line":1}},{"line":102,"address":null,"length":0,"stats":{"Line":1}},{"line":103,"address":null,"length":0,"stats":{"Line":1}},{"line":105,"address":null,"length":0,"stats":{"Line":0}},{"line":106,"address":null,"length":0,"stats":{"Line":1}},{"line":108,"address":null,"length":0,"stats":{"Line":1}},{"line":110,"address":null,"length":0,"stats":{"Line":1}},{"line":111,"address":null,"length":0,"stats":{"Line":1}},{"line":113,"address":null,"length":0,"stats":{"Line":1}},{"line":114,"address":null,"length":0,"stats":{"Line":1}},{"line":115,"address":null,"length":0,"stats":{"Line":1}},{"line":118,"address":null,"length":0,"stats":{"Line":0}},{"line":119,"address":null,"length":0,"stats":{"Line":1}},{"line":120,"address":null,"length":0,"stats":{"Line":1}},{"line":121,"address":null,"length":0,"stats":{"Line":1}},{"line":122,"address":null,"length":0,"stats":{"Line":0}},{"line":123,"address":null,"length":0,"stats":{"Line":1}},{"line":124,"address":null,"length":0,"stats":{"Line":0}},{"line":125,"address":null,"length":0,"stats":{"Line":1}},{"line":126,"address":null,"length":0,"stats":{"Line":0}},{"line":129,"address":null,"length":0,"stats":{"Line":1}},{"line":130,"address":null,"length":0,"stats":{"Line":1}},{"line":131,"address":null,"length":0,"stats":{"Line":1}},{"line":132,"address":null,"length":0,"stats":{"Line":1}},{"line":133,"address":null,"length":0,"stats":{"Line":1}},{"line":134,"address":null,"length":0,"stats":{"Line":1}},{"line":135,"address":null,"length":0,"stats":{"Line":1}},{"line":136,"address":null,"length":0,"stats":{"Line":1}},{"line":137,"address":null,"length":0,"stats":{"Line":1}},{"line":138,"address":null,"length":0,"stats":{"Line":0}},{"line":139,"address":null,"length":0,"stats":{"Line":1}},{"line":140,"address":null,"length":0,"stats":{"Line":1}},{"line":141,"address":null,"length":0,"stats":{"Line":0}},{"line":144,"address":null,"length":0,"stats":{"Line":1}},{"line":146,"address":null,"length":0,"stats":{"Line":1}},{"line":150,"address":null,"length":0,"stats":{"Line":1}},{"line":151,"address":null,"length":0,"stats":{"Line":1}},{"line":152,"address":null,"length":0,"stats":{"Line":1}},{"line":153,"address":null,"length":0,"stats":{"Line":1}},{"line":154,"address":null,"length":0,"stats":{"Line":1}},{"line":155,"address":null,"length":0,"stats":{"Line":1}},{"line":156,"address":null,"length":0,"stats":{"Line":1}},{"line":157,"address":null,"length":0,"stats":{"Line":1}},{"line":158,"address":null,"length":0,"stats":{"Line":1}},{"line":159,"address":null,"length":0,"stats":{"Line":0}},{"line":160,"address":null,"length":0,"stats":{"Line":1}},{"line":161,"address":null,"length":0,"stats":{"Line":1}},{"line":162,"address":null,"length":0,"stats":{"Line":1}},{"line":163,"address":null,"length":0,"stats":{"Line":1}},{"line":164,"address":null,"length":0,"stats":{"Line":1}},{"line":165,"address":null,"length":0,"stats":{"Line":1}},{"line":166,"address":null,"length":0,"stats":{"Line":1}},{"line":168,"address":null,"length":0,"stats":{"Line":1}},{"line":171,"address":null,"length":0,"stats":{"Line":1}},{"line":172,"address":null,"length":0,"stats":{"Line":0}},{"line":174,"address":null,"length":0,"stats":{"Line":1}},{"line":178,"address":null,"length":0,"stats":{"Line":1}},{"line":184,"address":null,"length":0,"stats":{"Line":1}},{"line":186,"address":null,"length":0,"stats":{"Line":1}},{"line":187,"address":null,"length":0,"stats":{"Line":1}},{"line":188,"address":null,"length":0,"stats":{"Line":1}},{"line":189,"address":null,"length":0,"stats":{"Line":1}},{"line":191,"address":null,"length":0,"stats":{"Line":1}},{"line":193,"address":null,"length":0,"stats":{"Line":1}},{"line":194,"address":null,"length":0,"stats":{"Line":1}},{"line":195,"address":null,"length":0,"stats":{"Line":1}},{"line":196,"address":null,"length":0,"stats":{"Line":1}},{"line":198,"address":null,"length":0,"stats":{"Line":1}},{"line":199,"address":null,"length":0,"stats":{"Line":1}},{"line":200,"address":null,"length":0,"stats":{"Line":1}},{"line":201,"address":null,"length":0,"stats":{"Line":1}},{"line":202,"address":null,"length":0,"stats":{"Line":1}},{"line":203,"address":null,"length":0,"stats":{"Line":1}},{"line":204,"address":null,"length":0,"stats":{"Line":1}},{"line":207,"address":null,"length":0,"stats":{"Line":0}},{"line":208,"address":null,"length":0,"stats":{"Line":0}},{"line":211,"address":null,"length":0,"stats":{"Line":1}},{"line":212,"address":null,"length":0,"stats":{"Line":1}},{"line":213,"address":null,"length":0,"stats":{"Line":1}},{"line":214,"address":null,"length":0,"stats":{"Line":1}},{"line":215,"address":null,"length":0,"stats":{"Line":1}},{"line":216,"address":null,"length":0,"stats":{"Line":1}},{"line":217,"address":null,"length":0,"stats":{"Line":0}},{"line":218,"address":null,"length":0,"stats":{"Line":1}},{"line":222,"address":null,"length":0,"stats":{"Line":1}},{"line":223,"address":null,"length":0,"stats":{"Line":1}},{"line":224,"address":null,"length":0,"stats":{"Line":1}},{"line":225,"address":null,"length":0,"stats":{"Line":1}},{"line":228,"address":null,"length":0,"stats":{"Line":1}},{"line":229,"address":null,"length":0,"stats":{"Line":1}},{"line":231,"address":null,"length":0,"stats":{"Line":1}},{"line":232,"address":null,"length":0,"stats":{"Line":1}},{"line":233,"address":null,"length":0,"stats":{"Line":1}},{"line":234,"address":null,"length":0,"stats":{"Line":1}},{"line":236,"address":null,"length":0,"stats":{"Line":1}},{"line":237,"address":null,"length":0,"stats":{"Line":1}},{"line":238,"address":null,"length":0,"stats":{"Line":1}},{"line":243,"address":null,"length":0,"stats":{"Line":1}},{"line":244,"address":null,"length":0,"stats":{"Line":1}},{"line":245,"address":null,"length":0,"stats":{"Line":0}},{"line":248,"address":null,"length":0,"stats":{"Line":1}},{"line":250,"address":null,"length":0,"stats":{"Line":1}}],"covered":96,"coverable":114},{"path":["/","home","elois","dev","duniter","nodes","rust","duniter-rs","lib","tools","wot","operations","path.rs"],"content":"// Copyright (C) 2017-2019 The AXIOM TEAM Association.\n//\n// This program is free software: you can redistribute it and/or modify\n// it under the terms of the GNU Affero General Public License as\n// published by the Free Software Foundation, either version 3 of the\n// License, or (at your option) any later version.\n//\n// This program is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU Affero General Public License for more details.\n//\n// You should have received a copy of the GNU Affero General Public License\n// along with this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\n//! Provide a trait and implementations to find paths between nodes.\n\nuse crate::data::NodeId;\nuse crate::data::WebOfTrust;\nuse std::collections::HashSet;\n\n/// Find paths between 2 nodes of a `WebOfTrust`.\npub trait PathFinder\u003cT: WebOfTrust\u003e {\n /// Get paths from one node to the other.\n fn find_paths(\u0026self, wot: \u0026T, from: NodeId, to: NodeId, k_max: u32) -\u003e Vec\u003cVec\u003cNodeId\u003e\u003e;\n}\n\n/// A new \"rusty-er\" implementation of `WoT` path finding.\n#[derive(Debug, Clone, Copy)]\npub struct RustyPathFinder;\n\nimpl\u003cT: WebOfTrust\u003e PathFinder\u003cT\u003e for RustyPathFinder {\n fn find_paths(\u0026self, wot: \u0026T, from: NodeId, to: NodeId, k_max: u32) -\u003e Vec\u003cVec\u003cNodeId\u003e\u003e {\n if from.0 \u003e= wot.size() || to.0 \u003e= wot.size() {\n return vec![];\n }\n\n // 1. We explore the k_max area around `to`, and only remember backward\n // links of the smallest distance.\n\n // Stores for each node its distance to `to` node and its backward links.\n // By default all nodes are out of range (`k_max + 1`) and links are known.\n let mut graph: Vec\u003c(u32, Vec\u003cNodeId\u003e)\u003e =\n (0..wot.size()).map(|_| (k_max + 1, vec![])).collect();\n // `to` node is at distance 0, and have no backward links.\n graph[to.0] = (0, vec![]);\n // Explored zone border.\n let mut border = HashSet::new();\n border.insert(to);\n\n for distance in 1..=k_max {\n let mut next_border = HashSet::new();\n\n for node in border {\n for source in \u0026wot.get_links_source(node).unwrap() {\n if graph[source.0].0 \u003e distance {\n // shorter path, we replace\n graph[source.0] = (distance, vec![node]);\n next_border.insert(*source);\n } else if graph[source.0].0 == distance {\n // same length, we combine\n graph[source.0].1.push(node);\n next_border.insert(*source);\n }\n }\n }\n\n border = next_border;\n }\n\n // 2. If `from` is found, we follow the backward links and build paths.\n // For each path, we look at the last element sources and build new paths with them.\n let mut paths = vec![vec![from]];\n\n for _ in 1..=k_max {\n let mut new_paths = vec![];\n\n for path in \u0026paths {\n let node = path.last().unwrap();\n\n if node == \u0026to {\n // If path is complete, we keep it.\n new_paths.push(path.clone())\n } else {\n // If not complete we comlete paths\n let sources = \u0026graph[node.0];\n for source in \u0026sources.1 {\n let mut new_path = path.clone();\n new_path.push(*source);\n new_paths.push(new_path);\n }\n }\n }\n\n paths = new_paths;\n }\n\n paths\n }\n}\n","traces":[{"line":33,"address":null,"length":0,"stats":{"Line":1}},{"line":34,"address":null,"length":0,"stats":{"Line":1}},{"line":35,"address":null,"length":0,"stats":{"Line":0}},{"line":43,"address":null,"length":0,"stats":{"Line":0}},{"line":44,"address":null,"length":0,"stats":{"Line":2}},{"line":46,"address":null,"length":0,"stats":{"Line":1}},{"line":48,"address":null,"length":0,"stats":{"Line":1}},{"line":49,"address":null,"length":0,"stats":{"Line":1}},{"line":51,"address":null,"length":0,"stats":{"Line":1}},{"line":52,"address":null,"length":0,"stats":{"Line":1}},{"line":54,"address":null,"length":0,"stats":{"Line":1}},{"line":55,"address":null,"length":0,"stats":{"Line":1}},{"line":56,"address":null,"length":0,"stats":{"Line":1}},{"line":58,"address":null,"length":0,"stats":{"Line":1}},{"line":59,"address":null,"length":0,"stats":{"Line":1}},{"line":60,"address":null,"length":0,"stats":{"Line":0}},{"line":62,"address":null,"length":0,"stats":{"Line":0}},{"line":63,"address":null,"length":0,"stats":{"Line":0}},{"line":68,"address":null,"length":0,"stats":{"Line":1}},{"line":73,"address":null,"length":0,"stats":{"Line":1}},{"line":75,"address":null,"length":0,"stats":{"Line":1}},{"line":76,"address":null,"length":0,"stats":{"Line":1}},{"line":78,"address":null,"length":0,"stats":{"Line":1}},{"line":79,"address":null,"length":0,"stats":{"Line":1}},{"line":81,"address":null,"length":0,"stats":{"Line":1}},{"line":83,"address":null,"length":0,"stats":{"Line":0}},{"line":84,"address":null,"length":0,"stats":{"Line":0}},{"line":86,"address":null,"length":0,"stats":{"Line":1}},{"line":87,"address":null,"length":0,"stats":{"Line":1}},{"line":88,"address":null,"length":0,"stats":{"Line":1}},{"line":89,"address":null,"length":0,"stats":{"Line":1}},{"line":90,"address":null,"length":0,"stats":{"Line":1}},{"line":95,"address":null,"length":0,"stats":{"Line":1}},{"line":98,"address":null,"length":0,"stats":{"Line":1}}],"covered":27,"coverable":34}]};</script> - <script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script> - <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script> - <script>const e = React.createElement; - -function pathToString(path) { - if (path[0] === '/') { - return '/' + path.slice(1).join('/'); - } else { - return path.join('/'); - } -} - -function findCommonPath(files) { - if (!files || !files.length) { - return []; - } - - function isPrefix(arr, prefix) { - if (arr.length < prefix.length) { - return false; - } - for (let i = prefix.length - 1; i >= 0; --i) { - if (arr[i] !== prefix[i]) { - return false; - } - } - return true; - } - - let commonPath = files[0].path.slice(0, -1); - while (commonPath.length) { - if (files.every(file => isPrefix(file.path, commonPath))) { - break; - } - commonPath.pop(); - } - return commonPath; -} - -function findFolders(files) { - if (!files || !files.length) { - return []; - } - - let folders = files.filter(file => file.path.length > 1).map(file => file.path[0]); - folders = [...new Set(folders)]; // unique - folders.sort(); - - folders = folders.map(folder => { - let filesInFolder = files - .filter(file => file.path[0] === folder) - .map(file => ({ - ...file, - path: file.path.slice(1), - parent: [...file.parent, file.path[0]], - })); - - const children = findFolders(filesInFolder); // recursion - - return { - is_folder: true, - path: [folder], - parent: files[0].parent, - children, - covered: children.reduce((sum, file) => sum + file.covered, 0), - coverable: children.reduce((sum, file) => sum + file.coverable, 0), - }; - }); - - return [ - ...folders, - ...files.filter(file => file.path.length === 1), - ]; -} - -class App extends React.Component { - constructor(...args) { - super(...args); - - this.state = { - current: [], - }; - } - - componentDidMount() { - this.updateStateFromLocation(); - window.addEventListener("hashchange", () => this.updateStateFromLocation(), false); - } - - updateStateFromLocation() { - if (window.location.hash.length > 1) { - const current = window.location.hash.substr(1).split('/'); - this.setState({current}); - } else { - this.setState({current: []}); - } - } - - getCurrentPath() { - let file = this.props.root; - let path = [file]; - for (let p of this.state.current) { - file = file.children.find(file => file.path[0] === p); - if (!file) { - return path; - } - path.push(file); - } - return path; - } - - render() { - const path = this.getCurrentPath(); - const file = path[path.length - 1]; - - let w = null; - if (file.is_folder) { - w = e(FilesList, { - folder: file, - onSelectFile: this.selectFile.bind(this), - onBack: path.length > 1 ? this.back.bind(this) : null, - }); - } else { - w = e(DisplayFile, { - file, - onBack: this.back.bind(this), - }); - } - - return e('div', {className: 'app'}, w); - } - - selectFile(file) { - this.setState(({current}) => { - return {current: [...current, file.path[0]]}; - }, () => this.updateHash()); - } - - back(file) { - this.setState(({current}) => { - return {current: current.slice(0, current.length - 1)}; - }, () => this.updateHash()); - } - - updateHash() { - if (!this.state.current || !this.state.current.length) { - window.location = '#'; - } else { - window.location = '#' + this.state.current.join('/'); - } - } -} - -function FilesList({folder, onSelectFile, onBack}) { - let files = folder.children; - return e('div', {className: 'display-folder'}, - e(FileHeader, {file: folder, onBack}), - e('table', {className: 'files-list'}, - e('thead', {className: 'files-list__head'}, - e('tr', null, - e('th', null, "Path"), - e('th', null, "Coverage") - ) - ), - e('tbody', {className: 'files-list__body'}, - files.map(file => e(File, {file, onClick: onSelectFile})) - ) - ) - ); -} - -function File({file, onClick}) { - const coverage = file.coverable ? file.covered / file.coverable * 100 : -1; - - return e('tr', { - className: 'files-list__file' - + (coverage >= 0 && coverage < 50 ? ' files-list__file_low': '') - + (coverage >= 50 && coverage < 80 ? ' files-list__file_medium': '') - + (coverage >= 80 ? ' files-list__file_high': '') - + (file.is_folder ? ' files-list__file_folder': ''), - onClick: () => onClick(file), - }, - e('td', null, pathToString(file.path)), - e('td', null, - file.covered + ' / ' + file.coverable + - (coverage >= 0 ? ' (' + coverage.toFixed(2) + '%)' : '') - ) - ); -} - -function DisplayFile({file, onBack}) { - return e('div', {className: 'display-file'}, - e(FileHeader, {file, onBack}), - e(FileContent, {file}) - ); -} - -function FileHeader({file, onBack}) { - return e('div', {className: 'file-header'}, - onBack ? e('a', {className: 'file-header__back', onClick: onBack}, 'Back') : null, - e('div', {className: 'file-header__name'}, pathToString([...file.parent, ...file.path])), - e('div', {className: 'file-header__stat'}, - 'Covered: ' + file.covered + ' of ' + file.coverable + - (file.coverable ? ' (' + (file.covered / file.coverable * 100).toFixed(2) + '%)' : '') - ) - ); -} - -function FileContent({file}) { - return e('div', {className: 'file-content'}, - file.content.split(/\r?\n/).map((line, index) => { - const trace = file.traces.find(trace => trace.line === index + 1); - const covered = trace && trace.stats.Line; - const uncovered = trace && !trace.stats.Line; - return e('pre', { - className: 'code-line' - + (covered ? ' code-line_covered' : '') - + (uncovered ? ' code-line_uncovered' : ''), - title: trace ? JSON.stringify(trace.stats, null, 2) : null, - }, line); - }) - ); -} - -(function(){ - const commonPath = findCommonPath(data.files); - const files = data.files.map(file => ({...file, path: file.path.slice(commonPath.length), parent: commonPath})); - const children = findFolders(files); - - const root = { - is_folder: true, - children, - path: commonPath, - parent: [], - covered: children.reduce((sum, file) => sum + file.covered, 0), - coverable: children.reduce((sum, file) => sum + file.coverable, 0), - }; - - ReactDOM.render(e(App, {root}), document.getElementById('root')); -}()); -</script> -</body> -</html> \ No newline at end of file