diff --git a/Cargo.lock b/Cargo.lock index 441c5071b3..7250677e8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,21 +605,10 @@ dependencies = [ "num-integer", "num-traits 0.2.14", "serde 1.0.130", - "time", + "time 0.1.44", "winapi 0.3.9", ] -[[package]] -name = "chrono-english" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0be5180df5f7c41fc2416bc038bc8d78d44db8136c415b94ccbc95f523dc38e9" -dependencies = [ - "chrono", - "scanlex", - "time", -] - [[package]] name = "cidr" version = "0.1.1" @@ -1921,7 +1910,7 @@ dependencies = [ "log 0.3.9", "mime 0.2.6", "num_cpus", - "time", + "time 0.1.44", "traitobject", "typeable", "unicase", @@ -3777,12 +3766,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scanlex" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088c5d71572124929ea7549a8ce98e1a6fd33d0a38367b09027b382e67c033db" - [[package]] name = "schannel" version = "0.1.19" @@ -4591,9 +4574,8 @@ dependencies = [ name = "tari_console_wallet" version = "0.21.0" dependencies = [ + "anyhow", "bitflags 1.3.2", - "chrono", - "chrono-english", "crossterm 0.17.7", "futures 0.3.17", "log 0.4.14", @@ -4619,6 +4601,7 @@ dependencies = [ "tari_shutdown", "tari_wallet", "thiserror", + "time 0.3.4", "tokio 1.13.0", "tonic", "tracing", @@ -4802,7 +4785,7 @@ dependencies = [ "tari_core", "tari_crypto", "thiserror", - "time", + "time 0.1.44", "tokio 1.13.0", "tonic", ] @@ -5037,7 +5020,7 @@ dependencies = [ "tari_test_utils", "tempfile", "thiserror", - "time", + "time 0.1.44", "tokio 1.13.0", "tower 0.3.1", ] @@ -5191,6 +5174,23 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "time" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99beeb0daeac2bd1e86ac2c21caddecb244b39a093594da1a661ec2060c7aedd" +dependencies = [ + "itoa", + "libc", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25eb0ca3468fc0acc11828786797f6ef9aa1555e4a211a60d64cc8e4d1be47d6" + [[package]] name = "tiny-keccak" version = "2.0.2" diff --git a/applications/tari_console_wallet/Cargo.toml b/applications/tari_console_wallet/Cargo.toml index ea49dc33cc..1881c45448 100644 --- a/applications/tari_console_wallet/Cargo.toml +++ b/applications/tari_console_wallet/Cargo.toml @@ -17,9 +17,8 @@ tari_app_grpc = { path = "../tari_app_grpc" } tari_shutdown = { path = "../../infrastructure/shutdown" } tari_key_manager = { path = "../../base_layer/key_manager" } +anyhow = "1.0.44" bitflags = "1.2.1" -chrono = { version = "0.4.6", features = ["serde"] } -chrono-english = "0.1" futures = { version = "^0.3.16", default-features = false, features = ["alloc"] } crossterm = { version = "0.17" } rand = "0.8" @@ -35,6 +34,7 @@ strum_macros = "^0.19" tokio = { version = "1.11", features = ["signal"] } thiserror = "1.0.26" tonic = "0.5.2" +time = { version = "0.3.4", features = ["formatting", "local-offset", "macros", "parsing"] } tracing = "0.1.26" tracing-opentelemetry = "0.15.0" diff --git a/applications/tari_console_wallet/README.md b/applications/tari_console_wallet/README.md index 0b8f0bd2a5..8842d68005 100644 --- a/applications/tari_console_wallet/README.md +++ b/applications/tari_console_wallet/README.md @@ -72,12 +72,14 @@ Make it rain! Send many transactions to a public key or emoji id. `` can be `negotiated` or `one_sided` +`` is a string in RFC3339 format or `now` + example: ``` $ tari_console_wallet --command "make-it-rain 1 10 8000 100 now c69fbe5f05a304eaec65d5f234a6aa258a90b8bb5b9ceffea779653667ef2108 negotiated makin it rain yo" -1. make-it-rain 1 10 8000 µT 100 µT 2021-03-26 10:03:30.459157 UTC c69fbe5f05a304eaec65d5f234a6aa258a90b8bb5b9ceffea779653667ef2108 negotiated makin it rain yo +1. make-it-rain 1 10 8000 µT 100 µT 2021-03-26T10:03:30Z c69fbe5f05a304eaec65d5f234a6aa258a90b8bb5b9ceffea779653667ef2108 negotiated makin it rain yo Monitoring 10 sent transactions to Broadcast stage... Done! All transactions monitored to Broadcast stage. diff --git a/applications/tari_console_wallet/src/automation/command_parser.rs b/applications/tari_console_wallet/src/automation/command_parser.rs index d8eca4a212..b7cc3327e0 100644 --- a/applications/tari_console_wallet/src/automation/command_parser.rs +++ b/applications/tari_console_wallet/src/automation/command_parser.rs @@ -22,8 +22,6 @@ use crate::automation::{commands::WalletCommand, error::ParseError}; -use chrono::{DateTime, Utc}; -use chrono_english::{parse_date_string, Dialect}; use core::str::SplitWhitespace; use std::{ fmt::{Display, Formatter}, @@ -34,6 +32,7 @@ use tari_comms::multiaddr::Multiaddr; use tari_common_types::types::PublicKey; use tari_core::transactions::tari_amount::MicroTari; +use time::{format_description::well_known::Rfc3339, OffsetDateTime}; #[derive(Debug)] pub struct ParsedCommand { @@ -78,7 +77,7 @@ pub enum ParsedArgument { Text(String), Float(f64), Int(u64), - Date(DateTime), + Date(OffsetDateTime), OutputToCSVFile(String), CSVFileName(String), Address(Multiaddr), @@ -216,11 +215,10 @@ fn parse_make_it_rain(mut args: SplitWhitespace) -> Result, // start time utc or 'now' let start_time = args.next().ok_or_else(|| ParseError::Empty("start time".to_string()))?; - let now = Utc::now(); let start_time = if start_time != "now" { - parse_date_string(start_time, now, Dialect::Uk).map_err(ParseError::Date)? + OffsetDateTime::parse(start_time, &Rfc3339)? } else { - now + OffsetDateTime::now_utc() }; parsed_args.push(ParsedArgument::Date(start_time)); diff --git a/applications/tari_console_wallet/src/automation/commands.rs b/applications/tari_console_wallet/src/automation/commands.rs index 34301b3d45..5706df5087 100644 --- a/applications/tari_console_wallet/src/automation/commands.rs +++ b/applications/tari_console_wallet/src/automation/commands.rs @@ -30,7 +30,6 @@ use std::{ time::{Duration, Instant}, }; -use chrono::{DateTime, Utc}; use futures::FutureExt; use strum_macros::{Display, EnumIter, EnumString}; use tari_crypto::ristretto::pedersen::PedersenCommitmentFactory; @@ -59,6 +58,7 @@ use tari_wallet::{ transaction_service::handle::{TransactionEvent, TransactionServiceHandle}, WalletSqlite, }; +use time::OffsetDateTime; use tokio::{ sync::{broadcast, mpsc}, time::{sleep, timeout}, @@ -274,7 +274,7 @@ pub async fn make_it_rain( }?; let start_time = match args[4].clone() { - Date(dt) => Ok(dt as DateTime), + Date(dt) => Ok(dt), _ => Err(CommandError::Argument), }?; @@ -296,13 +296,13 @@ pub async fn make_it_rain( // We are spawning this command in parallel, thus not collecting transaction IDs tokio::task::spawn(async move { // Wait until specified test start time - let now = Utc::now(); + let now = OffsetDateTime::now_utc(); let delay_ms = if start_time > now { println!( "`make-it-rain` scheduled to start at {}: msg \"{}\"", start_time, message ); - (start_time - now).num_milliseconds() as u64 + (start_time - now).whole_milliseconds() as u64 } else { 0 }; @@ -314,7 +314,7 @@ pub async fn make_it_rain( sleep(Duration::from_millis(delay_ms)).await; let num_txs = (txps * duration as f64) as usize; - let started_at = Utc::now(); + let started_at = OffsetDateTime::now_utc(); struct TransactionSendStats { i: usize, @@ -348,7 +348,7 @@ pub async fn make_it_rain( ParsedArgument::Text(message.clone()), ]; // Manage transaction submission rate - let actual_ms = (Utc::now() - started_at).num_milliseconds(); + let actual_ms = (OffsetDateTime::now_utc() - started_at).whole_milliseconds() as i64; let target_ms = (i as f64 / (txps / 1000.0)) as i64; if target_ms - actual_ms > 0 { // Maximum delay between Txs set to 120 s @@ -420,7 +420,7 @@ pub async fn make_it_rain( num_txs, transaction_type, message, - Utc::now() + OffsetDateTime::now_utc(), ); }); diff --git a/applications/tari_console_wallet/src/automation/error.rs b/applications/tari_console_wallet/src/automation/error.rs index 483d2ae371..ead7e548f4 100644 --- a/applications/tari_console_wallet/src/automation/error.rs +++ b/applications/tari_console_wallet/src/automation/error.rs @@ -22,7 +22,6 @@ use std::num::{ParseFloatError, ParseIntError}; -use chrono_english::DateError; use log::*; use tari_common::exit_codes::ExitCodes; use tari_core::transactions::{ @@ -35,6 +34,7 @@ use tari_wallet::{ transaction_service::error::TransactionServiceError, }; use thiserror::Error; +use time::error::ComponentRange; use tokio::task::JoinError; pub const LOG_TARGET: &str = "wallet::automation::error"; @@ -88,7 +88,9 @@ pub enum ParseError { #[error("Failed to parse int.")] Int(#[from] ParseIntError), #[error("Failed to parse date. {0}")] - Date(#[from] DateError), + Date(#[from] time::error::Parse), + #[error("Failed to convert time. {0}")] + TimeRange(#[from] ComponentRange), #[error("Failed to parse a net address.")] Address, #[error("Invalid combination of arguments ({0}).")] diff --git a/applications/tari_console_wallet/src/recovery.rs b/applications/tari_console_wallet/src/recovery.rs index d3c247977a..2b2ff73aa6 100644 --- a/applications/tari_console_wallet/src/recovery.rs +++ b/applications/tari_console_wallet/src/recovery.rs @@ -20,7 +20,6 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use chrono::offset::Local; use futures::FutureExt; use log::*; use rustyline::Editor; @@ -36,6 +35,7 @@ use tari_wallet::{ use crate::wallet_modes::PeerConfig; use tari_key_manager::cipher_seed::CipherSeed; +use time::OffsetDateTime; use tokio::sync::broadcast; pub const LOG_TARGET: &str = "wallet::recovery"; @@ -126,14 +126,14 @@ pub async fn wallet_recovery(wallet: &WalletSqlite, base_node_config: &PeerConfi debug!( target: LOG_TARGET, "{}: Recovery process {}% complete ({} of {} utxos).", - Local::now(), + OffsetDateTime::now_local().unwrap(), percentage_progress, current, total ); println!( "{}: Recovery process {}% complete ({} of {} utxos).", - Local::now(), + OffsetDateTime::now_local().unwrap(), percentage_progress, current, total diff --git a/applications/tari_console_wallet/src/ui/components/notification_tab.rs b/applications/tari_console_wallet/src/ui/components/notification_tab.rs index 80a6685879..00a115f93f 100644 --- a/applications/tari_console_wallet/src/ui/components/notification_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/notification_tab.rs @@ -9,7 +9,9 @@ // notification, the UI should go there if I click on it. use crate::ui::{components::Component, state::AppState}; +use anyhow::Error; use tari_comms::runtime::Handle; +use time::{error::Format, format_description::FormatItem, macros::format_description}; use tui::{ backend::Backend, layout::{Constraint, Layout, Rect}, @@ -19,6 +21,8 @@ use tui::{ Frame, }; +const DT_FORMAT: &[FormatItem<'static>] = format_description!("[year]-[month]-[day] [hour]-[minute]-[second] "); + pub struct NotificationTab {} impl NotificationTab { @@ -26,7 +30,7 @@ impl NotificationTab { Self {} } - fn draw_notifications(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) + fn draw_notifications(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) -> Result<(), Error> where B: Backend { let block = Block::default().borders(Borders::ALL).title(Span::styled( "Notifications", @@ -37,22 +41,21 @@ impl NotificationTab { .constraints([Constraint::Min(42)].as_ref()) .margin(1) .split(area); - let mut text: Vec = app_state + let text = app_state .get_notifications() .iter() + .rev() .map(|(time, line)| { - Spans::from(vec![ - Span::styled( - time.format("%Y-%m-%d %H:%M:%S ").to_string(), - Style::default().fg(Color::LightGreen), - ), + Ok(Spans::from(vec![ + Span::styled(time.format(&DT_FORMAT)?, Style::default().fg(Color::LightGreen)), Span::raw(line), - ]) + ])) }) - .collect(); - text.reverse(); - let paragraph = Paragraph::new(text.clone()).wrap(Wrap { trim: true }); + .collect::, Format>>() + .unwrap(); + let paragraph = Paragraph::new(text).wrap(Wrap { trim: true }); f.render_widget(paragraph, notifications_area[0]); + Ok(()) } } @@ -61,7 +64,9 @@ impl Component for NotificationTab { let areas = Layout::default() .constraints([Constraint::Min(42)].as_ref()) .split(area); - self.draw_notifications(f, areas[0], app_state); + if let Err(err) = self.draw_notifications(f, areas[0], app_state) { + log::error!("Notification tab rendering failed: {}", err); + } } fn on_tick(&mut self, app_state: &mut AppState) { diff --git a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs index 2c9c49f8b6..9642abb085 100644 --- a/applications/tari_console_wallet/src/ui/components/transactions_tab.rs +++ b/applications/tari_console_wallet/src/ui/components/transactions_tab.rs @@ -6,8 +6,9 @@ use crate::ui::{ widgets::{draw_dialog, MultiColumnList, WindowedListState}, MAX_WIDTH, }; -use chrono::{DateTime, Local}; +use anyhow::Error; use tari_common_types::transaction::{TransactionDirection, TransactionStatus}; +use time::{format_description::FormatItem, macros::format_description, UtcOffset}; use tokio::runtime::Handle; use tui::{ backend::Backend, @@ -18,6 +19,8 @@ use tui::{ Frame, }; +const DT_FORMAT: &[FormatItem<'static>] = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); + pub struct TransactionsTab { balance: Balance, selected_tx_list: SelectedTransactionList, @@ -41,7 +44,7 @@ impl TransactionsTab { } } - fn draw_transaction_lists(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) + fn draw_transaction_lists(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) -> Result<(), Error> where B: Backend { let (pending_constraint, completed_constraint) = if app_state.get_pending_txs().is_empty() { self.selected_tx_list = SelectedTransactionList::CompletedTxs; @@ -66,12 +69,20 @@ impl TransactionsTab { .title(Span::styled("(P)ending Transactions", style)); f.render_widget(block, list_areas[0]); - self.draw_pending_transactions(f, list_areas[0], app_state); - self.draw_completed_transactions(f, list_areas[1], app_state); + self.draw_pending_transactions(f, list_areas[0], app_state)?; + self.draw_completed_transactions(f, list_areas[1], app_state)?; + Ok(()) } - fn draw_pending_transactions(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) - where B: Backend { + fn draw_pending_transactions( + &mut self, + f: &mut Frame, + area: Rect, + app_state: &AppState, + ) -> Result<(), Error> + where + B: Backend, + { // Pending Transactions self.pending_list_state.set_num_items(app_state.get_pending_txs().len()); let mut pending_list_state = self @@ -114,9 +125,10 @@ impl TransactionsTab { }; column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); } - let local_time = DateTime::::from_utc(t.timestamp, Local::now().offset().to_owned()); + let offset = UtcOffset::current_local_offset()?; + let local_time = t.timestamp.replace_offset(offset); column2_items.push(ListItem::new(Span::styled( - format!("{}", local_time.format("%Y-%m-%d %H:%M:%S")), + local_time.format(&DT_FORMAT)?, Style::default().fg(text_color), ))); column3_items.push(ListItem::new(Span::styled( @@ -134,10 +146,18 @@ impl TransactionsTab { .add_column(Some("Local Date/Time"), Some(20), column2_items) .add_column(Some("Message"), None, column3_items); column_list.render(f, area, &mut pending_list_state); + Ok(()) } - fn draw_completed_transactions(&mut self, f: &mut Frame, area: Rect, app_state: &AppState) - where B: Backend { + fn draw_completed_transactions( + &mut self, + f: &mut Frame, + area: Rect, + app_state: &AppState, + ) -> Result<(), Error> + where + B: Backend, + { // Completed Transactions let style = if self.selected_tx_list == SelectedTransactionList::CompletedTxs { Style::default().fg(Color::Magenta).add_modifier(Modifier::BOLD) @@ -203,9 +223,10 @@ impl TransactionsTab { let amount_style = Style::default().fg(color); column1_items.push(ListItem::new(Span::styled(format!("{}", t.amount), amount_style))); } - let local_time = DateTime::::from_utc(t.timestamp, Local::now().offset().to_owned()); + let offset = UtcOffset::current_local_offset()?; + let local_time = t.timestamp.replace_offset(offset); column2_items.push(ListItem::new(Span::styled( - format!("{}", local_time.format("%Y-%m-%d %H:%M:%S")), + local_time.format(&DT_FORMAT)?, Style::default().fg(text_color), ))); let status = if (t.cancelled || !t.valid) && t.status == TransactionStatus::Coinbase { @@ -232,6 +253,7 @@ impl TransactionsTab { .add_column(Some("Status"), None, column3_items); column_list.render(f, area, &mut completed_list_state); + Ok(()) } fn draw_detailed_transaction(&self, f: &mut Frame, area: Rect, app_state: &AppState) @@ -344,9 +366,10 @@ impl TransactionsTab { }; let status = Span::styled(status_msg, Style::default().fg(Color::White)); let message = Span::styled(tx.message.as_str(), Style::default().fg(Color::White)); - let local_time = DateTime::::from_utc(tx.timestamp, Local::now().offset().to_owned()); + // TODO: Get Local from UTC + let local_time = tx.timestamp; let timestamp = Span::styled( - format!("{}", local_time.format("%Y-%m-%d %H:%M:%S")), + format!("{}", local_time.format(&DT_FORMAT).unwrap()), Style::default().fg(Color::White), ); let excess = Span::styled(tx.excess_signature.as_str(), Style::default().fg(Color::White)); @@ -446,7 +469,9 @@ impl Component for TransactionsTab { let instructions = Paragraph::new(Spans::from(span_vec)).wrap(Wrap { trim: true }); f.render_widget(instructions, areas[1]); - self.draw_transaction_lists(f, areas[2], app_state); + if let Err(err) = self.draw_transaction_lists(f, areas[2], app_state) { + log::error!("Can't draw transactions list: {}", err); + } self.draw_detailed_transaction(f, areas[3], app_state); if let Some(msg) = self.error_message.clone() { diff --git a/applications/tari_console_wallet/src/ui/state/app_state.rs b/applications/tari_console_wallet/src/ui/state/app_state.rs index 466f091fe3..bdf08f8a7c 100644 --- a/applications/tari_console_wallet/src/ui/state/app_state.rs +++ b/applications/tari_console_wallet/src/ui/state/app_state.rs @@ -27,11 +27,11 @@ use std::{ }; use bitflags::bitflags; -use chrono::{DateTime, Local, NaiveDateTime}; use log::*; use qrcode::{render::unicode, QrCode}; use tari_crypto::{ristretto::RistrettoPublicKey, tari_utilities::hex::Hex}; use tari_p2p::auto_update::SoftwareUpdaterHandle; +use time::OffsetDateTime; use tokio::{ sync::{watch, RwLock}, task, @@ -442,7 +442,7 @@ impl AppState { self.completed_tx_filter.toggle(TransactionFilter::ABANDONED_COINBASES); } - pub fn get_notifications(&self) -> &Vec<(DateTime, String)> { + pub fn get_notifications(&self) -> &[(OffsetDateTime, String)] { &self.cached_data.notifications } @@ -847,7 +847,9 @@ impl AppStateInner { } pub fn add_notification(&mut self, notification: String) { - self.data.notifications.push((Local::now(), notification)); + self.data + .notifications + .push((OffsetDateTime::now_local().unwrap(), notification)); self.data.new_notification_count += 1; self.updated = true; } @@ -873,7 +875,7 @@ pub struct CompletedTransactionInfo { pub maturity: u64, pub status: TransactionStatus, pub message: String, - pub timestamp: NaiveDateTime, + pub timestamp: OffsetDateTime, pub cancelled: bool, pub direction: TransactionDirection, pub valid: bool, @@ -911,7 +913,7 @@ impl CompletedTransactionInfo { .unwrap_or(0), status: tx.status, message: tx.message, - timestamp: tx.timestamp, + timestamp: OffsetDateTime::from_unix_timestamp(tx.timestamp.timestamp()).unwrap(), cancelled: tx.cancelled, direction: tx.direction, valid: tx.valid, @@ -938,7 +940,7 @@ struct AppStateData { base_node_previous: Peer, base_node_list: Vec<(String, Peer)>, base_node_peer_custom: Option, - notifications: Vec<(DateTime, String)>, + notifications: Vec<(OffsetDateTime, String)>, new_notification_count: u32, }