1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::load::MultiControllerError;
use crate::utils::qr::PinReadModeSettings;
use crate::MultiController;
use jormungandr_automation::jormungandr::RestSettings;
use jortestkit::load::Configuration;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use thiserror::Error;

/// Node Load configuration struct. It defines all aspects of load run.
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
    /// Inner configuration which controls common settings like grace period or step delay
    pub config: Configuration,
    /// Use REST API V1, which allows send batch votes or checking individual vote status
    pub use_v1: bool,
    /// Batch size for single load step
    pub batch_size: usize,
    /// Success criteria for run. It helps to put simple assertion on outcome of load scenario.
    /// Usually it should be number from 0 to 100 describing for instance expected success rate for
    /// load messages
    pub criterion: Option<u8>,
    /// REST API address to be excercised by load tool
    pub address: String,
    /// Print verbose information during load
    pub debug: bool,
    /// Use https protocol
    pub use_https: bool,
    /// This parameter work in conjunction with `global_pin` and `qr_codes_folder`.
    /// If source of account secrets is a specific folder with qr codes, then each qr code need to be
    /// individually decrypted using pin code. This parameter defines strategy of acquiring pin for qr code.
    /// Using this parameter and with correct format of file (which should be for example alice_1234.png)
    /// load tool can successfully decrypt all qr code. Alternative for this setting is global pin.
    pub read_pin_from_filename: bool,
    /// Sometimes we may want to run load tool again on the same environment. The problem is that,
    /// Jormungandr blockchain uses account based model so we need to refresh accounts spending counters
    /// This particular settings controls when load tool will refresh account. If set to true load tool
    /// will refresh all accounts before starting the load. It is important because it may be long operation
    /// depending on amount of source accounts
    pub reuse_accounts_early: bool,
    /// This particular settings controls when load tool will refresh account. If set to true load tool
    /// will refresh all accounts just before running votes from particular account, removing necessity to
    /// wait for sync completion like when using `reuse_accounts_early` parameter.
    pub reuse_accounts_lazy: bool,
    /// This parameter work in conjunction with `qr_codes_folder`. If for some reasons file name
    /// does not contain pin, then we can use this setting to set global pin for every qr code
    pub global_pin: String,
    /// Source folder for seed qr codes which can be used during load scenario. Source can be either
    /// qr code folder (defined by `qr_codes_folder`)  or secret key as bech32 file (defined by `secrets_folder`)
    pub qr_codes_folder: Option<PathBuf>,
    /// Source folder for seed bech32 accounts file which will be use during load scenario
    pub secrets_folder: Option<PathBuf>,
    /// Voting groups which account belongs to. Existence of this paramters clearly forbids mixed accounts
    /// (direct voters or reps)
    pub voting_group: String,
}

impl Config {
    /// Gets rest settings
    #[must_use]
    pub fn rest_settings(&self) -> RestSettings {
        RestSettings {
            enable_debug: self.debug,
            use_https: self.use_https,
            ..Default::default()
        }
    }

    /// Construct multi controller (for multi accounts handling)
    ///
    /// # Errors
    ///
    /// On error reading qr or secret files
    ///
    /// # Panics
    ///
    /// On internal os error when extracting path from directory for specific qr code
    pub fn build_multi_controller(&self) -> Result<MultiController, Error> {
        if let Some(qr_codes) = &self.qr_codes_folder {
            let qr_codes: Vec<PathBuf> = std::fs::read_dir(qr_codes)
                .map_err(|_| Error::CannotReadQrs(qr_codes.clone()))?
                .map(|x| x.unwrap().path())
                .collect();

            MultiController::recover_from_qrs(
                &self.address,
                &qr_codes,
                &PinReadModeSettings {
                    from_filename: self.read_pin_from_filename,
                    global_pin: self.global_pin.clone(),
                },
                self.rest_settings(),
            )
            .map_err(Into::into)
        } else if let Some(secrets_folder) = &self.secrets_folder {
            let secrets: Vec<PathBuf> = std::fs::read_dir(secrets_folder)
                .unwrap()
                .map(|x| x.unwrap().path())
                .collect();
            MultiController::recover_from_sks(&self.address, &secrets, self.rest_settings())
                .map_err(Into::into)
        } else {
            Err(Error::CannotFindPrivateKeyRoot)
        }
    }
}

/// Builder related error
#[derive(Error, Debug)]
pub enum Error {
    /// No private key root
    #[error("source of private keys not selected")]
    CannotFindPrivateKeyRoot,
    /// No mnemonic file
    #[error("cannot read mnemonics file")]
    CannotReadMnemonicsFile,
    /// Cannot read qr
    #[error("cannot read folder {0:?}")]
    CannotReadQrs(PathBuf),
    /// MultiController setup
    #[error("multicontoller error")]
    MultiController(#[from] MultiControllerError),
}