2018-04-21 09:59:11 -07:00
|
|
|
extern crate chrono;
|
|
|
|
extern crate clap;
|
2018-09-25 06:56:44 -07:00
|
|
|
extern crate ctrlc;
|
2018-04-21 09:59:11 -07:00
|
|
|
extern crate dotenv;
|
|
|
|
extern crate ethabi;
|
|
|
|
extern crate ethereum_types;
|
2018-09-25 06:56:44 -07:00
|
|
|
extern crate failure;
|
2018-04-21 09:59:11 -07:00
|
|
|
extern crate hex;
|
|
|
|
extern crate jsonrpc_core;
|
2018-12-11 10:58:56 -08:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
2018-04-21 09:59:11 -07:00
|
|
|
extern crate lettre;
|
|
|
|
extern crate lettre_email;
|
2018-04-24 09:57:06 -07:00
|
|
|
extern crate native_tls;
|
2018-04-21 09:59:11 -07:00
|
|
|
extern crate reqwest;
|
|
|
|
extern crate serde_json;
|
2018-09-25 06:56:44 -07:00
|
|
|
#[macro_use]
|
|
|
|
extern crate slog;
|
2018-04-21 09:59:11 -07:00
|
|
|
extern crate slog_term;
|
|
|
|
extern crate web3;
|
|
|
|
|
2018-09-25 06:56:44 -07:00
|
|
|
mod blockchain;
|
2018-04-21 09:59:11 -07:00
|
|
|
mod cli;
|
2018-09-25 06:56:44 -07:00
|
|
|
mod client;
|
2018-04-21 09:59:11 -07:00
|
|
|
mod config;
|
2018-09-25 06:56:44 -07:00
|
|
|
mod error;
|
|
|
|
mod logger;
|
2018-04-21 09:59:11 -07:00
|
|
|
mod notify;
|
2018-09-25 06:56:44 -07:00
|
|
|
mod response;
|
2018-04-21 09:59:11 -07:00
|
|
|
|
2018-10-10 07:46:20 -07:00
|
|
|
use std::sync::{Arc, Mutex};
|
2018-09-25 06:56:44 -07:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
2018-04-21 09:59:11 -07:00
|
|
|
|
2018-09-25 06:56:44 -07:00
|
|
|
use blockchain::BlockchainIter;
|
2018-12-11 11:38:58 -08:00
|
|
|
use cli::parse_cli;
|
2018-09-25 06:56:44 -07:00
|
|
|
use client::RpcClient;
|
|
|
|
use config::{Config, ContractVersion};
|
|
|
|
use error::{Error, Result};
|
2018-10-10 07:46:20 -07:00
|
|
|
use logger::Logger;
|
2018-09-25 06:56:44 -07:00
|
|
|
use notify::{Notification, Notifier};
|
2018-04-21 09:59:11 -07:00
|
|
|
|
2018-12-11 10:58:56 -08:00
|
|
|
lazy_static! {
|
2018-12-11 15:44:51 -08:00
|
|
|
// Tracks whether or not the environment variables have been loaded from the .env file.
|
2018-12-11 10:58:56 -08:00
|
|
|
static ref LOADED_ENV_FILE: AtomicBool = AtomicBool::new(false);
|
|
|
|
}
|
|
|
|
|
2018-12-11 15:44:51 -08:00
|
|
|
/// Attempts to load the .env file once at the start of the main process or at the start of the
|
|
|
|
/// tests. Panics if the .env file cannot be found or if it cannot be parsed (most likely it
|
|
|
|
/// contains invalid UTF-8 bytes).
|
2018-10-10 07:46:20 -07:00
|
|
|
fn load_env_file() {
|
2018-12-11 10:58:56 -08:00
|
|
|
if !LOADED_ENV_FILE.load(Ordering::Relaxed) {
|
|
|
|
match dotenv::dotenv() {
|
|
|
|
Ok(_) => LOADED_ENV_FILE.store(true, Ordering::Relaxed),
|
|
|
|
Err(dotenv::Error::Io(_)) => panic!("could not find .env file"),
|
|
|
|
_ => panic!("could not parse .env file"),
|
2018-10-10 07:46:20 -07:00
|
|
|
};
|
2018-09-25 06:56:44 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-11 10:58:56 -08:00
|
|
|
/// Sets up ctrl-c to change the value of `poagov_is_running` from `true` to `false`. When
|
2018-12-11 15:44:51 -08:00
|
|
|
/// `poagov_is_running` changes to `false`, the `poagov` process begins to gracefully shut down.
|
|
|
|
/// The `AtomicBool` returned by this function is used to indicate whether or not the `poagov`
|
|
|
|
/// binary should continue running.
|
2018-10-10 07:46:20 -07:00
|
|
|
fn set_ctrlc_handler(logger: Arc<Mutex<Logger>>) -> Result<Arc<AtomicBool>> {
|
2018-12-11 10:58:56 -08:00
|
|
|
let poagov_is_running = Arc::new(AtomicBool::new(true));
|
|
|
|
let setup_res = {
|
|
|
|
let poagov_is_running = poagov_is_running.clone();
|
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
logger.lock().unwrap().log_ctrlc_pressed();
|
|
|
|
poagov_is_running.store(false, Ordering::SeqCst);
|
|
|
|
})
|
|
|
|
};
|
|
|
|
if let Err(e) = setup_res {
|
|
|
|
Err(Error::CtrlcSetupError(e))
|
|
|
|
} else {
|
|
|
|
Ok(poagov_is_running)
|
|
|
|
}
|
2018-04-21 09:59:11 -07:00
|
|
|
}
|
2018-05-07 05:11:48 -07:00
|
|
|
|
2018-09-25 06:56:44 -07:00
|
|
|
fn main() -> Result<()> {
|
2018-10-10 07:46:20 -07:00
|
|
|
load_env_file();
|
2018-12-11 06:54:31 -08:00
|
|
|
|
2018-12-11 11:38:58 -08:00
|
|
|
let cli = parse_cli();
|
2018-09-25 06:56:44 -07:00
|
|
|
let config = Config::new(&cli)?;
|
2018-10-10 07:46:20 -07:00
|
|
|
let logger = Arc::new(Mutex::new(Logger::new(&config)));
|
2018-12-11 15:44:51 -08:00
|
|
|
let running = set_ctrlc_handler(logger.clone())?;
|
|
|
|
let client = RpcClient::new(config.endpoint.clone());
|
|
|
|
let blockchain_iter = BlockchainIter::new(&client, &config, running)?;
|
|
|
|
let mut notifier = Notifier::new(&config, logger.clone())?;
|
2018-12-11 11:38:58 -08:00
|
|
|
|
|
|
|
// If email notifications have been enabled but there are no email recipients configured, warn
|
|
|
|
// the user.
|
2018-10-10 07:46:20 -07:00
|
|
|
if config.email_notifications && config.email_recipients.is_empty() {
|
|
|
|
logger.lock().unwrap().log_no_email_recipients_configured();
|
|
|
|
}
|
|
|
|
logger.lock().unwrap().log_starting_poagov();
|
|
|
|
|
2018-12-11 15:44:51 -08:00
|
|
|
'blockchain_walker: for block_range_res in blockchain_iter {
|
|
|
|
let (start_block, stop_block) = block_range_res?;
|
2018-09-25 06:56:44 -07:00
|
|
|
let mut notifications = vec![];
|
2018-12-11 15:44:51 -08:00
|
|
|
|
|
|
|
// For each contract that we are monitoring for governance events, get the ballot-created
|
|
|
|
// events that fall within the current `BlockchainIter`'s block window, convert those
|
|
|
|
// ballot-created logs to `Notification`s.
|
2018-09-25 06:56:44 -07:00
|
|
|
for contract in config.contracts.iter() {
|
|
|
|
let ballot_created_logs = client.get_ballot_created_logs(
|
|
|
|
contract,
|
|
|
|
start_block,
|
|
|
|
stop_block,
|
|
|
|
)?;
|
2018-12-11 06:54:31 -08:00
|
|
|
for log in ballot_created_logs.into_iter() {
|
2018-09-25 06:56:44 -07:00
|
|
|
let notification = if contract.version == ContractVersion::V1 {
|
|
|
|
let voting_state = client.get_voting_state(contract, log.ballot_id)?;
|
|
|
|
Notification::from_voting_state(&config, log, voting_state)
|
|
|
|
} else {
|
|
|
|
let ballot_info = client.get_ballot_info(contract, log.ballot_id)?;
|
|
|
|
Notification::from_ballot_info(&config, log, ballot_info)
|
|
|
|
};
|
|
|
|
notifications.push(notification);
|
|
|
|
}
|
|
|
|
}
|
2018-12-11 15:44:51 -08:00
|
|
|
|
|
|
|
// Sort the notifications by ascending block number.
|
2018-09-25 06:56:44 -07:00
|
|
|
notifications.sort_unstable_by(|notif1, notif2| {
|
|
|
|
notif1.log().block_number.cmp(¬if2.log().block_number)
|
|
|
|
});
|
2018-12-11 15:44:51 -08:00
|
|
|
|
|
|
|
// Notify the governance notifications recipients.
|
|
|
|
for notification in notifications {
|
|
|
|
notifier.notify(¬ification);
|
2018-10-10 07:46:20 -07:00
|
|
|
if notifier.reached_limit() {
|
|
|
|
let limit = config.notification_limit.unwrap();
|
|
|
|
logger.lock().unwrap().log_reached_notification_limit(limit);
|
2018-12-11 15:44:51 -08:00
|
|
|
break 'blockchain_walker;
|
2018-09-25 06:56:44 -07:00
|
|
|
}
|
|
|
|
}
|
2018-12-11 15:44:51 -08:00
|
|
|
|
2018-10-10 07:46:20 -07:00
|
|
|
logger.lock().unwrap().log_finished_block_window(start_block, stop_block);
|
2018-09-25 06:56:44 -07:00
|
|
|
}
|
2018-10-10 07:46:20 -07:00
|
|
|
|
2018-09-25 06:56:44 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
2018-10-10 07:46:20 -07:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
pub mod tests {
|
|
|
|
use super::load_env_file;
|
|
|
|
|
2018-12-11 10:58:56 -08:00
|
|
|
/// Loads the .env file once at the start of the tests.
|
2018-10-10 07:46:20 -07:00
|
|
|
pub fn setup() {
|
2018-12-11 10:58:56 -08:00
|
|
|
load_env_file();
|
2018-10-10 07:46:20 -07:00
|
|
|
}
|
|
|
|
}
|