diff --git a/build.rs b/build.rs index 1b9c4d9..24021f9 100644 --- a/build.rs +++ b/build.rs @@ -16,8 +16,9 @@ fn create_env_file_if_dne() { if !sample_env_path.exists() { panic!("neither the `.env` nor the `sample.env` files exist, one of these files must exist to build the `poagov`"); } - fs::copy(sample_env_path, env_path) - .unwrap_or_else(|e| panic!("failed to create the `.env` file from `sample.env`: {:?}", e)); + if let Err(e) = fs::copy(sample_env_path, env_path) { + panic!("failed to create the `.env` file from `sample.env`: {:?}", e); + } } } diff --git a/src/blockchain.rs b/src/blockchain.rs index 13d5601..fd5e389 100644 --- a/src/blockchain.rs +++ b/src/blockchain.rs @@ -9,7 +9,21 @@ use crate::client::RpcClient; use crate::config::{Config, StartBlock}; use crate::error::{Error, Result}; -fn sleep_or_ctrlc(n_secs: u64, running: Arc) -> Option<()> { +/// Represents the reason why the sleep cycle in `fn sleep_or_ctrlc()` ended. +#[derive(PartialEq)] +enum SleepExit { + CtrlC, + FinishedSleeping, +} + +/// Sleeps for `n_secs` number of seconds or returns early if the user shuts down `poagov` using +/// ctrl-c. +/// +/// Returns `SleepExit::CtrlC` if the user hit ctrl-c while this function was was sleeping or +/// returns `SleepExit::FinishedSleeping` if the function was able to sleep for the entire `n_secs` +/// duration. +fn sleep_or_ctrlc(n_secs: u64, running: Arc) -> SleepExit { + // This `AtomicBool` will become `true` when we have slept for `n_secs`. let done_sleeping = Arc::new(AtomicBool::new(false)); { let done_sleeping = done_sleeping.clone(); @@ -18,16 +32,16 @@ fn sleep_or_ctrlc(n_secs: u64, running: Arc) -> Option<()> { done_sleeping.store(true, Ordering::SeqCst); }); } - loop { + while !done_sleeping.load(Ordering::SeqCst) { if !running.load(Ordering::SeqCst) { - return None; - } - if done_sleeping.load(Ordering::SeqCst) { - return Some(()); + return SleepExit::CtrlC; } } + SleepExit::FinishedSleeping } +/// A type that we use to iterate over the blocks in a blockchain in discrete block-windows (each +/// "block-window" is an inclusively bounded range of block numbers). pub struct BlockchainIter<'a> { client: &'a RpcClient, start_block: u64, @@ -38,6 +52,16 @@ pub struct BlockchainIter<'a> { } impl<'a> BlockchainIter<'a> { + /// Creates a new `BlockchainIter`. + /// + /// # Errors + /// + /// Return an error if the HTTP-RPC server cannot be reached or if the response from the RPC + /// server cannot be parsed. + /// + /// Returns an `Error::StartBlockExceedsLastBlockMined` if the `start_block` that the user + /// passed in via a CLI argument is in the future (i.e. is greater than the block number of the + /// most recently mined block). pub fn new(client: &'a RpcClient, config: &Config, running: Arc) -> Result { let last_mined_block = client.get_last_mined_block_number()?; let start_block = match config.start_block { @@ -47,17 +71,19 @@ impl<'a> BlockchainIter<'a> { StartBlock::Tail(tail) => last_mined_block - tail, }; if start_block > last_mined_block { - return Err(Error::StartBlockExceedsLastBlockMined { start_block, last_mined_block }); + return Err(Error::StartBlockExceedsLastBlockMined { + start_block, + last_mined_block, + }); } - let bc_iter = BlockchainIter { + Ok(BlockchainIter { client, start_block, stop_block: last_mined_block, on_first_iteration: true, block_time: config.block_time, running, - }; - Ok(bc_iter) + }) } } @@ -70,9 +96,11 @@ impl<'a> Iterator for BlockchainIter<'a> { } else { self.start_block = self.stop_block + 1; while self.start_block >= self.stop_block { - sleep_or_ctrlc(self.block_time, self.running.clone())?; - match self.client.get_last_mined_block_number() { - Ok(last_mined) => self.stop_block = last_mined, + if sleep_or_ctrlc(self.block_time, self.running.clone()) == SleepExit::CtrlC { + return None; + } + self.stop_block = match self.client.get_last_mined_block_number() { + Ok(last_mined) => last_mined, Err(e) => return Some(Err(e)), }; } diff --git a/src/cli.rs b/src/cli.rs index 97c227e..c6b5986 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,7 @@ // Some of `Cli`'s methods are not currently being used. #![allow(dead_code)] -use clap::{ArgMatches, App}; +use clap::{App, ArgMatches}; pub fn parse_cli() -> Cli { let cli_args = App::new("poagov") @@ -58,6 +58,10 @@ impl Cli { self.0.is_present("emission") } + pub fn no_contracts_specified(&self) -> bool { + !self.keys() && !self.threshold() && !self.proxy() && !self.emission() + } + pub fn v1(&self) -> bool { self.0.is_present("v1") } diff --git a/src/client.rs b/src/client.rs index b3cefa1..a7b29a8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,8 +6,9 @@ use web3::types::{Address, BlockNumber, Filter, FilterBuilder, U256}; use crate::config::{ContractType, PoaContract}; use crate::error::{Error, Result}; -use crate::response::{v1, v2}; use crate::response::common::BallotCreatedLog; +use crate::response::v1; +use crate::response::v2; #[derive(Debug)] pub enum RpcMethod { @@ -43,8 +44,7 @@ impl RpcClient { &self, method: RpcMethod, params: Vec, - ) -> Result - { + ) -> Result { let method_call = json_rpc::types::request::MethodCall { jsonrpc: Some(json_rpc::types::version::Version::V2), method: method.into(), @@ -60,7 +60,8 @@ impl RpcClient { } fn send(&self, req: reqwest::Request) -> Result { - let resp: json_rpc::types::response::Response = self.client + let resp: json_rpc::types::response::Response = self + .client .execute(req) .map_err(|e| Error::RequestFailed(e))? .json() @@ -68,7 +69,9 @@ impl RpcClient { if let json_rpc::types::response::Response::Single(resp_status) = resp { match resp_status { json_rpc::types::response::Output::Success(resp) => return Ok(resp.result), - json_rpc::types::response::Output::Failure(e) => return Err(Error::JsonRpcResponseFailure(e)), + json_rpc::types::response::Output::Failure(e) => { + return Err(Error::JsonRpcResponseFailure(e)) + } }; } unreachable!("Recieved multiple responses for single request"); @@ -97,8 +100,7 @@ impl RpcClient { contract: &PoaContract, start: BlockNumber, stop: BlockNumber, - ) -> Result> - { + ) -> Result> { let event = contract.event("BallotCreated"); let event_sig = event.signature(); let filter = FilterBuilder::default() @@ -110,9 +112,15 @@ impl RpcClient { self.get_logs(filter)? .into_iter() .map(|web3_log| { - let web3::types::Log {topics, data, block_number, .. } = web3_log; + let web3::types::Log { + topics, + data, + block_number, + .. + } = web3_log; let raw_log = ethabi::RawLog::from((topics, data.0)); - let ethabi_log = event.parse_log(raw_log) + let ethabi_log = event + .parse_log(raw_log) .map_err(|e| Error::FailedToParseRawLogToLog(e))?; BallotCreatedLog::from_ethabi_log(ethabi_log, block_number.unwrap()) }) @@ -120,7 +128,11 @@ impl RpcClient { } /// V1 - pub fn get_voting_state(&self, contract: &PoaContract, ballot_id: U256) -> Result { + pub fn get_voting_state( + &self, + contract: &PoaContract, + ballot_id: U256, + ) -> Result { let function = contract.function("votingState"); let tokens = vec![ethabi::Token::Uint(ballot_id)]; let encoded_input = function.encode_input(&tokens).unwrap(); @@ -156,8 +168,12 @@ impl RpcClient { // TODO: When V2 contracts have been published and ballots have begun, test that calling // `.getBallotInfo()` with `Address::zero()` for the `votingKey` works (we don't care if // `votingKey` has voted yet). - pub fn get_ballot_info(&self, contract: &PoaContract, ballot_id: U256) -> Result { - // pub fn get_ballot_info(&self, contract: &PoaContract, ballot_id: U256, voting_key: Option
) -> Result { + pub fn get_ballot_info( + &self, + contract: &PoaContract, + ballot_id: U256, + ) -> Result { + // pub fn get_ballot_info(&self, contract: &PoaContract, ballot_id: U256, voting_key: Option
) -> Result { let function = contract.function("getBallotInfo"); /* let mut tokens = vec![ethabi::Token::Uint(ballot_id)]; @@ -208,15 +224,16 @@ mod tests { use web3::types::BlockNumber; - use crate::tests::setup; - use crate::config::{ContractType, ContractVersion, Network, PoaContract}; use super::RpcClient; + use crate::config::{ContractType, ContractVersion, Network, PoaContract}; + use crate::tests::setup; #[test] fn test_get_last_mined_block() { setup(); - let sokol_url = env::var("SOKOL_RPC_ENDPOINT").expect("Missing env-var: `SOKOL_RPC_ENDPOINT`"); + let sokol_url = + env::var("SOKOL_RPC_ENDPOINT").expect("Missing env-var: `SOKOL_RPC_ENDPOINT`"); let client = RpcClient::new(sokol_url); let res = client.get_last_mined_block_number(); println!("\nsokol last mined block number => {:?}", res); @@ -232,18 +249,13 @@ mod tests { #[test] fn test_get_ballot_created_logs() { setup(); - let contract = PoaContract::read( - ContractType::Keys, - Network::Sokol, - ContractVersion::V1, - ).unwrap_or_else(|e| panic!("Failed to load contract: {:?}", e)); - let endpoint = env::var("SOKOL_RPC_ENDPOINT").expect("Missing env-var: `SOKOL_RPC_ENDPOINT`"); + let contract = PoaContract::read(ContractType::Keys, Network::Sokol, ContractVersion::V1) + .unwrap_or_else(|e| panic!("Failed to load contract: {:?}", e)); + let endpoint = + env::var("SOKOL_RPC_ENDPOINT").expect("Missing env-var: `SOKOL_RPC_ENDPOINT`"); let client = RpcClient::new(endpoint); - let res = client.get_ballot_created_logs( - &contract, - BlockNumber::Earliest, - BlockNumber::Latest, - ); + let res = + client.get_ballot_created_logs(&contract, BlockNumber::Earliest, BlockNumber::Latest); println!("{:#?}", res); assert!(res.is_ok()); } @@ -273,12 +285,10 @@ mod tests { #[test] fn test_get_voting_state() { setup(); - let contract = PoaContract::read( - ContractType::Threshold, - Network::Sokol, - ContractVersion::V1 - ).unwrap_or_else(|e| panic!("Failed to load contract: {:?}", e)); - let sokol_url = env::var("SOKOL_RPC_ENDPOINT").expect("Missing env-var: `SOKOL_RPC_ENDPOINT`"); + let contract = PoaContract::read(ContractType::Threshold, Network::Sokol, ContractVersion::V1) + .unwrap_or_else(|e| panic!("Failed to load contract: {:?}", e)); + let sokol_url = env::var("SOKOL_RPC_ENDPOINT") + .expect("Missing env-var: `SOKOL_RPC_ENDPOINT`"); let client = RpcClient::new(sokol_url); let res = client.get_voting_state(&contract, 2.into()); println!("{:#?}", res); diff --git a/src/config.rs b/src/config.rs index d02ce5c..dba9aeb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -50,6 +50,10 @@ impl From for ContractType { } impl ContractType { + fn is_emission(&self) -> bool { + *self == ContractType::Emission + } + fn uppercase(&self) -> &str { match self { ContractType::Keys => "KEYS", @@ -76,6 +80,10 @@ pub enum ContractVersion { } impl ContractVersion { + fn is_v1(&self) -> bool { + *self == ContractVersion::V1 + } + fn lowercase(&self) -> &str { match self { ContractVersion::V1 => "v1", @@ -103,46 +111,44 @@ impl Debug for PoaContract { } impl PoaContract { + fn new( + kind: ContractType, + version: ContractVersion, + addr: Address, + abi: ethabi::Contract, + ) -> Self { + PoaContract { kind, version, addr, abi } + } + pub fn read( contract_type: ContractType, network: Network, version: ContractVersion, - ) -> Result - { - if contract_type == ContractType::Emission && version == ContractVersion::V1 { + ) -> Result { + // Exit quickly if we know that the contract does not exist. + if contract_type.is_emission() && version.is_v1() { return Err(Error::EmissionFundsV1ContractDoesNotExist); } - let addr_env_var = format!( + let env_var = format!( "{}_CONTRACT_ADDRESS_{}_{:?}", contract_type.uppercase(), network.uppercase(), - version, + version ); - - let addr = if let Ok(s) = env::var(&addr_env_var) { - match Address::from_str(s.trim_left_matches("0x")) { - Ok(addr) => addr, - Err(_) => return Err(Error::InvalidContractAddr(s.into())), - } - } else { - return Err(Error::MissingEnvVar(addr_env_var)); - }; + let contract_addr_str = env::var(&env_var).map_err(|_| Error::MissingEnvVar(env_var))?; + let contract_addr = Address::from_str(contract_addr_str.trim_left_matches("0x")) + .map_err(|_| Error::InvalidContractAddr(contract_addr_str.to_string()))?; let abi_path = format!( "abis/{}/{}", version.lowercase(), - contract_type.abi_file_name(), + contract_type.abi_file_name() ); + let abi_file = File::open(&abi_path).map_err(|_| Error::MissingAbiFile(abi_path.clone()))?; + let abi = Contract::load(&abi_file).map_err(|_| Error::InvalidAbi(abi_path))?; - let abi_file = File::open(&abi_path) - .map_err(|_| Error::MissingAbiFile(abi_path.clone()))?; - - let abi = Contract::load(&abi_file) - .map_err(|_| Error::InvalidAbi(abi_path))?; - - let contract = PoaContract { kind: contract_type, version, addr, abi }; - Ok(contract) + Ok(PoaContract::new(contract_type, version, contract_addr, abi)) } pub fn event(&self, event: &str) -> Event { @@ -184,85 +190,78 @@ pub struct Config { impl Config { pub fn new(cli: &Cli) -> Result { - let network = if cli.core() == cli.sokol() { - return Err(Error::MustSpecifyOneCliArgument("`--core` or `--sokol`".into())); - } else if cli.core() { + if cli.core() == cli.sokol() { + return Err(Error::MustSpecifyOneCliArgument("--core, --sokol".to_string())); + } + if cli.v1() == cli.v2() { + return Err(Error::MustSpecifyOneCliArgument("--v1, --v2".to_string())); + } + if cli.no_contracts_specified() { + return Err(Error::MustSpecifyAtLeastOneCliArgument( + "--keys, --threshold, --proxy, --emission".to_string().to_string(), + )); + } + if cli.multiple_start_blocks_specified() { + return Err(Error::MustSpecifyOneCliArgument( + "--earliest, --latest, --start-block, --tail".to_string() + )); + } + + let network = if cli.core() { Network::Core } else { Network::Sokol }; + let version = if cli.v1() { + ContractVersion::V1 + } else { + ContractVersion::V2 + }; + let endpoint_env_var = format!("{}_RPC_ENDPOINT", network.uppercase()); let endpoint = env::var(&endpoint_env_var) .map_err(|_| Error::MissingEnvVar(endpoint_env_var))?; - let version = if cli.v1() == cli.v2(){ - return Err(Error::MustSpecifyOneCliArgument("`--v1` or `--v2`".into())); - } else if cli.v1(){ - ContractVersion::V1 - } else { - ContractVersion::V2 - }; - let mut contracts = vec![]; if cli.keys() { - let keys = PoaContract::read( - ContractType::Keys, - network, - version - )?; - contracts.push(keys); + let keys_contract = PoaContract::read(ContractType::Keys, network, version)?; + contracts.push(keys_contract); } if cli.threshold() { - let threshold = PoaContract::read( - ContractType::Threshold, - network, - version - )?; - contracts.push(threshold); + let threshold_contract = PoaContract::read(ContractType::Threshold, network, version)?; + contracts.push(threshold_contract); } if cli.proxy() { - let proxy = PoaContract::read( - ContractType::Proxy, - network, - version - )?; - contracts.push(proxy); + let proxy_contract = PoaContract::read(ContractType::Proxy, network, version)?; + contracts.push(proxy_contract); } if cli.emission() { - let emission_funds = PoaContract::read( - ContractType::Emission, - network, - version, - )?; + let emission_funds = PoaContract::read(ContractType::Emission, network, version)?; contracts.push(emission_funds); } - if contracts.is_empty() { - return Err(Error::MustSpecifyAtLeastOneCliArgument( - "`--keys`, `--threshold`, `--proxy`, `--emission`".into() - )); - } - let start_block = if cli.multiple_start_blocks_specified() { - return Err(Error::MustSpecifyOneCliArgument( - "`--earliest` or `--latest` or `--start-block` or `--tail`".into() - )); - } else if cli.earliest() { + let start_block = if cli.earliest() { StartBlock::Earliest } else if cli.latest() { StartBlock::Latest - } else if let Some(s) = cli.start_block() { - let block_number = s.parse().map_err(|_| Error::InvalidStartBlock(s.into()))?; - StartBlock::Number(block_number) - } else if let Some(s) = cli.tail() { - let tail = s.parse().map_err(|_| Error::InvalidTail(s.into()))?; - StartBlock::Tail(tail) + } else if let Some(start_block_str) = cli.start_block() { + match start_block_str.parse::() { + Ok(block_number) => StartBlock::Number(block_number), + _ => return Err(Error::InvalidStartBlock(start_block_str.to_string())), + } + } else if let Some(tail_str) = cli.tail() { + match tail_str.parse::() { + Ok(tail) => StartBlock::Tail(tail), + _ => return Err(Error::InvalidTail(tail_str.to_string())), + } } else { + // TODO: use `DEFAULT_START_BLOCK`? unreachable!(); }; - let block_time = if let Some(s) = cli.block_time() { - s.parse().map_err(|_| Error::InvalidBlockTime(s.into()))? + let block_time = if let Some(n_secs_str) = cli.block_time() { + n_secs_str.parse().map_err(|_| Error::InvalidBlockTime(n_secs_str.to_string()))? } else { DEFAULT_BLOCK_TIME_SECS }; @@ -272,13 +271,7 @@ impl Config { let email_recipients: Vec = env::var("EMAIL_RECIPIENTS") .map_err(|_| Error::MissingEnvVar("EMAIL_RECIPIENTS".into()))? .split(',') - .filter_map(|s| { - if s.is_empty() { - None - } else { - Some(s.into()) - } - }) + .filter_map(|s| if s.is_empty() { None } else { Some(s.into()) }) .collect(); let smtp_host_domain = if email_notifications { @@ -325,7 +318,9 @@ impl Config { }; let notification_limit = if let Some(s) = cli.notification_limit() { - let limit = s.parse().map_err(|_| Error::InvalidNotificationLimit(s.into()))?; + let limit = s + .parse() + .map_err(|_| Error::InvalidNotificationLimit(s.into()))?; Some(limit) } else { None @@ -334,7 +329,7 @@ impl Config { let log_emails = cli.log_emails(); let log_to_file = cli.log_to_file(); - let config = Config { + Ok(Config { network, endpoint, version, @@ -351,8 +346,7 @@ impl Config { notification_limit, log_emails, log_to_file, - }; - Ok(config) + }) } } @@ -360,14 +354,14 @@ impl Config { mod tests { use std::env; - use super::super::tests::setup; - use super::{ContractType, ContractVersion, PoaContract, Network}; + use super::{ContractType, ContractVersion, Network, PoaContract}; + use crate::tests::setup; const CONTRACT_TYPES: [ContractType; 4] = [ - ContractType::Keys, - ContractType::Threshold, - ContractType::Proxy, - ContractType::Emission, + ContractType::Keys, + ContractType::Threshold, + ContractType::Proxy, + ContractType::Emission, ]; const NETWORKS: [Network; 2] = [Network::Sokol, Network::Core]; const VERSIONS: [ContractVersion; 2] = [ContractVersion::V1, ContractVersion::V2]; @@ -380,7 +374,7 @@ mod tests { assert!(env::var(&env_var).is_ok()); for contract_type in CONTRACT_TYPES.iter() { for version in VERSIONS.iter() { - if *contract_type == ContractType::Emission && *version == ContractVersion::V1 { + if contract_type.is_emission() && version.is_v1() { continue; } let env_var = format!( @@ -400,13 +394,12 @@ mod tests { setup(); for contract_type in CONTRACT_TYPES.iter() { for version in VERSIONS.iter() { + if contract_type.is_emission() && version.is_v1() { + continue; + } for network in NETWORKS.iter() { let res = PoaContract::read(*contract_type, *network, *version); - if *contract_type == ContractType::Emission && *version == ContractVersion::V1 { - assert!(res.is_err()); - } else { - assert!(res.is_ok()); - } + assert!(res.is_ok()); } } } diff --git a/src/logger.rs b/src/logger.rs index 6e45d8a..ff4df4f 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -41,7 +41,8 @@ fn read_logs_dir() -> Vec { let path = res.ok()?.path(); let file_name = path.file_name().unwrap().to_str().unwrap(); LogFile::from_file_name(file_name).ok() - }).collect(); + }) + .collect(); log_files.sort_unstable(); log_files } @@ -106,14 +107,12 @@ impl LogFile { fn create_file(&self) -> File { let path = self.path(); - File::create(&path) - .unwrap_or_else(|_| panic!("failed to create log file: {}", path)) + File::create(&path).unwrap_or_else(|_| panic!("failed to create log file: {}", path)) } fn remove_file(&self) { let path = self.path(); - remove_file(&path) - .unwrap_or_else(|_| panic!("failed to delete log file: {}", path)) + remove_file(&path).unwrap_or_else(|_| panic!("failed to delete log file: {}", path)) } } @@ -185,17 +184,27 @@ impl Logger { } pub fn log_ctrlc_pressed(&mut self) { - warn!(&self.logger, "recieved ctrl-c signal, gracefully shutting down..."); + warn!( + &self.logger, + "received ctrl-c signal, gracefully shutting down..." + ); self.increment_log_count(); } pub fn log_no_email_recipients_configured(&mut self) { - warn!(&self.logger, "email notifications are enabled, but there are no email recipients"); + warn!( + &self.logger, + "email notifications are enabled, but there are no email recipients" + ); self.increment_log_count(); } pub fn log_notification_email_body(&mut self, notif: &Notification) { - info!(&self.logger, "governance notification\n{}", notif.email_text()); + info!( + &self.logger, + "governance notification\n{}", + notif.email_text() + ); self.increment_log_count(); } diff --git a/src/main.rs b/src/main.rs index 6b929dd..de0dd4a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -33,7 +33,7 @@ fn load_env_file() { 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"), + _ => panic!("could not parse .env file"), }; } } @@ -89,13 +89,16 @@ fn main() -> Result<()> { start_block, stop_block, )?; - for log in ballot_created_logs.into_iter() { - 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) + for log in ballot_created_logs { + let notification = match contract.version { + ContractVersion::V1 => { + let voting_state = client.get_voting_state(contract, log.ballot_id)?; + Notification::from_voting_state(&config, log, voting_state) + } + ContractVersion::V2 => { + let ballot_info = client.get_ballot_info(contract, log.ballot_id)?; + Notification::from_ballot_info(&config, log, ballot_info) + } }; notifications.push(notification); } @@ -116,7 +119,10 @@ fn main() -> Result<()> { } } - logger.lock().unwrap().log_finished_block_window(start_block, stop_block); + logger + .lock() + .unwrap() + .log_finished_block_window(start_block, stop_block); } Ok(()) diff --git a/src/notify.rs b/src/notify.rs index 5d68ca0..5058f9e 100644 --- a/src/notify.rs +++ b/src/notify.rs @@ -33,29 +33,35 @@ impl<'a> Notification<'a> { config: &'a Config, log: BallotCreatedLog, voting_state: VotingState, - ) -> Self - { - Notification::VotingState { config, log, voting_state } + ) -> Self { + Notification::VotingState { + config, + log, + voting_state, + } } pub fn from_ballot_info( config: &'a Config, log: BallotCreatedLog, ballot_info: BallotInfo, - ) -> Self - { - Notification::BallotInfo { config, log, ballot_info } + ) -> Self { + Notification::BallotInfo { + config, + log, + ballot_info, + } } pub fn email_text(&self) -> String { format!( "Network: {:?}\n\ - RPC Endpoint: {}\n\ - Block Number: {}\n\ - Contract: {}\n\ - Version: {:?}\n\ - Ballot ID: {}\n\ - {}\n", + RPC Endpoint: {}\n\ + Block Number: {}\n\ + Contract: {}\n\ + Version: {:?}\n\ + Ballot ID: {}\n\ + {}\n", self.config().network, self.config().endpoint, self.log().block_number, @@ -127,12 +133,20 @@ impl<'a> Notifier<'a> { } else { None }; - Ok(Notifier { config, emailer, logger, notification_count: 0 }) + Ok(Notifier { + config, + emailer, + logger, + notification_count: 0, + }) } pub fn notify(&mut self, notif: &Notification) { if self.config.log_emails { - self.logger.lock().unwrap().log_notification_email_body(notif); + self.logger + .lock() + .unwrap() + .log_notification_email_body(notif); } else { self.logger.lock().unwrap().log_notification(notif); } @@ -143,10 +157,13 @@ impl<'a> Notifier<'a> { Err(e) => { self.logger.lock().unwrap().log_failed_to_build_email(e); continue; - }, + } }; if let Err(e) = self.send_email(email) { - self.logger.lock().unwrap().log_failed_to_send_email(recipient, e); + self.logger + .lock() + .unwrap() + .log_failed_to_send_email(recipient, e); } else { self.logger.lock().unwrap().log_email_sent(recipient); } diff --git a/src/response/common.rs b/src/response/common.rs index d8f3b37..dc37648 100644 --- a/src/response/common.rs +++ b/src/response/common.rs @@ -2,7 +2,6 @@ #![allow(deprecated)] use chrono::{DateTime, NaiveDateTime, Utc}; -use ethabi; use web3::types::{Address, H256, U256}; use crate::error::{Error, Result}; @@ -129,12 +128,25 @@ impl BallotCreatedLog { }; let ballot_type = match ballot_type { Some(ballot_type) => ballot_type, - None => return Err(Error::FailedToParseBallotCreatedLog("missing `ballot_type`".into())), + None => { + return Err(Error::FailedToParseBallotCreatedLog( + "missing `ballot_type`".to_string(), + )) + } }; let creator = match creator { Some(creator) => creator, - None => return Err(Error::FailedToParseBallotCreatedLog("missing `creator`".into())), + None => { + return Err(Error::FailedToParseBallotCreatedLog( + "missing `creator`".to_string(), + )) + } }; - Ok(BallotCreatedLog { ballot_id, ballot_type, creator, block_number }) + Ok(BallotCreatedLog { + ballot_id, + ballot_type, + creator, + block_number, + }) } } diff --git a/src/response/v1.rs b/src/response/v1.rs index 3fa528e..f5ba56b 100644 --- a/src/response/v1.rs +++ b/src/response/v1.rs @@ -96,7 +96,7 @@ pub struct KeysVotingState { pub index: U256, pub min_threshold_of_voters: U256, pub creator: Address, - pub memo: String + pub memo: String, } impl From> for KeysVotingState {