Applied a handful of cherry-picked rustfmt and clippy changes.

This commit is contained in:
DrPeterVanNostrand 2018-12-14 19:58:33 +00:00
parent 0150df2f8e
commit a29b43c26c
10 changed files with 264 additions and 184 deletions

View File

@ -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);
}
}
}

View File

@ -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<AtomicBool>) -> 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<AtomicBool>) -> 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<AtomicBool>) -> 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<AtomicBool>) -> Result<Self> {
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)),
};
}

View File

@ -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")
}

View File

@ -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<json::Value>,
) -> Result<reqwest::Request>
{
) -> Result<reqwest::Request> {
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<json::Value> {
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<Vec<BallotCreatedLog>>
{
) -> Result<Vec<BallotCreatedLog>> {
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<v1::VotingState> {
pub fn get_voting_state(
&self,
contract: &PoaContract,
ballot_id: U256,
) -> Result<v1::VotingState> {
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<v2::BallotInfo> {
// pub fn get_ballot_info(&self, contract: &PoaContract, ballot_id: U256, voting_key: Option<Address>) -> Result<v2::BallotInfo> {
pub fn get_ballot_info(
&self,
contract: &PoaContract,
ballot_id: U256,
) -> Result<v2::BallotInfo> {
// pub fn get_ballot_info(&self, contract: &PoaContract, ballot_id: U256, voting_key: Option<Address>) -> Result<v2::BallotInfo> {
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);

View File

@ -50,6 +50,10 @@ impl From<BallotType> 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<Self>
{
if contract_type == ContractType::Emission && version == ContractVersion::V1 {
) -> Result<Self> {
// 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<Self> {
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::<u64>() {
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::<u64>() {
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<String> = 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());
}
}
}

View File

@ -41,7 +41,8 @@ fn read_logs_dir() -> Vec<LogFile> {
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();
}

View File

@ -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(())

View File

@ -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);
}

View File

@ -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,
})
}
}

View File

@ -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<Vec<ethabi::Token>> for KeysVotingState {