Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 184-gva-rename-commontime-field-to-blockchaintime
  • Kiss/XXX-personal-difficulty
  • dev
  • dvermd/200-keypairs-dewif
  • elois/191-sled
  • elois/195
  • elois/195-bcdbwriteop
  • elois/deps-crypto
  • elois/exemple-gva-global-context
  • elois/gva-monetary-mass
  • elois/kv-parts-db
  • elois/use-rule-engine-example
  • elois/wot
  • elois/ws2pv2
  • hugo/151-ws2pv2-sync
  • jawaka/155-dbex-add-dump-fork-tree-command
  • ji_emme/181-gva-implement-identity-request
  • ji_emme/182-gva-implement-block-meta-data
  • ji_emme/89-implement-client-api-gva-graphql-verification-api
  • ji_emme/gva-humantimefield
  • ji_emme/rml14
  • jonas/141-durs-core-load-conf-from-environment-variables-as-a-priority
  • jonas/dev-gva
  • jonas/wip
  • logo
  • release/0.2
  • test-juniper-from-schema
  • tp/personal-diffi
  • tuxmain/84-add-block-checking-checking-all-rules-of-the-unit-protocol
  • vainamoinen197-transactiondocument-replace-vec-fields-by-smallvec-2
  • vindarel/feat/add-dbex-human-option
  • 0.1.0-a0.1
  • crypto/v0.1.0
  • crypto/v0.1.1
  • crypto/v0.1.2
  • crypto/v0.3.0-b1
  • crypto/v0.3.0-b2
  • crypto/v0.3.0-b3
  • crypto/v0.4.0-b1
  • documents/v0.1.1
  • documents/v0.10.0-b1
  • documents/v0.7.0
  • documents/v0.7.1
  • keys/v0.1.0
  • keys/v0.2.0
  • keys/v0.3.0
  • keys/v0.3.1-deprecated
  • protocol/v0.2.0
  • protocol/v0.3.0
  • protocol/v0.4.0
  • protocol/v0.4.1
  • v0.0.1
  • v0.0.1-a0.1
  • v0.0.1-a0.10
  • v0.0.1-a0.11
  • v0.0.1-a0.12
  • v0.0.1-a0.2
  • v0.0.1-a0.3
  • v0.0.1-a0.4
  • v0.0.1-a0.5
  • v0.0.1-a0.6
  • v0.0.1-a0.7
  • v0.0.1-a0.8
  • v0.0.1-a0.9
  • v0.1.1-a1
  • v0.2.0-a
  • v0.2.0-a2
  • v0.2.0-a4
  • wot/v0.8.0-a0.8
  • wot/v0.8.0-a0.9
  • wotb/0.7.0
  • wotb/0.7.1
  • wotb/v0.4.0
  • wotb/v0.4.1
  • wotb/v0.5.0
  • wotb/v0.6.0
  • wotb/v0.6.1
  • wotb/v0.8.0-a0.2
  • wotb/v0.8.0-a0.3
  • wotb/v0.8.0-a0.4
  • wotb/v0.8.0-a0.5
81 results

Target

Select target project
  • librelois/duniter-rs
  • ji_emme/duniter-rs
  • vindarel/duniter-rs
  • Hiroty1er/duniter-rs
  • dvermd/duniter-rs
  • 666titi999/duniter-rs
6 results
Select Git revision
  • Hiroty1er/173-personnal-diffi
  • Kiss/XXX-personal-difficulty
  • counter/122-keys-subcommand-create-keypair-from-seed
  • counter/123-gva-first-minimal-schema-with-identitydocument
  • dev
  • elois/172-fail-to-revert-block-with-transactions
  • elois/exemple-gva-global-context
  • elois/kv-parts-db
  • elois/use-rule-engine-example
  • elois/ws2pv2
  • hugo/151-ws2pv2-sync
  • jawaka-146-dubp-local-validation
  • ji_emme/181-gva-implement-identity-request
  • ji_emme/89-implement-client-api-gva-graphql-verification-api
  • jonas/141-durs-core-load-conf-from-environment-variables-as-a-priority
  • jonas/dev-gva
  • jonas/wip
  • lmdb-ref
  • logo
  • release/0.2
  • test-juniper-from-schema
  • tp/personal-diffi
  • tuxmain/84-add-block-checking-checking-all-rules-of-the-unit-protocol
  • vindarel/feat/add-dbex-human-option
  • 0.1.0-a0.1
  • crypto/v0.1.0
  • crypto/v0.1.1
  • crypto/v0.1.2
  • crypto/v0.3.0-b1
  • crypto/v0.3.0-b2
  • crypto/v0.3.0-b3
  • crypto/v0.4.0-b1
  • documents/v0.1.1
  • documents/v0.10.0-b1
  • documents/v0.7.0
  • documents/v0.7.1
  • keys/v0.1.0
  • keys/v0.2.0
  • keys/v0.3.0
  • keys/v0.3.1-deprecated
  • protocol/v0.2.0
  • protocol/v0.3.0
  • protocol/v0.4.0
  • protocol/v0.4.1
  • v0.0.1
  • v0.0.1-a0.1
  • v0.0.1-a0.10
  • v0.0.1-a0.11
  • v0.0.1-a0.12
  • v0.0.1-a0.2
  • v0.0.1-a0.3
  • v0.0.1-a0.4
  • v0.0.1-a0.5
  • v0.0.1-a0.6
  • v0.0.1-a0.7
  • v0.0.1-a0.8
  • v0.0.1-a0.9
  • v0.1.1-a1
  • v0.2.0-a
  • v0.2.0-a2
  • v0.2.0-a4
  • wot/v0.8.0-a0.8
  • wot/v0.8.0-a0.9
  • wotb/0.7.0
  • wotb/0.7.1
  • wotb/v0.4.0
  • wotb/v0.4.1
  • wotb/v0.5.0
  • wotb/v0.6.0
  • wotb/v0.6.1
  • wotb/v0.8.0-a0.2
  • wotb/v0.8.0-a0.3
  • wotb/v0.8.0-a0.4
  • wotb/v0.8.0-a0.5
