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
125
126
127
/* This file is part of DarkFi (https://dark.fi)
 *
 * Copyright (C) 2020-2024 Dyne.org foundation
 *
 * 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/>.
 */

use std::{collections::HashMap, sync::Arc};

use log::{debug, error, info};
use smol::{fs::read_to_string, Executor};
use structopt_toml::StructOptToml;

use darkfi::{
    net::{session::SESSION_DEFAULT, P2p, P2pPtr, Settings},
    rpc::jsonrpc::JsonSubscriber,
    util::path::get_config_path,
    validator::ValidatorPtr,
    Error, Result,
};

use crate::{
    proto::{ProtocolProposal, ProtocolSync, ProtocolTx},
    BlockchainNetwork, CONFIG_FILE,
};

/// Auxiliary function to generate the P2P network and register all its protocols.
pub async fn spawn_p2p(
    settings: &Settings,
    validator: &ValidatorPtr,
    subscribers: &HashMap<&'static str, JsonSubscriber>,
    executor: Arc<Executor<'static>>,
) -> Result<P2pPtr> {
    info!(target: "darkfid", "Registering sync network P2P protocols...");
    let p2p = P2p::new(settings.clone(), executor.clone()).await?;
    let registry = p2p.protocol_registry();

    let _validator = validator.clone();
    registry
        .register(SESSION_DEFAULT, move |channel, _p2p| {
            let validator = _validator.clone();
            async move { ProtocolSync::init(channel, validator).await.unwrap() }
        })
        .await;

    let _validator = validator.clone();
    let _subscriber = subscribers.get("proposals").unwrap().clone();
    registry
        .register(SESSION_DEFAULT, move |channel, p2p| {
            let validator = _validator.clone();
            let subscriber = _subscriber.clone();
            async move {
                ProtocolProposal::init(channel, validator, p2p, subscriber)
                    .await
                    .unwrap()
            }
        })
        .await;

    let _validator = validator.clone();
    let _subscriber = subscribers.get("txs").unwrap().clone();
    registry
        .register(SESSION_DEFAULT, move |channel, p2p| {
            let validator = _validator.clone();
            let subscriber = _subscriber.clone();
            async move { ProtocolTx::init(channel, validator, p2p, subscriber).await.unwrap() }
        })
        .await;

    Ok(p2p)
}

/// Auxiliary function to parse darkfid configuration file and extract requested
/// blockchain network config.
pub async fn parse_blockchain_config(
    config: Option<String>,
    network: &str,
) -> Result<BlockchainNetwork> {
    // Grab config path
    let config_path = get_config_path(config, CONFIG_FILE)?;
    debug!(target: "darkfid", "Parsing configuration file: {:?}", config_path);

    // Parse TOML file contents
    let contents = read_to_string(&config_path).await?;
    let contents: toml::Value = match toml::from_str(&contents) {
        Ok(v) => v,
        Err(e) => {
            error!(target: "darkfid", "Failed parsing TOML config: {}", e);
            return Err(Error::ParseFailed("Failed parsing TOML config"))
        }
    };

    // Grab requested network config
    let Some(table) = contents.as_table() else { return Err(Error::ParseFailed("TOML not a map")) };
    let Some(network_configs) = table.get("network_config") else {
        return Err(Error::ParseFailed("TOML does not contain network configurations"))
    };
    let Some(network_configs) = network_configs.as_table() else {
        return Err(Error::ParseFailed("`network_config` not a map"))
    };
    let Some(network_config) = network_configs.get(network) else {
        return Err(Error::ParseFailed("TOML does not contain requested network configuration"))
    };
    let network_config = toml::to_string(&network_config).unwrap();
    let network_config =
        match BlockchainNetwork::from_iter_with_toml::<Vec<String>>(&network_config, vec![]) {
            Ok(v) => v,
            Err(e) => {
                error!(target: "darkfid", "Failed parsing requested network configuration: {}", e);
                return Err(Error::ParseFailed("Failed parsing requested network configuration"))
            }
        };
    debug!(target: "darkfid", "Parsed network configuration: {:?}", network_config);

    Ok(network_config)
}