From 000d939a2c8fcb1d0fa229acb2ca3c21dfa037e3 Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Wed, 3 Aug 2022 12:36:25 +0100 Subject: [PATCH 1/5] clear pending coinbase transactions now rely on utxo hashes --- base_layer/core/src/transactions/coinbase_builder.rs | 2 +- .../wallet/src/output_manager_service/service.rs | 4 ++-- .../output_manager_service/storage/database/backend.rs | 4 ++-- .../src/output_manager_service/storage/database/mod.rs | 6 +++--- .../output_manager_service/storage/sqlite_db/mod.rs | 8 ++++---- .../storage/sqlite_db/output_sql.rs | 10 ++++++++++ 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index 6363831374..ecdce58e26 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -203,7 +203,7 @@ impl CoinbaseBuilder { let sig = Signature::sign(spending_key.clone(), nonce, &challenge) .map_err(|_| CoinbaseBuildError::BuildError("Challenge could not be represented as a scalar".into()))?; - let sender_offset_private_key = PrivateKey::random(&mut OsRng); + let sender_offset_private_key = PrivateKey::from_bytes(Blake256::digest(spending_key)); // H(spending_key) <- Blake256 let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); let covenant = self.covenant; diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index cc16d39468..c50c7da0aa 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1012,12 +1012,12 @@ where match self .resources .db - .clear_pending_coinbase_transaction_at_block_height(block_height) + .clear_pending_coinbase_transaction_with_hash(output.hash.as_slice()) { Ok(_) => { debug!( target: LOG_TARGET, - "An existing pending coinbase was cleared for block height {}", block_height + "An existing pending coinbase was cleared with hash {}", output.hash.to_hex() ) }, Err(e) => match e { diff --git a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs index 4527e1b561..e3a7cad923 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs @@ -97,9 +97,9 @@ pub trait OutputManagerBackend: Send + Sync + Clone { /// Get the output that was most recently spent, ordered descending by mined height fn get_last_spent_output(&self) -> Result, OutputManagerStorageError>; /// Check if there is a pending coinbase transaction at this block height, if there is clear it. - fn clear_pending_coinbase_transaction_at_block_height( + fn clear_pending_coinbase_transaction_with_hash( &self, - block_height: u64, + hash: &[u8], ) -> Result<(), OutputManagerStorageError>; /// Set if a coinbase output is abandoned or not fn set_coinbase_abandoned(&self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerStorageError>; diff --git a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs index 91c15e1bbb..d5af1bcbae 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs @@ -220,11 +220,11 @@ where T: OutputManagerBackend + 'static } /// Check if there is a pending coinbase transaction at this block height, if there is clear it. - pub fn clear_pending_coinbase_transaction_at_block_height( + pub fn clear_pending_coinbase_transaction_with_hash( &self, - block_height: u64, + hash: &[u8], ) -> Result<(), OutputManagerStorageError> { - self.db.clear_pending_coinbase_transaction_at_block_height(block_height) + self.db.clear_pending_coinbase_transaction_with_hash(hash) } pub fn fetch_all_unspent_outputs(&self) -> Result, OutputManagerStorageError> { diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 09fdafe15d..f2d0a21e03 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -1074,21 +1074,21 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { Ok(()) } - fn clear_pending_coinbase_transaction_at_block_height( + fn clear_pending_coinbase_transaction_with_hash( &self, - block_height: u64, + hash: &[u8], ) -> Result<(), OutputManagerStorageError> { let start = Instant::now(); let conn = self.database_connection.get_pooled_connection()?; let acquire_lock = start.elapsed(); - let output = OutputSql::find_pending_coinbase_at_block_height(block_height, &conn)?; + let output = OutputSql::find_pending_coinbase_with_hash(hash, &conn)?; output.delete(&conn)?; if start.elapsed().as_millis() > 0 { trace!( target: LOG_TARGET, - "sqlite profile - clear_pending_coinbase_transaction_at_block_height: lock {} + db_op {} = {} ms", + "sqlite profile - clear_pending_coinbase_transaction_with_hash: lock {} + db_op {} = {} ms", acquire_lock.as_millis(), (start.elapsed() - acquire_lock).as_millis(), start.elapsed().as_millis() diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index 63480ee86c..804681ab59 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -576,6 +576,16 @@ impl OutputSql { .first::(conn)?) } + pub fn find_pending_coinbase_with_hash( + hash: &[u8], + conn: &SqliteConnection, + ) -> Result { + Ok(outputs::table + .filter(outputs::status.ne(OutputStatus::Unspent as i32)) + .filter(outputs::hash.eq(Some(hash))) + .first::(conn)?) + } + /// Find a particular Output, if it exists and is in the specified Spent state pub fn find_pending_coinbase_at_block_height( block_height: u64, From aa225a474a69c0cf0ad02f203c1de853063742b2 Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Mon, 8 Aug 2022 12:15:51 +0100 Subject: [PATCH 2/5] sync with dev --- base_layer/core/src/transactions/coinbase_builder.rs | 2 +- .../wallet/src/output_manager_service/service.rs | 4 ++-- .../output_manager_service/storage/database/backend.rs | 4 ++-- .../src/output_manager_service/storage/database/mod.rs | 6 +++--- .../output_manager_service/storage/sqlite_db/mod.rs | 8 ++++---- .../storage/sqlite_db/output_sql.rs | 10 ---------- 6 files changed, 12 insertions(+), 22 deletions(-) diff --git a/base_layer/core/src/transactions/coinbase_builder.rs b/base_layer/core/src/transactions/coinbase_builder.rs index 5b984352cf..0b55b78e6d 100644 --- a/base_layer/core/src/transactions/coinbase_builder.rs +++ b/base_layer/core/src/transactions/coinbase_builder.rs @@ -205,7 +205,7 @@ impl CoinbaseBuilder { let sig = Signature::sign(spending_key.clone(), nonce, &challenge) .map_err(|_| CoinbaseBuildError::BuildError("Challenge could not be represented as a scalar".into()))?; - let sender_offset_private_key = PrivateKey::from_bytes(Blake256::digest(spending_key)); // H(spending_key) <- Blake256 + let sender_offset_private_key = PrivateKey::random(&mut OsRng); let sender_offset_public_key = PublicKey::from_secret_key(&sender_offset_private_key); let covenant = self.covenant; diff --git a/base_layer/wallet/src/output_manager_service/service.rs b/base_layer/wallet/src/output_manager_service/service.rs index 9e8395c57a..97bdfd64a3 100644 --- a/base_layer/wallet/src/output_manager_service/service.rs +++ b/base_layer/wallet/src/output_manager_service/service.rs @@ -1014,12 +1014,12 @@ where match self .resources .db - .clear_pending_coinbase_transaction_with_hash(output.hash.as_slice()) + .clear_pending_coinbase_transaction_at_block_height(block_height) { Ok(_) => { debug!( target: LOG_TARGET, - "An existing pending coinbase was cleared with hash {}", output.hash.to_hex() + "An existing pending coinbase was cleared for block height {}", block_height ) }, Err(e) => match e { diff --git a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs index e3a7cad923..4527e1b561 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/backend.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/backend.rs @@ -97,9 +97,9 @@ pub trait OutputManagerBackend: Send + Sync + Clone { /// Get the output that was most recently spent, ordered descending by mined height fn get_last_spent_output(&self) -> Result, OutputManagerStorageError>; /// Check if there is a pending coinbase transaction at this block height, if there is clear it. - fn clear_pending_coinbase_transaction_with_hash( + fn clear_pending_coinbase_transaction_at_block_height( &self, - hash: &[u8], + block_height: u64, ) -> Result<(), OutputManagerStorageError>; /// Set if a coinbase output is abandoned or not fn set_coinbase_abandoned(&self, tx_id: TxId, abandoned: bool) -> Result<(), OutputManagerStorageError>; diff --git a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs index d5af1bcbae..91c15e1bbb 100644 --- a/base_layer/wallet/src/output_manager_service/storage/database/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/database/mod.rs @@ -220,11 +220,11 @@ where T: OutputManagerBackend + 'static } /// Check if there is a pending coinbase transaction at this block height, if there is clear it. - pub fn clear_pending_coinbase_transaction_with_hash( + pub fn clear_pending_coinbase_transaction_at_block_height( &self, - hash: &[u8], + block_height: u64, ) -> Result<(), OutputManagerStorageError> { - self.db.clear_pending_coinbase_transaction_with_hash(hash) + self.db.clear_pending_coinbase_transaction_at_block_height(block_height) } pub fn fetch_all_unspent_outputs(&self) -> Result, OutputManagerStorageError> { diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs index 9fab53d2d1..73af04cd9c 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/mod.rs @@ -1074,21 +1074,21 @@ impl OutputManagerBackend for OutputManagerSqliteDatabase { Ok(()) } - fn clear_pending_coinbase_transaction_with_hash( + fn clear_pending_coinbase_transaction_at_block_height( &self, - hash: &[u8], + block_height: u64, ) -> Result<(), OutputManagerStorageError> { let start = Instant::now(); let conn = self.database_connection.get_pooled_connection()?; let acquire_lock = start.elapsed(); - let output = OutputSql::find_pending_coinbase_with_hash(hash, &conn)?; + let output = OutputSql::find_pending_coinbase_at_block_height(block_height, &conn)?; output.delete(&conn)?; if start.elapsed().as_millis() > 0 { trace!( target: LOG_TARGET, - "sqlite profile - clear_pending_coinbase_transaction_with_hash: lock {} + db_op {} = {} ms", + "sqlite profile - clear_pending_coinbase_transaction_at_block_height: lock {} + db_op {} = {} ms", acquire_lock.as_millis(), (start.elapsed() - acquire_lock).as_millis(), start.elapsed().as_millis() diff --git a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs index cfedab1288..8e6cbdd476 100644 --- a/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs +++ b/base_layer/wallet/src/output_manager_service/storage/sqlite_db/output_sql.rs @@ -576,16 +576,6 @@ impl OutputSql { .first::(conn)?) } - pub fn find_pending_coinbase_with_hash( - hash: &[u8], - conn: &SqliteConnection, - ) -> Result { - Ok(outputs::table - .filter(outputs::status.ne(OutputStatus::Unspent as i32)) - .filter(outputs::hash.eq(Some(hash))) - .first::(conn)?) - } - /// Find a particular Output, if it exists and is in the specified Spent state pub fn find_pending_coinbase_at_block_height( block_height: u64, From 6be10ccb47e39ca87d1261bf8ba3a17ca246f268 Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Mon, 29 Aug 2022 08:50:46 +0100 Subject: [PATCH 3/5] remove assets and tokens tab from console wallet --- applications/tari_console_wallet/src/ui/app.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/applications/tari_console_wallet/src/ui/app.rs b/applications/tari_console_wallet/src/ui/app.rs index 1baccfa568..422482aec1 100644 --- a/applications/tari_console_wallet/src/ui/app.rs +++ b/applications/tari_console_wallet/src/ui/app.rs @@ -94,8 +94,6 @@ impl App { .add("Receive".into(), Box::new(ReceiveTab::new())) .add("Contacts".into(), Box::new(ContactsTab::new())) .add("Network".into(), Box::new(NetworkTab::new(base_node_selected))) - .add("Assets".into(), Box::new(AssetsTab::new())) - .add("Tokens".into(), Box::new(TokensComponent::new())) .add("Events".into(), Box::new(EventsComponent::new())) .add("Log".into(), Box::new(LogTab::new())) .add("Notifications".into(), Box::new(NotificationTab::new())); From a1d7ab4b89cfd9a3796a53619df52598c7afccf3 Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Mon, 29 Aug 2022 08:56:54 +0100 Subject: [PATCH 4/5] delete unused files applications/ tari_console_wallet/src/ui/assets_tab.rs and applications/tari_console_wallet/src/ui/tokens_components.rs --- .../tari_console_wallet/src/ui/app.rs | 2 - .../src/ui/components/assets_tab.rs | 77 ------------------- .../src/ui/components/tokens_component.rs | 68 ---------------- 3 files changed, 147 deletions(-) delete mode 100644 applications/tari_console_wallet/src/ui/components/assets_tab.rs delete mode 100644 applications/tari_console_wallet/src/ui/components/tokens_component.rs diff --git a/applications/tari_console_wallet/src/ui/app.rs b/applications/tari_console_wallet/src/ui/app.rs index 422482aec1..833fd14764 100644 --- a/applications/tari_console_wallet/src/ui/app.rs +++ b/applications/tari_console_wallet/src/ui/app.rs @@ -34,7 +34,6 @@ use crate::{ notifier::Notifier, ui::{ components::{ - assets_tab::AssetsTab, base_node::BaseNode, contacts_tab::ContactsTab, events_component::EventsComponent, @@ -45,7 +44,6 @@ use crate::{ receive_tab::ReceiveTab, send_tab::SendTab, tabs_container::TabsContainer, - tokens_component::TokensComponent, transactions_tab::TransactionsTab, Component, }, diff --git a/applications/tari_console_wallet/src/ui/components/assets_tab.rs b/applications/tari_console_wallet/src/ui/components/assets_tab.rs deleted file mode 100644 index 688b7015b9..0000000000 --- a/applications/tari_console_wallet/src/ui/components/assets_tab.rs +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// 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 tui::{ - backend::Backend, - layout::{Constraint, Layout, Rect}, - style::{Modifier, Style}, - text::{Span, Spans}, - widgets::{Block, Borders, Paragraph, Row, Table, TableState, Wrap}, - Frame, -}; - -use crate::ui::{ - components::{styles, Component}, - state::AppState, -}; - -pub struct AssetsTab { - table_state: TableState, -} - -impl AssetsTab { - pub fn new() -> Self { - Self { - table_state: TableState::default(), - } - } -} - -impl Component for AssetsTab { - fn draw(&mut self, f: &mut Frame, area: Rect, _app_state: &AppState) { - let list_areas = Layout::default() - .constraints([Constraint::Length(1), Constraint::Min(42)].as_ref()) - .split(area); - - let instructions = Paragraph::new(Spans::from(vec![ - Span::raw("Use "), - Span::styled("Up↑/Down↓ Keys", Style::default().add_modifier(Modifier::BOLD)), - Span::raw(" to select a contract."), - ])) - .wrap(Wrap { trim: true }); - - f.render_widget(instructions, list_areas[0]); - - let rows: Vec<_> = Vec::new(); - let table = Table::new(rows) - .header(Row::new(vec!["Name", "Status", "Pub Key", "Owner"]).style(styles::header_row())) - .block(Block::default().title("Assets").borders(Borders::ALL)) - .widths(&[Constraint::Length(30 + 20 + 64 + 64)]) - .highlight_style(styles::highlight()) - .highlight_symbol(">>"); - f.render_stateful_widget(table, list_areas[1], &mut self.table_state); - } - - fn on_up(&mut self, _app_state: &mut AppState) {} - - fn on_down(&mut self, _app_state: &mut AppState) {} -} diff --git a/applications/tari_console_wallet/src/ui/components/tokens_component.rs b/applications/tari_console_wallet/src/ui/components/tokens_component.rs deleted file mode 100644 index 33a0175229..0000000000 --- a/applications/tari_console_wallet/src/ui/components/tokens_component.rs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2021. The Tari Project -// -// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -// following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -// disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the -// following disclaimer in the documentation and/or other materials provided with the distribution. -// -// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote -// products derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, -// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// 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 tui::{ - backend::Backend, - layout::{Constraint, Rect}, - widgets::{Block, Borders, Row, Table, TableState}, - Frame, -}; - -use crate::ui::{ - components::{styles, Component}, - state::AppState, -}; - -pub struct TokensComponent { - table_state: TableState, -} - -impl TokensComponent { - pub fn new() -> Self { - Self { - table_state: TableState::default(), - } - } -} - -impl Component for TokensComponent { - fn draw(&mut self, f: &mut Frame, area: Rect, _app_state: &AppState) { - let rows: Vec<_> = Vec::new(); - let table = Table::new(rows) - .header(Row::new(vec!["Name", "Status", "Asset Pub Key", "Unique ID", "Owner"]).style(styles::header_row())) - .block(Block::default().title("Tokens").borders(Borders::ALL)) - .widths(&[ - Constraint::Length(30), - Constraint::Length(20), - Constraint::Length(32), - Constraint::Length(32), - Constraint::Length(64), - ]) - .highlight_style(styles::highlight()) - .highlight_symbol(">>"); - f.render_stateful_widget(table, area, &mut self.table_state) - } - - fn on_up(&mut self, _app_state: &mut AppState) {} - - fn on_down(&mut self, _app_state: &mut AppState) {} -} From 09685f080a30813fca5434b2ffa90d4bfc6d60f6 Mon Sep 17 00:00:00 2001 From: jorgeantonio21 Date: Mon, 29 Aug 2022 09:50:21 +0100 Subject: [PATCH 5/5] remove assets and tokens mods --- applications/tari_console_wallet/src/ui/components/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/applications/tari_console_wallet/src/ui/components/mod.rs b/applications/tari_console_wallet/src/ui/components/mod.rs index 43d129edcd..7d4a48925d 100644 --- a/applications/tari_console_wallet/src/ui/components/mod.rs +++ b/applications/tari_console_wallet/src/ui/components/mod.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. -pub mod assets_tab; pub mod balance; pub mod base_node; mod component; @@ -32,7 +31,6 @@ pub mod receive_tab; pub mod send_tab; mod styles; pub mod tabs_container; -pub mod tokens_component; pub mod transactions_tab; pub use self::component::*; pub mod contacts_tab;