74 results
Show changes
Commits on Source (147)
Showing
with 3084 additions and 2675 deletions
......@@ -78,7 +78,7 @@ skip_ci:
.rust_stable_win64:
extends: .ci_conditions
image: registry.duniter.org/docker/rust/win64-builder:v1.39.0
image: registry.duniter.org/docker/rust/win64-builder:v1.41.0
tags:
- redshift-docker-runner
before_script:
......@@ -100,8 +100,7 @@ tests:linux64:stable:
script:
- cd bin/dunitrust-server
- RUSTFLAGS="-D warnings" cargo build --features=ssl
- cargo test --all --exclude durs-gva
- cargo test --package durs-gva -- --test-threads=1
- cargo test --all
- cargo test --all -- --ignored
tests:arm-v7-:stable:
......@@ -131,28 +130,7 @@ tests:win64:stable:
script:
- cd bin/dunitrust-server
- RUSTFLAGS="-D warnings" cargo build --target=x86_64-pc-windows-gnu
- cargo test --package dunitrust --target=x86_64-pc-windows-gnu
- cargo test --package durs-conf --target=x86_64-pc-windows-gnu
- cargo test --package durs-core --target=x86_64-pc-windows-gnu
- cargo test --package durs-message --target=x86_64-pc-windows-gnu
- cargo test --package durs-module --target=x86_64-pc-windows-gnu
- cargo test --package durs-network --target=x86_64-pc-windows-gnu
- cargo test --package durs-bc-db-reader --target=x86_64-pc-windows-gnu
- cargo test --package durs-bc-db-writer --target=x86_64-pc-windows-gnu
- cargo test --package durs-blockchain --target=x86_64-pc-windows-gnu
- cargo test --package durs-dbs-tools --target=x86_64-pc-windows-gnu
#- cargo test --package durs-skeleton-module --target=x86_64-pc-windows-gnu
- cargo test --package durs-gva --target=x86_64-pc-windows-gnu -- --test-threads=1
- cargo test --package durs-ws2p-v1-legacy --target=x86_64-pc-windows-gnu
- cargo test --package durs-ws2p --target=x86_64-pc-windows-gnu
- cargo test --package durs-ws2p-messages --target=x86_64-pc-windows-gnu
- cargo test --package dup-crypto --target=x86_64-pc-windows-gnu
- cargo test --package durs-common-tools --target=x86_64-pc-windows-gnu
- cargo test --package dubp-user-docs --target=x86_64-pc-windows-gnu
- cargo test --package json-pest-parser --target=x86_64-pc-windows-gnu
- cargo test --package durs-network-documents --target=x86_64-pc-windows-gnu
- cargo test --package rules-engine --target=x86_64-pc-windows-gnu
- cargo test --package durs-wot --target=x86_64-pc-windows-gnu
- cargo test --all
clippy:
extends: .rust_stable_lin64
......@@ -160,30 +138,15 @@ clippy:
- cargo clippy -- -V
stage: quality
script:
- cargo clippy --all -- -D warnings --verbose
- cargo clippy --all --tests -- -D warnings --verbose
audit:manual:
audit_dependencies:
extends: .rust_stable_lin64
before_script:
- cargo install --force cargo-audit
- cargo deny -V
stage: quality
script:
- cargo audit
when: manual
except:
refs:
- dev
audit:
extends: .rust_stable_lin64
before_script:
- cargo install --force cargo-audit
stage: quality
script:
- cargo audit
only:
refs:
- dev
- cargo deny check
publish:crate:
extends: .rust_stable_lin64
......@@ -212,6 +175,22 @@ package:test:lin64:deb:
- work/bin/
expire_in: 1 weeks
package:dev:lin64:deb:
extends: .rust_stable_lin64
stage: package
only:
refs:
- dev
except:
refs:
- tags
script:
- bash "release/arch/linux-x64/build-deb.sh" "dev"
artifacts:
paths:
- work/bin/
expire_in: 1 weeks
package:test:lin64:arch:
extends: .rust_stable_lin64_arch
stage: package
......@@ -257,16 +236,16 @@ package:test:docker-test-image:
variables:
IMAGE_TAG: "test-image"
package:test:docker:
extends: .docker-build-app-image
only:
refs:
- dev
except:
refs:
- tags
variables:
IMAGE_TAG: "dev"
#package:dev:docker:
# extends: .docker-build-app-image
# only:
# refs:
# - dev
# except:
# refs:
# - tags
# variables:
# IMAGE_TAG: "dev"
package:test:armv7:
extends: .rust_stable_armv7
......
This diff is collapsed.
......@@ -6,7 +6,6 @@ members = [
"lib/core/message",
"lib/core/module",
"lib/core/network",
"lib/crypto",
"lib/dubp/block-doc",
"lib/dubp/common-doc",
"lib/dubp/currency-params",
......@@ -30,7 +29,6 @@ members = [
"lib/tools/common-tools",
"lib/tools/dbs-tools",
"lib/tools/json-pest-parser",
"lib/tools/pkstl",
"lib/tools/rules-engine",
]
......
......@@ -5,7 +5,7 @@
[![Latest Version](https://img.shields.io/badge/latest-v0.1.1--a1-orange.svg)](https://git.duniter.org/nodes/rust/duniter-rs/tags/v0.1.1-a1)
[![docs](https://librelois.fr/img/docs-read%20now-green.svg)](https://nodes.duniter.io/rust/duniter-rs/dunitrust/)
[![build status](https://git.duniter.org/nodes/rust/duniter-rs/badges/dev/build.svg)](https://git.duniter.org/nodes/rust/duniter-rs/commits/dev)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.37.0+-yellow.svg)](https://github.com/rust-lang/rust/blob/master/RELEASES.md)
[![Minimum rustc version](https://img.shields.io/badge/rustc-1.41.0+-yellow.svg)](https://github.com/rust-lang/rust/blob/master/RELEASES.md)
## What is Duniter
......
......@@ -14,18 +14,20 @@ edition = "2018"
[dependencies]
durs-network = { path = "../../lib/core/network" }
durs-core = { path = "../../lib/core/core" }
durs-gva = { path = "../../lib/modules/gva" }
durs-module = { path = "../../lib/core/module" }
#durs-skeleton = { path = "../../lib/modules/skeleton" }
durs-ws2p = { path = "../../lib/modules/ws2p/ws2p" }
durs-ws2p-v1-legacy = { path = "../../lib/modules/ws2p-v1-legacy" }
human-panic = "1.0.1"
#human-panic = "1.0.1"
log = "0.4.8"
structopt= "0.3.4"
structopt= "0.3.9"
[target.'cfg(unix)'.dependencies]
durs-tui = { path = "../../lib/modules/tui" }
[target.'cfg(not(target_arch = "arm"))'.dependencies]
durs-gva = { path = "../../lib/modules/gva" }
[features]
ssl = ["durs-ws2p-v1-legacy/ssl"]
......@@ -50,8 +52,9 @@ section = "misc"
priority = "optional"
assets = [
["../../target/armv7-unknown-linux-gnueabihf/release/dunitrust", "usr/bin/", "755"],
["../../images/duniter-rs.png", "usr/share/dunitrust/", "644"],
["../../images/dunitrust.png", "usr/share/dunitrust/", "644"],
]
default-features = false
features = ["ssl"]
[package.metadata.arch]
......
......@@ -25,6 +25,7 @@ use durs_core::commands::{
};
use durs_core::errors::DursCoreError;
use durs_core::DursCore;
#[cfg(not(target_arch = "arm"))]
use durs_gva::{GvaModule, GvaOpt};
use durs_network::cli::sync::SyncOpt;
use durs_ws2p_v1_legacy::{WS2POpt, WS2Pv1Module};
......@@ -32,6 +33,11 @@ use log::Level;
use std::path::PathBuf;
use structopt::StructOpt;
// Default log level is "info".
const LOG_LEVEL_DEFAULT: usize = 2;
static LOG_LEVEL_NAMES: [&str; 5] = ["error", "warn", "info", "debug", "trace"];
#[derive(StructOpt, Debug)]
#[structopt(name = "durs", setting(structopt::clap::AppSettings::ColoredHelp))]
/// Dunitrust command line options
......@@ -45,10 +51,9 @@ pub struct DursCliOpt {
/// Keypairs file path
#[structopt(long = "keypairs-file", parse(from_os_str))]
keypairs_file: Option<PathBuf>,
/// Set log level. (Defaults to INFO).
/// Available levels: [ERROR, WARN, INFO, DEBUG, TRACE]
#[structopt(short = "l", long = "logs", next_line_help = true)]
logs_level: Option<Level>,
/// Change logs level.
#[structopt(short = "l", long = "logs", default_value = LOG_LEVEL_NAMES[LOG_LEVEL_DEFAULT], possible_values = &LOG_LEVEL_NAMES)]
logs_level: Level,
/// Print logs in standard output
#[structopt(long = "log-stdout")]
log_stdout: bool,
......@@ -69,6 +74,7 @@ impl ExecutableModuleCommand for DursCliOpt {
env!("CARGO_PKG_VERSION"),
)
}
#[cfg(not(target_arch = "arm"))]
DursCliSubCommand::Gva(module_opts) => DursCore::execute_module_command::<GvaModule>(
options,
module_opts,
......@@ -164,6 +170,7 @@ pub enum DursCliSubCommand {
#[structopt(name = "sync", setting(structopt::clap::AppSettings::ColoredHelp))]
SyncOpt(SyncOpt),
/// GVA module subcommand
#[cfg(not(target_arch = "arm"))]
#[structopt(name = "gva", setting(structopt::clap::AppSettings::ColoredHelp))]
Gva(GvaOpt),
/// WS2P1 module subcommand
......
......@@ -16,6 +16,8 @@
//! Main function for classic Dunitrust nodes (no specialization).
#![deny(
clippy::option_unwrap_used,
clippy::result_unwrap_used,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
......@@ -33,12 +35,12 @@ mod init;
use crate::cli::DursCliOpt;
use crate::init::init;
use durs_core::durs_plug;
use log::error;
use structopt::StructOpt;
#[cfg(not(target_arch = "arm"))]
pub use durs_gva::GvaModule;
#[cfg(unix)]
pub use durs_tui::TuiModule;
use log::error;
use structopt::StructOpt;
//pub use durs_skeleton::SkeletonModule;
pub use durs_ws2p::WS2PModule;
pub use durs_ws2p_v1_legacy::WS2Pv1Module;
......
[bans]
multiple-versions = "warn"
deny = [
# color-backtrace is nice but brings in too many dependencies and that are often outdated, so not worth it for us.
{ name = "color-backtrace" },
# deprecated
{ name = "quickersort" },
# term is not fully maintained, and termcolor is replacing it
{ name = "term" },
]
skip-tree = [
{ name = "winapi", version = "<= 0.3" },
{ name = "autocfg", version = "<= 1" },
]
[licenses]
unlicensed = "deny"
# We want really high confidence when inferring licenses from text
confidence-threshold = 0.92
allow = [
"AGPL-3.0",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"CC0-1.0",
"ISC",
"MIT",
"MPL-2.0",
"OpenSSL",
"Zlib"
]
[[licenses.clarify]]
name = "ring"
# SPDX considers OpenSSL to encompass both the OpenSSL and SSLeay licenses
# https://spdx.org/licenses/OpenSSL.html
# ISC - Both BoringSSL and ring use this for their new files
# MIT - "Files in third_party/ have their own licenses, as described therein. The MIT
# license, for third_party/fiat, which, unlike other third_party directories, is
# compiled into non-test libraries, is included below."
# OpenSSL - Obviously
expression = "ISC AND MIT AND OpenSSL"
license-files = [
{ path = "LICENSE", hash = 0xbd0eed23 },
]
[sources]
unknown-registry = "deny"
unknown-git = "deny"
......@@ -159,7 +159,7 @@ You just have to choose one of the three variants and return it. For example, if
```rust
fn priority() -> ModulePriority {
ModulePriority::Optional()
ModulePriority::Optional
}
```
......@@ -178,7 +178,7 @@ As above, you just have to return a variant. For example, if you don't need any
```rust
fn ask_required_keys() -> RequiredKeys {
RequiredKeys::None()
RequiredKeys::None
}
```
......
......@@ -168,7 +168,7 @@ Il suffit de choisir la variante de l'énumération qui vous convient puis de la
```rust
fn priority() -> ModulePriority {
ModulePriority::Optional()
ModulePriority::Optional
}
```
......@@ -187,7 +187,7 @@ Il suffit de choisir la variante de l'énumération qui vous convient puis de la
```rust
fn ask_required_keys() -> RequiredKeys {
RequiredKeys::None()
RequiredKeys::None
}
```
......
......@@ -10,17 +10,26 @@ edition = "2018"
path = "src/lib.rs"
[dependencies]
dirs = "1.0.2"
dup-crypto = { path = "../../crypto" }
dirs = "2.0.2"
dup-crypto = "0.8.4"
dubp-currency-params = { path = "../../dubp/currency-params" }
dubp-user-docs= { path = "../../dubp/user-docs" }
durs-message = { path = "../message" }
durs-module = { path = "../module" }
durs-common-tools = { path = "../../tools/common-tools" }
envy = "0.4.1"
failure = "0.1.5"
log = "0.4.*"
rpassword = "1.0.0"
rpassword = "4.0.3"
serde = "1.0.*"
serde_derive = "1.0.*"
serde_json = "1.0.*"
unwrap = "1.2.1"
[dev-dependencies]
durs-module = { path = "../module", features = ["module-test"] }
maplit = "1.0.2"
mockall = { version = "0.6.0"}
once_cell = "1.3.1"
[features]
......@@ -15,20 +15,26 @@
//! Dunitrust configuration constants
/// User datas folder
/// User datas folder.
pub static USER_DATAS_FOLDER: &str = "durs-dev";
/// Configuration filename
/// Configuration filename.
pub static CONF_FILENAME: &str = "conf.json";
/// Keypairs filename
/// Keypairs filename.
pub static KEYPAIRS_FILENAME: &str = "keypairs.json";
/// If no currency is specified by the user, is the currency will be chosen by default
/// If no currency is specified by the user, is the currency will be chosen by default.
pub static DEFAULT_CURRENCY: &str = "g1";
/// Default value for `default_sync_module` conf field
/// Default value for `default_sync_module` conf field.
pub static DEFAULT_DEFAULT_SYNC_MODULE: &str = "ws2p";
/// Modules datas folder
/// Modules datas folder.
pub static MODULES_DATAS_FOLDER: &str = "datas";
/// Prefix for dunitrust environment variables.
pub static DURS_ENV_PREFIX: &str = "DURS_";
/// Name of the environment variable that indicates the version of the configuration.
pub static DURS_CONF_VERSION: &str = "DURS_CONF_VERSION";
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Dunitrust configuration from environment variables
use crate::constants;
use crate::errors::DursConfEnvError;
use crate::global_conf::v2::DuRsGlobalUserConfV2;
use crate::global_conf::DuRsGlobalUserConf;
use crate::resources::ResourcesUsage;
/// Load global user configuration from environment variables
pub fn load_env_global_user_conf() -> Result<DuRsGlobalUserConf, DursConfEnvError> {
if let Ok(conf_version) = std::env::var(constants::DURS_CONF_VERSION) {
match conf_version
.parse::<usize>()
.map_err(DursConfEnvError::ConfVersionParseErr)?
{
2 => {
let resources_usage =
envy::prefixed(&format!("{}RESOURCES_USAGE_", constants::DURS_ENV_PREFIX))
.from_env::<ResourcesUsage>()
.map_err(DursConfEnvError::EnvyErr)?;
let mut global_user_conf_v2 = envy::prefixed(constants::DURS_ENV_PREFIX)
.from_env::<DuRsGlobalUserConfV2>()
.map_err(DursConfEnvError::EnvyErr)?;
global_user_conf_v2.resources_usage = Some(resources_usage);
Ok(DuRsGlobalUserConf::V2(global_user_conf_v2))
}
v => Err(DursConfEnvError::UnsupportedVersion {
expected: vec![2],
found: v,
}),
}
} else {
Ok(DuRsGlobalUserConf::V2(DuRsGlobalUserConfV2::default()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::resources::ResourceUsage;
use dubp_currency_params::CurrencyName;
use durs_module::ModuleName;
use maplit::hashset;
use once_cell::sync::Lazy;
use std::sync::Mutex;
// Empty mutex used to ensure that only one test runs at a time
static MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
#[test]
fn test_env_conf_without_env_vars() -> Result<(), DursConfEnvError> {
let _lock = MUTEX.lock().expect("MUTEX poisoned");
std::env::remove_var(constants::DURS_CONF_VERSION);
assert_eq!(
DuRsGlobalUserConf::V2(DuRsGlobalUserConfV2::default()),
load_env_global_user_conf()?,
);
Ok(())
}
#[test]
fn test_env_conf_with_unsupported_conf_version_var() -> Result<(), DursConfEnvError> {
let _lock = MUTEX.lock().expect("MUTEX poisoned");
std::env::set_var(constants::DURS_CONF_VERSION, "3");
if let Err(DursConfEnvError::UnsupportedVersion { .. }) = load_env_global_user_conf() {
Ok(())
} else {
panic!("load_env_global_user_conf() must return an error DursConfEnvError::UnsupportedVersion.");
}
}
#[test]
fn test_env_conf_with_some_valid_env_vars() -> Result<(), DursConfEnvError> {
let _lock = MUTEX.lock().expect("MUTEX poisoned");
std::env::set_var(constants::DURS_CONF_VERSION, "2");
std::env::set_var(&format!("{}CURRENCY", constants::DURS_ENV_PREFIX), "g1");
std::env::set_var(
&format!("{}DISABLED", constants::DURS_ENV_PREFIX),
"tui,gva",
);
std::env::set_var(
&format!("{}RESOURCES_USAGE_MEMORY_USAGE", constants::DURS_ENV_PREFIX),
"medium",
);
assert_eq!(
DuRsGlobalUserConf::V2(DuRsGlobalUserConfV2 {
currency: Some(CurrencyName(String::from("g1"))),
my_node_id: None,
default_sync_module: None,
resources_usage: Some(ResourcesUsage {
cpu_usage: ResourceUsage::Large,
network_usage: ResourceUsage::Large,
memory_usage: ResourceUsage::Medium,
disk_space_usage: ResourceUsage::Large,
}),
disabled: Some(hashset![
ModuleName("tui".to_owned()),
ModuleName("gva".to_owned())
]),
enabled: None,
}),
load_env_global_user_conf()?,
);
Ok(())
}
#[test]
fn test_env_conf_with_invalid_conf_version_var() -> Result<(), DursConfEnvError> {
let _lock = MUTEX.lock().expect("MUTEX poisoned");
std::env::set_var(constants::DURS_CONF_VERSION, "str");
if let Err(DursConfEnvError::ConfVersionParseErr(_)) = load_env_global_user_conf() {
Ok(())
} else {
panic!("load_env_global_user_conf() must return an error DursConfEnvError::ConfVersionParseErr.");
}
}
}
......@@ -13,51 +13,53 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Provide base16 convertion tools
//! Dunitrust configuration errors
use crate::bases::BaseConvertionError;
use failure::Fail;
/// Convert a hexadecimal string in an array of 32 bytes.
///
/// The hex string must only contains hex characters
/// and produce a 32 bytes value.
pub fn str_hex_to_32bytes(text: &str) -> Result<[u8; 32], BaseConvertionError> {
if text.len() != 64 {
Err(BaseConvertionError::InvalidLength {
expected: 64,
found: text.len(),
})
} else {
let mut bytes = [0u8; 32];
let chars: Vec<char> = text.chars().collect();
for i in 0..64 {
if i % 2 != 0 {
continue;
/// Error with configuration file
#[derive(Debug, Fail)]
pub enum DursConfError {
/// Env var error
#[fail(display = "fail to parse configuration file: {}", _0)]
EnvVarErr(DursConfEnvError),
/// File error
#[fail(display = "{}", _0)]
FileErr(DursConfFileError),
}
let byte1 = chars[i].to_digit(16);
let byte2 = chars[i + 1].to_digit(16);
if let Some(byte1) = byte1 {
if let Some(byte2) = byte2 {
let byte = ((byte1 as u8) << 4) | byte2 as u8;
bytes[i / 2] = byte;
} else {
return Err(BaseConvertionError::InvalidCharacter {
character: chars[i + 1],
offset: i + 1,
});
}
} else {
return Err(BaseConvertionError::InvalidCharacter {
character: chars[i],
offset: i,
});
}
/// Error with configuration file
#[derive(Debug, Fail)]
pub enum DursConfEnvError {
/// Fail to parse conf version
#[fail(display = "Fail to parse conf version : {}.", _0)]
ConfVersionParseErr(std::num::ParseIntError),
/// Envy error
#[fail(display = "{}", _0)]
EnvyErr(envy::Error),
/// Unsupported version
#[fail(
display = "Version {} not supported. List of supported versions : {:?}.",
found, expected
)]
UnsupportedVersion {
/// List of supported versions
expected: Vec<usize>,
/// Version found
found: usize,
},
}
Ok(bytes)
}
/// Error with configuration file
#[derive(Debug, Fail)]
pub enum DursConfFileError {
/// Read error
#[fail(display = "fail to read configuration file: {}", _0)]
ReadError(std::io::Error),
/// Parse error
#[fail(display = "fail to parse configuration file: {}", _0)]
ParseError(serde_json::Error),
/// Write error
#[fail(display = "fail to write configuration file: {}", _0)]
WriteError(std::io::Error),
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Dunitrust configuration file
use crate::constants;
use crate::errors::DursConfFileError;
use crate::DuRsConf;
use durs_module::DursConfTrait;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
#[inline]
/// Return path to configuration file
pub fn get_conf_path(profile_path: &PathBuf) -> PathBuf {
let mut conf_path = profile_path.clone();
conf_path.push(constants::CONF_FILENAME);
conf_path
}
/// Load configuration from file
pub fn load_conf_from_file(mut conf_file_path: PathBuf) -> Result<DuRsConf, DursConfFileError> {
// Open conf file
conf_file_path.push(constants::CONF_FILENAME);
if conf_file_path.as_path().exists() {
match File::open(conf_file_path.as_path()) {
Ok(mut f) => {
let mut contents = String::new();
f.read_to_string(&mut contents)
.map_err(DursConfFileError::ReadError)?;
// Parse conf file
let conf: DuRsConf =
serde_json::from_str(&contents).map_err(DursConfFileError::ParseError)?;
// Upgrade conf to latest version
let (conf, upgraded) = conf.upgrade();
// If conf is upgraded, rewrite conf file
if upgraded {
write_conf_file(conf_file_path.as_path(), &conf)
.map_err(DursConfFileError::WriteError)?;
}
Ok(conf)
}
Err(e) => Err(DursConfFileError::ReadError(e)),
}
} else {
// Create conf file with default conf
let conf = DuRsConf::default();
write_conf_file(conf_file_path.as_path(), &conf)
.unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to write default conf file!")));
Ok(conf)
}
}
/// Save configuration in profile folder
pub fn write_conf_file<DC: DursConfTrait>(
conf_path: &Path,
conf: &DC,
) -> Result<(), std::io::Error> {
let mut f = File::create(conf_path)?;
f.write_all(
serde_json::to_string_pretty(conf)
.expect("Fatal error : fail to write default conf file !")
.as_bytes(),
)?;
f.sync_all()?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[inline]
fn save_old_conf(profile_path: PathBuf) -> std::io::Result<()> {
let mut conf_path = profile_path.clone();
conf_path.push(constants::CONF_FILENAME);
let mut conf_sav_path = profile_path;
conf_sav_path.push("conf-sav.json");
std::fs::copy(conf_path.as_path(), conf_sav_path.as_path())?;
Ok(())
}
fn restore_old_conf_and_save_upgraded_conf(profile_path: PathBuf) -> std::io::Result<()> {
let mut conf_path = profile_path.clone();
conf_path.push(constants::CONF_FILENAME);
let mut conf_sav_path = profile_path.clone();
conf_sav_path.push("conf-sav.json");
let mut conf_upgraded_path = profile_path;
conf_upgraded_path.push("conf-upgraded.json");
std::fs::copy(conf_path.as_path(), &conf_upgraded_path.as_path())?;
std::fs::copy(conf_sav_path.as_path(), &conf_path.as_path())?;
std::fs::remove_file(conf_sav_path.as_path())?;
Ok(())
}
#[test]
fn load_conf_file_v1() -> Result<(), DursConfFileError> {
let profile_path = PathBuf::from("./test/v1/");
save_old_conf(profile_path.clone()).map_err(DursConfFileError::WriteError)?;
let conf = load_conf_from_file(profile_path.clone())?;
assert_eq!(
conf.modules()
.get("ws2p")
.expect("Not found ws2p conf")
.clone(),
json!({
"sync_endpoints": [
{
"endpoint": "WS2P c1c39a0a i3.ifee.fr 80 /ws2p",
"pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx"
},
{
"endpoint": "WS2P 15af24db g1.ifee.fr 80 /ws2p",
"pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG"
},
{
"endpoint": "WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p",
"pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef"
}
]
})
);
restore_old_conf_and_save_upgraded_conf(profile_path)
.map_err(DursConfFileError::WriteError)?;
Ok(())
}
#[test]
fn load_conf_file_v2() -> Result<(), DursConfFileError> {
let profile_path = PathBuf::from("./test/v2/");
let conf = load_conf_from_file(profile_path)?;
assert_eq!(
conf.modules()
.get("ws2p")
.expect("Not found ws2p conf")
.clone(),
json!({
"sync_endpoints": [
{
"endpoint": "WS2P c1c39a0a i3.ifee.fr 80 /ws2p",
"pubkey": "D9D2zaJoWYWveii1JRYLVK3J4Z7ZH3QczoKrnQeiM6mx"
},
{
"endpoint": "WS2P 15af24db g1.ifee.fr 80 /ws2p",
"pubkey": "BoZP6aqtErHjiKLosLrQxBafi4ATciyDZQ6XRQkNefqG"
},
{
"endpoint": "WS2P b48824f0 g1.monnaielibreoccitanie.org 80 /ws2p",
"pubkey": "7v2J4badvfWQ6qwRdCwhhJfAsmKwoxRUNpJHiJHj7zef"
}
]
})
);
Ok(())
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Dunitrust global configuration
pub mod v2;
use durs_common_tools::fatal_error;
use durs_module::{DursGlobalConfTrait, ModuleName};
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
/// Dunitrust global configuration (without modules configuration)
pub enum DuRsGlobalConf {
/// Dunitrust global configuration v1
V1(crate::v1::DuRsConfV1),
/// Dunitrust global configuration v2
V2(v2::DuRsGlobalConfV2),
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
/// Dunitrust global configuration (without modules configuration)
pub enum DuRsGlobalUserConf {
/// Dunitrust global user configuration v2
V2(v2::DuRsGlobalUserConfV2),
}
impl DursGlobalConfTrait for DuRsGlobalConf {
type GlobalUserConf = DuRsGlobalUserConf;
fn my_node_id(&self) -> u32 {
match *self {
DuRsGlobalConf::V1(ref conf_v1) => conf_v1.my_node_id,
DuRsGlobalConf::V2(ref conf_v2) => conf_v2.my_node_id,
}
}
fn default_sync_module(&self) -> ModuleName {
match *self {
DuRsGlobalConf::V1(_) => {
fatal_error!("Feature default_sync_module not exist in durs conf v1 !")
}
DuRsGlobalConf::V2(ref conf_v2) => conf_v2.default_sync_module.clone(),
}
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Dunitrust global configuration V2
use crate::constants;
use crate::resources::ResourcesUsage;
use crate::v1::DuRsConfV1;
use dubp_currency_params::CurrencyName;
use durs_module::ModuleName;
use std::collections::HashSet;
#[derive(Debug, Default, Clone, Deserialize, PartialEq, Serialize)]
/// Dunitrust configuration v2
pub struct DuRsGlobalUserConfV2 {
/// Currency name
pub currency: Option<CurrencyName>,
/// Node unique identifier
pub my_node_id: Option<u32>,
/// Name of the module used by default for synchronization
pub default_sync_module: Option<ModuleName>,
/// Ressources usage
pub resources_usage: Option<ResourcesUsage>,
/// Disabled modules
pub disabled: Option<HashSet<ModuleName>>,
/// Enabled modules
pub enabled: Option<HashSet<ModuleName>>,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Serialize)]
/// Dunitrust configuration v2
pub struct DuRsGlobalConfV2 {
/// Currency name
pub currency: CurrencyName,
/// Duniter node unique identifier
pub my_node_id: u32,
/// Name of the module used by default for synchronization
pub default_sync_module: ModuleName,
/// Ressources usage
pub resources_usage: ResourcesUsage,
/// Disabled modules
pub disabled: HashSet<ModuleName>,
/// Enabled modules
pub enabled: HashSet<ModuleName>,
}
impl Default for DuRsGlobalConfV2 {
fn default() -> Self {
DuRsGlobalConfV2 {
currency: CurrencyName(String::from(constants::DEFAULT_CURRENCY)),
my_node_id: crate::generate_random_node_id(),
default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)),
resources_usage: ResourcesUsage::default(),
disabled: HashSet::with_capacity(0),
enabled: HashSet::with_capacity(0),
}
}
}
impl From<DuRsConfV1> for DuRsGlobalConfV2 {
fn from(conf_v1: DuRsConfV1) -> Self {
DuRsGlobalConfV2 {
currency: conf_v1.currency,
my_node_id: conf_v1.my_node_id,
default_sync_module: ModuleName(String::from(constants::DEFAULT_DEFAULT_SYNC_MODULE)),
resources_usage: ResourcesUsage::default(),
disabled: conf_v1.disabled,
enabled: conf_v1.enabled,
}
}
}
impl DuRsGlobalConfV2 {
/// Override configuration with user configuration
pub fn r#override(self, global_user_conf: DuRsGlobalUserConfV2) -> Self {
DuRsGlobalConfV2 {
currency: global_user_conf.currency.unwrap_or(self.currency),
my_node_id: global_user_conf.my_node_id.unwrap_or(self.my_node_id),
default_sync_module: global_user_conf
.default_sync_module
.unwrap_or(self.default_sync_module),
resources_usage: global_user_conf
.resources_usage
.unwrap_or(self.resources_usage),
disabled: global_user_conf.disabled.unwrap_or(self.disabled),
enabled: global_user_conf.enabled.unwrap_or(self.enabled),
}
}
}
impl From<DuRsGlobalUserConfV2> for DuRsGlobalConfV2 {
fn from(global_user_conf: DuRsGlobalUserConfV2) -> Self {
Self::default().r#override(global_user_conf)
}
}
// Copyright (C) 2017-2019 The AXIOM TEAM Association.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Dunitrust keypairs
pub mod cli;
use crate::constants;
use crate::errors::DursConfError;
use dup_crypto::keys::*;
use durs_module::{RequiredKeys, RequiredKeysContent};
use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
#[derive(Debug, Clone, PartialEq, Eq)]
/// Keypairs filled in by the user (via a file or by direct entry in the terminal).
pub struct DuniterKeyPairs {
/// 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.
pub network_keypair: KeyPairEnum,
/// Keypair used to sign the blocks forged by this node. If this keypair is'nt filled in, the node will not calculate blocks.
pub member_keypair: Option<KeyPairEnum>,
}
impl Serialize for DuniterKeyPairs {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let member_seed = if let Some(ref member_keypair) = self.member_keypair {
member_keypair.seed().to_string()
} else {
String::from("")
};
let member_pub = if let Some(ref member_keypair) = self.member_keypair {
member_keypair.public_key().to_string()
} else {
String::from("")
};
let mut state = serializer.serialize_struct("DuniterKeyPairs", 4)?;
state.serialize_field(
"network_seed",
&self.network_keypair.seed().to_string().as_str(),
)?;
state.serialize_field(
"network_pub",
&self.network_keypair.public_key().to_string().as_str(),
)?;
state.serialize_field("member_seed", member_seed.as_str())?;
state.serialize_field("member_pub", member_pub.as_str())?;
state.end()
}
}
impl DuniterKeyPairs {
/// Returns only the keys indicated as required
pub fn get_required_keys_content(
required_keys: RequiredKeys,
keypairs: DuniterKeyPairs,
) -> RequiredKeysContent {
match required_keys {
RequiredKeys::MemberKeyPair => {
RequiredKeysContent::MemberKeyPair(keypairs.member_keypair)
}
RequiredKeys::MemberPublicKey => {
RequiredKeysContent::MemberPublicKey(if let Some(keys) = keypairs.member_keypair {
Some(keys.public_key())
} else {
None
})
}
RequiredKeys::NetworkKeyPair => {
RequiredKeysContent::NetworkKeyPair(keypairs.network_keypair)
}
RequiredKeys::NetworkPublicKey => {
RequiredKeysContent::NetworkPublicKey(keypairs.network_keypair.public_key())
}
RequiredKeys::None => RequiredKeysContent::None,
}
}
}
/// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !
fn generate_random_keypair(algo: KeysAlgo) -> KeyPairEnum {
match algo {
KeysAlgo::Ed25519 => KeyPairEnum::Ed25519(
ed25519::Ed25519KeyPair::generate_random().expect("unspecified rand error"),
),
KeysAlgo::Schnorr => panic!("Schnorr algo not yet supported !"),
}
}
/// Save keypairs in profile folder
// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !
pub fn write_keypairs_file(
file_path: &PathBuf,
keypairs: &DuniterKeyPairs,
) -> Result<(), std::io::Error> {
let mut f = File::create(file_path.as_path())?;
f.write_all(
serde_json::to_string_pretty(keypairs)
.unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to deserialize keypairs !")))
.as_bytes(),
)?;
f.sync_all()?;
Ok(())
}
/// Load keypairs from file
pub fn load_keypairs_from_file(
profile_path: &PathBuf,
keypairs_file_path: &Option<PathBuf>,
) -> Result<DuniterKeyPairs, DursConfError> {
// Get KeyPairs
let keypairs_path = if let Some(ref keypairs_file_path) = keypairs_file_path {
keypairs_file_path.clone()
} else {
let mut keypairs_path = profile_path.clone();
keypairs_path.push(constants::KEYPAIRS_FILENAME);
keypairs_path
};
if keypairs_path.as_path().exists() {
if let Ok(mut f) = File::open(keypairs_path.as_path()) {
let mut contents = String::new();
if f.read_to_string(&mut contents).is_ok() {
let json_conf: serde_json::Value =
serde_json::from_str(&contents).expect("Conf: Fail to parse keypairs file !");
if let Some(network_seed) = json_conf.get("network_seed") {
if let Some(network_pub) = json_conf.get("network_pub") {
let network_seed = network_seed
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let network_pub = network_pub
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let network_keypair = KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(network_seed)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(network_pub)
.expect("conf : keypairs file : fail to parse network_pub !"),
});
let member_keypair = if let Some(member_seed) = json_conf.get("member_seed")
{
if let Some(member_pub) = json_conf.get("member_pub") {
let member_seed = member_seed
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let member_pub = member_pub
.as_str()
.expect("Conf: Fail to parse keypairs file !");
if member_seed.is_empty() || member_pub.is_empty() {
None
} else {
Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(member_seed).expect(
"conf : keypairs file : fail to parse member_seed !",
),
pubkey: ed25519::PublicKey::from_base58(member_pub).expect(
"conf : keypairs file : fail to parse member_pub !",
),
}))
}
} else {
panic!("Fatal error : keypairs file wrong format : no field member_pub !")
}
} else {
panic!(
"Fatal error : keypairs file wrong format : no field member_seed !"
)
};
// Return keypairs
Ok(DuniterKeyPairs {
network_keypair,
member_keypair,
})
} else {
panic!("Fatal error : keypairs file wrong format : no field salt !")
}
} else {
panic!("Fatal error : keypairs file wrong format : no field password !")
}
} else {
panic!("Fail to read keypairs file !");
}
} else {
panic!("Fail to open keypairs file !");
}
} else {
// Create keypairs file with random keypair
let keypairs = DuniterKeyPairs {
network_keypair: generate_random_keypair(KeysAlgo::Ed25519),
member_keypair: None,
};
write_keypairs_file(&keypairs_path, &keypairs).unwrap_or_else(|_| {
panic!(dbg!("Fatal error : fail to write default keypairs file !"))
});
Ok(keypairs)
}
}
......@@ -13,20 +13,40 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Dunitrust keys configuration module
//! Dunitrust keypairs cli commands
#![deny(
missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
missing_docs,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications
)]
use crate::*;
#[cfg(test)]
use mockall::*;
use std::io;
#[cfg_attr(test, automock)]
trait UserPasswordInput {
fn get_password(&self, prompt: &str) -> std::io::Result<String>;
}
impl UserPasswordInput for std::io::Stdin {
#[inline]
fn get_password(&self, prompt: &str) -> std::io::Result<String> {
Ok(rpassword::prompt_password_stdout(prompt)?)
}
}
#[derive(Debug, Copy, Clone)]
/// Errors encountered by the wizard
pub enum WizardError {
/// Errors encountered by the user interaction
pub enum CliError {
/// Canceled
Canceled,
......@@ -34,55 +54,87 @@ pub enum WizardError {
BadInput,
}
impl From<std::io::Error> for WizardError {
impl From<std::io::Error> for CliError {
fn from(_e: std::io::Error) -> Self {
WizardError::BadInput
CliError::BadInput
}
}
#[inline]
/// Modify network keys command
pub fn modify_network_keys(
salt: String,
password: String,
pub fn modify_network_keys(key_pairs: DuniterKeyPairs) -> Result<DuniterKeyPairs, CliError> {
inner_modify_network_keys(std::io::stdin(), key_pairs)
}
/// Private function to modify network keys
fn inner_modify_network_keys<T: UserPasswordInput>(
stdin: T,
mut key_pairs: DuniterKeyPairs,
) -> DuniterKeyPairs {
let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();
key_pairs.network_keypair =
KeyPairEnum::Ed25519(generator.generate(ed25519::SaltedPassword::new(salt, password)));
key_pairs
) -> Result<DuniterKeyPairs, CliError> {
key_pairs.network_keypair = salt_password_prompt(stdin)?;
Ok(key_pairs)
}
#[inline]
/// Modify member keys command
pub fn modify_member_keys(
salt: String,
password: String,
pub fn modify_member_keys(key_pairs: DuniterKeyPairs) -> Result<DuniterKeyPairs, CliError> {
inner_modify_member_keys(std::io::stdin(), key_pairs)
}
/// Private function to modify network keys
fn inner_modify_member_keys<T: UserPasswordInput>(
stdin: T,
mut key_pairs: DuniterKeyPairs,
) -> DuniterKeyPairs {
let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();
key_pairs.member_keypair = Some(KeyPairEnum::Ed25519(
generator.generate(ed25519::SaltedPassword::new(salt, password)),
));
key_pairs
) -> Result<DuniterKeyPairs, CliError> {
key_pairs.member_keypair = Some(salt_password_prompt(stdin)?);
Ok(key_pairs)
}
/// Clear keys command
/// Ask user for confirmation and Clear keys command
pub fn clear_keys(network: bool, member: bool, mut key_pairs: DuniterKeyPairs) -> DuniterKeyPairs {
if network {
key_pairs.network_keypair = generate_random_keypair(KeysAlgo::Ed25519);
if let Ok("y") = question_prompt("Clear your network keypair?", &["y", "n"]) {
println!("Generating a new network keypair!");
clear_network_key(&mut key_pairs);
}
}
if member {
key_pairs.member_keypair = None;
if let Ok("y") = question_prompt("Clear your member keypair?", &["y", "n"]) {
println!("Deleting member keypair!");
clear_member_key(&mut key_pairs);
}
if !network && !member {
println!("No key was cleared. Please specify a key to clear.")
}
key_pairs
}
#[inline]
/// Private function to Clear keys
fn clear_network_key(key_pairs: &mut DuniterKeyPairs) {
key_pairs.network_keypair = super::generate_random_keypair(KeysAlgo::Ed25519);
}
#[inline]
/// Private function to Clear member key
fn clear_member_key(key_pairs: &mut DuniterKeyPairs) {
key_pairs.member_keypair = None;
}
/// Show keys command
pub fn show_keys(key_pairs: DuniterKeyPairs) {
show_network_keys(&key_pairs);
show_member_keys(&key_pairs);
}
#[inline]
/// Show network keys
pub fn show_network_keys(key_pairs: &DuniterKeyPairs) {
println!("Network key: {}", key_pairs.network_keypair);
match key_pairs.member_keypair {
}
#[inline]
/// Show member keys
pub fn show_member_keys(key_pairs: &DuniterKeyPairs) {
match &key_pairs.member_keypair {
None => println!("No member key configured"),
Some(key) => println!("Member key: {}", key),
}
......@@ -92,7 +144,7 @@ pub fn show_keys(key_pairs: DuniterKeyPairs) {
pub fn save_keypairs(
profile_path: PathBuf,
keypairs_file_path: &Option<PathBuf>,
key_pairs: DuniterKeyPairs,
key_pairs: &DuniterKeyPairs,
) -> Result<(), std::io::Error> {
let conf_keys_path: PathBuf = if let Some(keypairs_file_path) = keypairs_file_path {
keypairs_file_path.to_path_buf()
......@@ -101,32 +153,30 @@ pub fn save_keypairs(
conf_keys_path.push(crate::constants::KEYPAIRS_FILENAME);
conf_keys_path
};
write_keypairs_file(&conf_keys_path, &key_pairs)?;
Ok(())
super::write_keypairs_file(&conf_keys_path, &key_pairs)
}
fn question_prompt(question: &str, answers: Vec<String>) -> Result<String, WizardError> {
fn question_prompt<'a>(question: &str, answers: &[&'a str]) -> Result<&'a str, CliError> {
let mut buf = String::new();
println!("{} ({}):", question, answers.join("/"));
let res = io::stdin().read_line(&mut buf);
match res {
Ok(_) => {
let answer = answers.into_iter().find(|x| x == buf.trim());
match answer {
Some(value) => Ok(value),
None => Err(WizardError::Canceled),
}
}
Err(_) => Err(WizardError::Canceled),
if res.is_ok() {
answers
.iter()
.find(|x| **x == buf.trim())
.copied()
.ok_or(CliError::Canceled)
} else {
Err(CliError::Canceled)
}
}
fn salt_password_prompt() -> Result<KeyPairEnum, WizardError> {
let salt = rpassword::prompt_password_stdout("Salt: ")?;
fn salt_password_prompt<T: UserPasswordInput>(stdin: T) -> Result<KeyPairEnum, CliError> {
let salt = stdin.get_password("Salt: ")?;
if !salt.is_empty() {
let password = rpassword::prompt_password_stdout("Password: ")?;
let password = stdin.get_password("Password: ")?;
if !password.is_empty() {
let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters();
let key_pairs = KeyPairEnum::Ed25519(
......@@ -134,32 +184,26 @@ fn salt_password_prompt() -> Result<KeyPairEnum, WizardError> {
);
Ok(key_pairs)
} else {
Err(WizardError::BadInput)
Err(CliError::BadInput)
}
} else {
Err(WizardError::BadInput)
Err(CliError::BadInput)
}
}
/// The wizard key function
pub fn key_wizard(mut key_pairs: DuniterKeyPairs) -> Result<DuniterKeyPairs, WizardError> {
let mut answer = question_prompt(
"Modify your network keypair?",
vec!["y".to_string(), "n".to_string()],
)?;
pub fn key_wizard(mut key_pairs: DuniterKeyPairs) -> Result<DuniterKeyPairs, CliError> {
let mut answer = question_prompt("Modify your network keypair?", &["y", "n"])?;
if answer == "y" {
key_pairs.network_keypair = salt_password_prompt()?;
key_pairs.network_keypair = salt_password_prompt(std::io::stdin())?;
}
answer = question_prompt(
"Modify your member keypair?",
vec!["y".to_string(), "n".to_string(), "d".to_string()],
)?;
answer = question_prompt("Modify your member keypair?", &["y", "n", "d"])?;
if answer == "y" {
key_pairs.member_keypair = Some(salt_password_prompt()?);
key_pairs.member_keypair = Some(salt_password_prompt(std::io::stdin())?);
} else if answer == "d" {
println!("Deleting member keypair!");
key_pairs.member_keypair = None;
clear_member_key(&mut key_pairs);
}
Ok(key_pairs)
......@@ -169,29 +213,64 @@ pub fn key_wizard(mut key_pairs: DuniterKeyPairs) -> Result<DuniterKeyPairs, Wiz
mod tests {
use super::*;
static BASE58_SEED_INIT: &'static str = "4iXXx5GgRkZ85BVPwn8vFXvztdXAAa5yB573ErcAnngA";
static BASE58_PUB_INIT: &'static str = "otDgSpKvKAPPmE1MUYxc3UQ3RtEnKYz4iGD3BmwKPzM";
//static SALT_INIT: &'static str = "initsalt";
//static PASSWORD_INIT: &'static str = "initpassword";
use unwrap::unwrap;
static BASE58_SEED_TEST: &'static str = "ELjDWGPyCGMuhr7R7H2aip6UJA9qLRepmK77pcD41UqQ";
static BASE58_PUB_TEST: &'static str = "6sewkaNWyEMqkEa2PVRWrDb3hxWtjPdUSB1zXVCqhdWV";
static SALT_TEST: &'static str = "testsalt";
static PASSWORD_TEST: &'static str = "testpassword";
static BASE58_SEED_INIT: &str = "4iXXx5GgRkZ85BVPwn8vFXvztdXAAa5yB573ErcAnngA";
static BASE58_PUB_INIT: &str = "otDgSpKvKAPPmE1MUYxc3UQ3RtEnKYz4iGD3BmwKPzM";
#[test]
fn test_modify_member_keys() {
let key_pairs = DuniterKeyPairs {
static BASE58_SEED_TEST: &str = "ELjDWGPyCGMuhr7R7H2aip6UJA9qLRepmK77pcD41UqQ";
static BASE58_PUB_TEST: &str = "6sewkaNWyEMqkEa2PVRWrDb3hxWtjPdUSB1zXVCqhdWV";
static SALT_TEST: &str = "testsalt";
static PASSWORD_TEST: &str = "testpassword";
fn setup_user_password_input() -> MockUserPasswordInput {
let mut stdin_mock = MockUserPasswordInput::new();
stdin_mock
.expect_get_password()
.returning(|prompt| {
if prompt.starts_with("Salt:") {
Ok(SALT_TEST.to_owned())
} else if prompt.starts_with("Password:") {
Ok(PASSWORD_TEST.to_owned())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("should not be called with {}", prompt),
))
}
})
.times(2);
stdin_mock
}
fn setup_keys(both_keys: bool) -> DuniterKeyPairs {
let member_keypair = if both_keys {
Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}))
} else {
None
};
DuniterKeyPairs {
network_keypair: KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}),
member_keypair: None,
};
member_keypair,
}
}
#[test]
fn test_modify_member_keys() {
let key_pairs = setup_keys(false);
let stdin_mock = setup_user_password_input();
let result_key_pairs =
modify_member_keys(SALT_TEST.to_owned(), PASSWORD_TEST.to_owned(), key_pairs);
inner_modify_member_keys(stdin_mock, key_pairs).expect("Fail to read new member keys");
// We expect network key not to change
assert_eq!(
result_key_pairs.network_keypair.public_key(),
......@@ -207,21 +286,20 @@ mod tests {
// We expect member key to update as intended
assert_eq!(
result_key_pairs
.member_keypair
.clone()
.unwrap()
.public_key(),
PubKey::Ed25519(
ed25519::PublicKey::from_base58(BASE58_PUB_TEST)
.expect("Wrong data in BASE58_PUB_TEST")
unwrap!(
result_key_pairs.member_keypair.clone(),
"conf: member_keypair must have a value"
)
.public_key(),
PubKey::Ed25519(unwrap!(
ed25519::PublicKey::from_base58(BASE58_PUB_TEST),
"Wrong data in BASE58_PUB_TEST"
))
);
assert_eq!(
result_key_pairs
.member_keypair
.clone()
.unwrap()
.expect("conf: member_keypair must have a value")
.seed()
.clone(),
Seed32::from_base58(BASE58_SEED_TEST).expect("Wrong data in BASE58_SEED_TEST"),
......@@ -230,17 +308,10 @@ mod tests {
#[test]
fn test_modify_network_keys() {
let key_pairs = DuniterKeyPairs {
network_keypair: KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}),
member_keypair: None,
};
let result_key_pairs =
modify_network_keys(SALT_TEST.to_owned(), PASSWORD_TEST.to_owned(), key_pairs);
let key_pairs = setup_keys(false);
let stdin_mock = setup_user_password_input();
let result_key_pairs = inner_modify_network_keys(stdin_mock, key_pairs)
.expect("Fail to read new network keys");
// We expect network key to update
assert_eq!(
result_key_pairs.network_keypair.public_key(),
......@@ -259,40 +330,30 @@ mod tests {
#[test]
fn test_clear_network_keys() {
let key_pairs = DuniterKeyPairs {
network_keypair: KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}),
member_keypair: Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
})),
};
let result_key_pairs = clear_keys(true, false, key_pairs);
let mut key_pairs = setup_keys(true);
clear_network_key(&mut key_pairs);
// We expect network key to be reset to a new random key
assert_ne!(
result_key_pairs.network_keypair.public_key(),
key_pairs.network_keypair.public_key(),
PubKey::Ed25519(
ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("Wrong data in BASE58_PUB_TEST")
)
);
assert_ne!(
result_key_pairs.network_keypair.seed().clone(),
Seed32::from_base58(BASE58_SEED_INIT).expect("Wrong data in BASE58_SEED_TEST")
key_pairs.network_keypair.seed().clone(),
unwrap!(
Seed32::from_base58(BASE58_SEED_INIT),
"Wrong data in BASE58_SEED_TEST"
)
);
// We expect member key not to change
assert_eq!(
result_key_pairs
.member_keypair
.clone()
.unwrap()
unwrap!(
key_pairs.member_keypair.clone(),
"conf: keypair must have a value"
)
.public_key(),
PubKey::Ed25519(
ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
......@@ -300,10 +361,9 @@ mod tests {
)
);
assert_eq!(
result_key_pairs
key_pairs
.member_keypair
.clone()
.unwrap()
.expect("conf: keypair must have a value")
.seed()
.clone(),
Seed32::from_base58(BASE58_SEED_INIT).expect("Wrong data in BASE58_SEED_TEST")
......@@ -312,36 +372,22 @@ mod tests {
#[test]
fn test_clear_member_keys() {
let key_pairs = DuniterKeyPairs {
network_keypair: KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}),
member_keypair: Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
})),
};
let result_key_pairs = clear_keys(false, true, key_pairs);
let mut key_pairs = setup_keys(true);
clear_member_key(&mut key_pairs);
// We expect network key not to change
assert_eq!(
result_key_pairs.network_keypair.public_key(),
key_pairs.network_keypair.public_key(),
PubKey::Ed25519(
ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("Wrong data in BASE58_PUB_TEST")
)
);
assert_eq!(
result_key_pairs.network_keypair.seed().clone(),
key_pairs.network_keypair.seed().clone(),
Seed32::from_base58(BASE58_SEED_INIT).expect("Wrong data in BASE58_SEED_TEST")
);
// We expect member key to change
assert_eq!(result_key_pairs.member_keypair, None);
assert_eq!(result_key_pairs.member_keypair, None);
assert_eq!(key_pairs.member_keypair, None);
}
}
This diff is collapsed.