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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use anyhow::{ensure, Result};
use diem_config::{
    config::{Identity, NodeConfig, PeerRole, RoleType, WaypointConfig},
    generator::build_seed_for_network,
    network_id::NetworkId,
};
use diem_crypto::{x25519, Uniform};
use diem_types::{transaction::Transaction, waypoint::Waypoint};
use rand::rngs::OsRng;
use std::{
    fs::File,
    io::Write,
    path::{Path, PathBuf},
};

pub struct FullnodeConfig {
    pub name: String,
    pub config: NodeConfig,
    pub directory: PathBuf,
}

impl FullnodeConfig {
    pub fn public_fullnode(
        name: String,
        config_directory: &Path,
        config: NodeConfig,
        waypoint: &Waypoint,
        genesis: &Transaction,
    ) -> Result<Self> {
        let mut fullnode_config = Self::new(name, config_directory, config)?;

        fullnode_config.insert_waypoint(waypoint);
        fullnode_config.insert_genesis(genesis)?;
        fullnode_config.set_identity();
        fullnode_config.config.randomize_ports();
        fullnode_config.save_config()?;

        Ok(fullnode_config)
    }

    pub fn validator_fullnode(
        name: String,
        config_directory: &Path,
        fullnode_config: NodeConfig,
        validator_config: &mut NodeConfig,
        waypoint: &Waypoint,
        genesis: &Transaction,
    ) -> Result<Self> {
        let mut fullnode_config = Self::new(name, config_directory, fullnode_config)?;

        fullnode_config.insert_waypoint(waypoint);
        fullnode_config.insert_genesis(genesis)?;
        fullnode_config.config.randomize_ports();

        fullnode_config.attach_to_validator(validator_config)?;
        fullnode_config.save_config()?;

        Ok(fullnode_config)
    }

    fn new(name: String, config_directory: &Path, mut config: NodeConfig) -> Result<Self> {
        ensure!(
            matches!(config.base.role, RoleType::FullNode),
            "config must be a FullNode config"
        );

        let directory = config_directory.join(&name);
        std::fs::create_dir_all(&directory)?;

        config.set_data_dir(directory.clone());

        Ok(Self {
            name,
            config,
            directory,
        })
    }

    fn insert_waypoint(&mut self, waypoint: &Waypoint) {
        self.config.base.waypoint = WaypointConfig::FromConfig(*waypoint);
    }

    fn insert_genesis(&mut self, genesis: &Transaction) -> Result<()> {
        // Save genesis file in this validator's config directory
        let genesis_file_location = self.directory.join("genesis.blob");
        File::create(&genesis_file_location)?.write_all(&bcs::to_bytes(&genesis)?)?;

        self.config.execution.genesis = Some(genesis.clone());
        self.config.execution.genesis_file_location = genesis_file_location;

        Ok(())
    }

    fn set_identity(&mut self) {
        let mut network_config = self
            .config
            .full_node_networks
            .iter_mut()
            .find(|config| config.network_id == NetworkId::Public)
            .unwrap();

        if let Identity::None = network_config.identity {
            let key = x25519::PrivateKey::generate(&mut OsRng);
            let peer_id = diem_types::account_address::from_identity_public_key(key.public_key());
            network_config.identity = Identity::from_config(key, peer_id);
        }
    }

    fn attach_to_validator(&mut self, validator_config: &mut NodeConfig) -> Result<()> {
        ensure!(
            matches!(validator_config.base.role, RoleType::Validator),
            "Validator config must be a Validator config"
        );

        // Grab the public network config from the validator and insert it into the VFN's config
        let public_network = {
            let (i, _) = validator_config
                .full_node_networks
                .iter()
                .enumerate()
                .find(|(_i, config)| config.network_id == NetworkId::Public)
                .expect("Validator should have a public network");
            validator_config.full_node_networks.remove(i)
        };

        let fullnode_public_network = self
            .config
            .full_node_networks
            .iter_mut()
            .find(|config| config.network_id == NetworkId::Public)
            .expect("VFN should have a public network");
        fullnode_public_network.identity = public_network.identity;
        fullnode_public_network.listen_address = public_network.listen_address;

        // Grab the validator's vfn network information and configure it as a seed for the VFN's
        // vfn network
        let validators_vfn_network = validator_config
            .full_node_networks
            .iter()
            .find(|config| config.network_id.is_vfn_network())
            .expect("Validator should have vfn network");

        let fullnode_vfn_network = self
            .config
            .full_node_networks
            .iter_mut()
            .find(|config| config.network_id.is_vfn_network())
            .expect("VFN should have a vfn network");
        fullnode_vfn_network.seeds =
            build_seed_for_network(validators_vfn_network, PeerRole::Validator);

        if let Identity::None = fullnode_vfn_network.identity {
            let key = x25519::PrivateKey::generate(&mut OsRng);
            let peer_id = diem_types::account_address::from_identity_public_key(key.public_key());
            fullnode_vfn_network.identity = Identity::from_config(key, peer_id);
        }

        Ok(())
    }

    pub fn config_path(&self) -> PathBuf {
        self.directory.join("node.yaml")
    }

    fn save_config(&mut self) -> Result<()> {
        self.config.save(self.config_path()).map_err(Into::into)
    }
}