141 lines
5.0 KiB
Rust
141 lines
5.0 KiB
Rust
#![feature(never_type)]
|
|
#![feature(btree_cursors)]
|
|
|
|
use {
|
|
anyhow::Result,
|
|
clap::{
|
|
CommandFactory,
|
|
Parser,
|
|
},
|
|
futures::future::join_all,
|
|
lazy_static::lazy_static,
|
|
state::State,
|
|
std::io::IsTerminal,
|
|
tokio::{
|
|
spawn,
|
|
sync::watch,
|
|
},
|
|
};
|
|
|
|
mod aggregate;
|
|
mod api;
|
|
mod config;
|
|
mod metrics_server;
|
|
mod network;
|
|
mod price_feeds_metadata;
|
|
mod serde;
|
|
mod state;
|
|
|
|
lazy_static! {
|
|
/// A static exit flag to indicate to running threads that we're shutting down. This is used to
|
|
/// gracefully shut down the application.
|
|
///
|
|
/// We make this global based on the fact the:
|
|
/// - The `Sender` side does not rely on any async runtime.
|
|
/// - Exit logic doesn't really require carefully threading this value through the app.
|
|
/// - The `Receiver` side of a watch channel performs the detection based on if the change
|
|
/// happened after the subscribe, so it means all listeners should always be notified
|
|
/// correctly.
|
|
pub static ref EXIT: watch::Sender<bool> = watch::channel(false).0;
|
|
}
|
|
|
|
/// Initialize the Application. This can be invoked either by real main, or by the Geyser plugin.
|
|
#[tracing::instrument]
|
|
async fn init() -> Result<()> {
|
|
tracing::info!("Initializing Hermes...");
|
|
|
|
// Parse the command line arguments with StructOpt, will exit automatically on `--help` or
|
|
// with invalid arguments.
|
|
match config::Options::parse() {
|
|
config::Options::Run(opts) => {
|
|
tracing::info!("Starting hermes service...");
|
|
|
|
// The update broadcast channel is used to send store update notifications to the public API.
|
|
let (update_tx, _) = tokio::sync::broadcast::channel(1000);
|
|
|
|
// Initialize a cache store with a 1000 element circular buffer.
|
|
let store = State::new(update_tx.clone(), 1000, opts.benchmarks.endpoint.clone());
|
|
|
|
// Listen for Ctrl+C so we can set the exit flag and wait for a graceful shutdown.
|
|
spawn(async move {
|
|
tracing::info!("Registered shutdown signal handler...");
|
|
tokio::signal::ctrl_c().await.unwrap();
|
|
tracing::info!("Shut down signal received, waiting for tasks...");
|
|
let _ = EXIT.send(true);
|
|
});
|
|
|
|
// Spawn all worker tasks, and wait for all to complete (which will happen if a shutdown
|
|
// signal has been observed).
|
|
let tasks = join_all(vec![
|
|
spawn(network::wormhole::spawn(opts.clone(), store.clone())),
|
|
spawn(network::pythnet::spawn(opts.clone(), store.clone())),
|
|
spawn(metrics_server::run(opts.clone(), store.clone())),
|
|
spawn(api::spawn(opts.clone(), store.clone(), update_tx)),
|
|
])
|
|
.await;
|
|
|
|
for task in tasks {
|
|
task??;
|
|
}
|
|
}
|
|
|
|
config::Options::ShowEnv(opts) => {
|
|
// For each subcommand, scan for arguments that allow overriding with an ENV variable
|
|
// and print that variable.
|
|
for subcommand in config::Options::command().get_subcommands() {
|
|
for arg in subcommand.get_arguments() {
|
|
if let Some(env) = arg.get_env().and_then(|env| env.to_str()) {
|
|
// Find the defaults for this argument, if present.
|
|
let defaults = arg
|
|
.get_default_values()
|
|
.iter()
|
|
.map(|v| v.to_str().unwrap())
|
|
.collect::<Vec<_>>()
|
|
.join(",");
|
|
|
|
println!(
|
|
"{}={}",
|
|
env,
|
|
match opts.defaults {
|
|
true => defaults,
|
|
false => std::env::var(env).unwrap_or(defaults),
|
|
}
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::main]
|
|
#[tracing::instrument]
|
|
async fn main() -> Result<()> {
|
|
// Initialize a Tracing Subscriber
|
|
let fmt_builder = tracing_subscriber::fmt()
|
|
.with_file(false)
|
|
.with_line_number(true)
|
|
.with_thread_ids(true)
|
|
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
|
|
.with_ansi(std::io::stderr().is_terminal());
|
|
|
|
// Use the compact formatter if we're in a terminal, otherwise use the JSON formatter.
|
|
if std::io::stderr().is_terminal() {
|
|
tracing::subscriber::set_global_default(fmt_builder.compact().finish())?;
|
|
} else {
|
|
tracing::subscriber::set_global_default(fmt_builder.json().finish())?;
|
|
}
|
|
|
|
// Launch the application. If it fails, print the full backtrace and exit. RUST_BACKTRACE
|
|
// should be set to 1 for this otherwise it will only print the top-level error.
|
|
if let Err(result) = init().await {
|
|
eprintln!("{}", result.backtrace());
|
|
result.chain().for_each(|cause| eprintln!("{cause}"));
|
|
std::process::exit(1);
|
|
}
|
|
|
|
Ok(())
|
|
}
|