conflux/
main.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5#[cfg(all(not(target_env = "msvc"), feature = "jemalloc-global"))]
6#[global_allocator]
7static ALLOC: cfx_mallocator_utils::allocator::Allocator =
8    cfx_mallocator_utils::allocator::new_allocator();
9// jemalloc profiling config
10#[allow(non_upper_case_globals)]
11#[export_name = "malloc_conf"]
12#[cfg(all(not(target_env = "msvc"), feature = "jemalloc-prof"))]
13pub static malloc_conf: &[u8] =
14    b"prof:true,prof_active:true,lg_prof_sample:19\0"; // 512kb
15
16#[cfg(test)]
17mod test;
18
19mod cli;
20mod command;
21
22use crate::command::rpc::RpcCommand;
23use cfxcore::NodeType;
24use clap::{crate_version, ArgMatches, CommandFactory};
25use cli::Cli;
26use client::{
27    archive::ArchiveClient,
28    common::{shutdown_handler, ClientTrait},
29    configuration::Configuration,
30    full::FullClient,
31    light::LightClient,
32};
33use command::{
34    account::{AccountCmd, ImportAccounts, ListAccounts, NewAccount},
35    dump::DumpCommand,
36};
37use log::{info, LevelFilter};
38use log4rs::{
39    append::{console::ConsoleAppender, file::FileAppender},
40    config::{Appender, Config as LogConfig, Logger, Root},
41    encode::pattern::PatternEncoder,
42};
43use network::throttling::THROTTLING_SERVICE;
44use parking_lot::{Condvar, Mutex};
45use std::sync::{Arc, OnceLock};
46
47static VERSION: OnceLock<String> = OnceLock::new();
48
49fn get_version() -> &'static str {
50    VERSION.get_or_init(|| parity_version::version(crate_version!()))
51}
52
53fn main() -> Result<(), String> {
54    #[cfg(feature = "deadlock-detection")]
55    {
56        // only for #[cfg]
57        use parking_lot::deadlock;
58        use std::{thread, time::Duration};
59
60        // Create a background thread which checks for deadlocks every 10s
61        thread::spawn(move || loop {
62            thread::sleep(Duration::from_secs(10));
63            let deadlocks = deadlock::check_deadlock();
64            if deadlocks.is_empty() {
65                continue;
66            }
67
68            eprintln!("{} deadlocks detected", deadlocks.len());
69            for (i, threads) in deadlocks.iter().enumerate() {
70                eprintln!("Deadlock #{}", i);
71                for t in threads {
72                    eprintln!("Thread Id {:#?}", t.thread_id());
73                    eprintln!("{:#?}", t.backtrace());
74                }
75            }
76        });
77    } // only for #[cfg]
78
79    let matches = Cli::command().version(get_version()).get_matches();
80
81    if let Some(output) = handle_sub_command(&matches)? {
82        println!("{}", output);
83        return Ok(());
84    }
85
86    let conf = Configuration::parse(&matches)?;
87
88    setup_logger(&conf)?;
89
90    THROTTLING_SERVICE.write().initialize(
91        conf.raw_conf.egress_queue_capacity,
92        conf.raw_conf.egress_min_throttle,
93        conf.raw_conf.egress_max_throttle,
94    );
95
96    let exit = Arc::new((Mutex::new(false), Condvar::new()));
97
98    info!(
99        "
100:'######:::'#######::'##::: ##:'########:'##:::::::'##::::'##:'##::::'##:
101'##... ##:'##.... ##: ###:: ##: ##.....:: ##::::::: ##:::: ##:. ##::'##::
102 ##:::..:: ##:::: ##: ####: ##: ##::::::: ##::::::: ##:::: ##::. ##'##:::
103 ##::::::: ##:::: ##: ## ## ##: ######::: ##::::::: ##:::: ##:::. ###::::
104 ##::::::: ##:::: ##: ##. ####: ##...:::: ##::::::: ##:::: ##::: ## ##:::
105 ##::: ##: ##:::: ##: ##:. ###: ##::::::: ##::::::: ##:::: ##:: ##:. ##::
106. ######::. #######:: ##::. ##: ##::::::: ########:. #######:: ##:::. ##:
107:......::::.......:::..::::..::..::::::::........:::.......:::..:::::..::
108Current Version: {}
109",
110        get_version()
111    );
112
113    let client_handle: Box<dyn ClientTrait>;
114    client_handle = match conf.node_type() {
115        NodeType::Archive => {
116            info!("Starting archive client...");
117            ArchiveClient::start(conf, exit.clone())
118                .map_err(|e| format!("failed to start archive client: {}", e))?
119        }
120        NodeType::Full => {
121            info!("Starting full client...");
122            FullClient::start(conf, exit.clone())
123                .map_err(|e| format!("failed to start full client: {}", e))?
124        }
125        NodeType::Light => {
126            info!("Starting light client...");
127            LightClient::start(conf, exit.clone())
128                .map_err(|e| format!("failed to start light client: {}", e))?
129        }
130        NodeType::Unknown => return Err("Unknown node type".into()),
131    };
132    info!("Conflux client started");
133    shutdown_handler::run(client_handle, exit);
134
135    Ok(())
136}
137
138fn handle_sub_command(matches: &ArgMatches) -> Result<Option<String>, String> {
139    if matches.subcommand_name().is_none() {
140        return Ok(None);
141    }
142
143    // account sub-commands
144    if let Some(("account", account_matches)) = matches.subcommand() {
145        let account_cmd = match account_matches.subcommand() {
146            Some(("new", new_acc_matches)) => {
147                AccountCmd::New(NewAccount::new(new_acc_matches))
148            }
149            Some(("list", list_acc_matches)) => {
150                AccountCmd::List(ListAccounts::new(list_acc_matches))
151            }
152            Some(("import", import_acc_matches)) => {
153                AccountCmd::Import(ImportAccounts::new(import_acc_matches))
154            }
155            _ => unreachable!(),
156        };
157        let execute_output = command::account::execute(account_cmd)?;
158        return Ok(Some(execute_output));
159    }
160
161    // dump sub-commands
162    if let Some(("dump", dump_matches)) = matches.subcommand() {
163        let dump_cmd = DumpCommand::parse(dump_matches).map_err(|e| {
164            format!("Failed to parse dump command arguments: {}", e)
165        })?;
166        let mut conf = Configuration::parse(&matches)?;
167        let execute_output = dump_cmd.execute(&mut conf)?;
168        return Ok(Some(execute_output));
169    }
170
171    // general RPC commands
172    let mut subcmd_matches = matches;
173    while let Some(m) = subcmd_matches.subcommand() {
174        subcmd_matches = m.1;
175    }
176
177    if let Some(cmd) = RpcCommand::parse(subcmd_matches)? {
178        let rt = tokio::runtime::Runtime::new().unwrap();
179        let result = rt.block_on(cmd.execute())?;
180        return Ok(Some(result));
181    }
182
183    Ok(None)
184}
185
186// If log_conf is provided, use it for log configuration and ignore
187// log_file and log_level. Otherwise, set stdout to INFO and set
188// all our crate log to log_level.
189fn setup_logger(conf: &Configuration) -> Result<(), String> {
190    match conf.raw_conf.log_conf {
191        Some(ref log_conf) => {
192            log4rs::init_file(log_conf, Default::default()).map_err(|e| {
193                format!(
194                    "failed to initialize log with log config file '{}': {:?}; maybe you want 'run/log.yaml'?",
195                    log_conf, e
196                )
197            })?;
198        }
199        None => {
200            let mut conf_builder =
201                LogConfig::builder().appender(Appender::builder().build(
202                    "stdout",
203                    Box::new(ConsoleAppender::builder().build()),
204                ));
205            let mut root_builder = Root::builder().appender("stdout");
206            if let Some(ref log_file) = conf.raw_conf.log_file {
207                conf_builder =
208                    conf_builder.appender(Appender::builder().build(
209                        "logfile",
210                        Box::new(
211                            FileAppender::builder().encoder(
212                                Box::new(
213                                    PatternEncoder::new(
214                                        "{d} {h({l}):5.5} {T:<20.20} {t:12.12} - {m}{n}")))
215                                .build(log_file)
216                                .map_err(
217                                    |e| format!("failed to build log pattern: {:?}", e))?,
218                        ),
219                    ));
220                root_builder = root_builder.appender("logfile");
221            };
222            // Should add new crate names here
223            for crate_name in [
224                "blockgen",
225                "cfxcore",
226                "cfx_statedb",
227                "cfx_storage",
228                "conflux",
229                "db",
230                "keymgr",
231                "network",
232                "txgen",
233                "client",
234                "primitives",
235                "io",
236            ]
237            .iter()
238            {
239                conf_builder = conf_builder.logger(
240                    Logger::builder()
241                        .build(*crate_name, conf.raw_conf.log_level),
242                );
243            }
244            let log_config = conf_builder
245                .build(root_builder.build(LevelFilter::Info))
246                .map_err(|e| format!("failed to build log config: {:?}", e))?;
247            log4rs::init_config(log_config).map_err(|e| {
248                format!("failed to initialize log with config: {:?}", e)
249            })?;
250        }
251    };
252
253    Ok(())
254}