From a6eaff0e9c573318c2150979997005c46a44efe0 Mon Sep 17 00:00:00 2001 From: deevope Date: Mon, 12 Sep 2022 20:57:45 +0200 Subject: [PATCH 1/3] release/version dependencies --- Cargo.lock | 20 ++++++++++---------- crates/core/Cargo.toml | 25 +++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1af7948..64b5dde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2444,7 +2444,7 @@ dependencies = [ [[package]] name = "grin_api" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "bytes 0.5.6", "easy-jsonrpc-mw", @@ -2476,7 +2476,7 @@ dependencies = [ [[package]] name = "grin_chain" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "bit-vec", "bitflags 1.3.2", @@ -2499,7 +2499,7 @@ dependencies = [ [[package]] name = "grin_config" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "dirs 2.0.2", "grin_core", @@ -2515,7 +2515,7 @@ dependencies = [ [[package]] name = "grin_core" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "blake2-rfc", "byteorder", @@ -2541,7 +2541,7 @@ dependencies = [ [[package]] name = "grin_keychain" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "blake2-rfc", "byteorder", @@ -2563,7 +2563,7 @@ dependencies = [ [[package]] name = "grin_p2p" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "bitflags 1.3.2", "bytes 0.5.6", @@ -2585,7 +2585,7 @@ dependencies = [ [[package]] name = "grin_pool" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "blake2-rfc", "chrono", @@ -2618,7 +2618,7 @@ dependencies = [ [[package]] name = "grin_servers" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "chrono", "fs2", @@ -2648,7 +2648,7 @@ dependencies = [ [[package]] name = "grin_store" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "byteorder", "croaring", @@ -2667,7 +2667,7 @@ dependencies = [ [[package]] name = "grin_util" version = "5.2.0-alpha.1" -source = "git+https://github.com/mimblewimble/grin?branch=master#6d25382ff587b52cbbc5b88395ea85f9c6d44362" +source = "git+https://github.com/mimblewimble/grin?branch=master#3119899551d07ed39e1211578e6d7c2d6aa2af10" dependencies = [ "backtrace", "base64 0.12.3", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 81b9968..5ad3b69 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -19,7 +19,9 @@ built = { version = "0.4", features = ["git2"]} [dependencies] -############ Release ################ + + +############ Master ################ ### Node grin_config = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_core = { git = "https://github.com/mimblewimble/grin", branch = "master" } @@ -28,7 +30,7 @@ grin_servers = { git = "https://github.com/mimblewimble/grin", branch = "master" grin_keychain = { git = "https://github.com/mimblewimble/grin", branch = "master" } grin_chain = { git = "https://github.com/mimblewimble/grin", branch = "master" } -### Wallet +#### Wallet grin_wallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } grin_wallet_config = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } @@ -38,6 +40,25 @@ grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branc grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +############ Release ################ +### Node +#grin_config = { git = "https://github.com/mimblewimble/grin", tag = "v5.1.2" } +#grin_core = { git = "https://github.com/mimblewimble/grin", tag = "v5.1.2" } +#grin_util = { git = "https://github.com/mimblewimble/grin", tag = "v5.1.2" } +#grin_servers = { git = "https://github.com/mimblewimble/grin", tag = "v5.1.2" } +#grin_keychain = { git = "https://github.com/mimblewimble/grin", tag = "v5.1.2" } +#grin_chain = { git = "https://github.com/mimblewimble/grin", tag = "v5.1.2" } + +##### Wallet +#grin_wallet = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0" } +#grin_wallet_config = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0" } +#grin_wallet_util = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0" } +#grin_wallet_controller = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0"} +#grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0" } +#grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0"} +#grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", tag = "v5.1.0" } + + ############ Local testing ################ ### Node From dd3e8565146f01573df0d08f2dbea3329372547d Mon Sep 17 00:00:00 2001 From: deevope Date: Sun, 18 Sep 2022 23:54:09 +0200 Subject: [PATCH 2/3] restore seed --- src/gui/element/wallet/setup/wallet_setup.rs | 153 ++++++++++++++----- 1 file changed, 118 insertions(+), 35 deletions(-) diff --git a/src/gui/element/wallet/setup/wallet_setup.rs b/src/gui/element/wallet/setup/wallet_setup.rs index ef931f3..1f76484 100644 --- a/src/gui/element/wallet/setup/wallet_setup.rs +++ b/src/gui/element/wallet/setup/wallet_setup.rs @@ -3,6 +3,7 @@ use crate::log_error; //use iced_native::Widget; use native_dialog::FileDialog; use std::path::PathBuf; +use regex::Regex; use { super::super::super::{DEFAULT_FONT_SIZE, DEFAULT_HEADER_FONT_SIZE, DEFAULT_PADDING}, @@ -14,19 +15,22 @@ use { grin_gui_core::{config::Wallet, fs::PersistentData, wallet::WalletInterface}, iced::{ alignment, button, text_input, Alignment, Button, Checkbox, Column, Command, Container, - Element, Length, Row, Space, Text, TextInput, + Element, Length, Radio, Row, Space, Text, TextInput, }, std::sync::{Arc, RwLock}, }; - +#[derive(Debug)] pub struct StateContainer { pub password_state: PasswordState, pub back_button_state: button::State, pub submit_button_state: button::State, pub restore_from_seed: bool, + pub restore_seed_state: RestoreSeedState, pub show_advanced_options: bool, - pub is_testnet: bool, pub advanced_options_state: AdvancedOptionsState, + pub is_testnet: bool, + pub seed_length: u8, + } impl Default for StateContainer { @@ -35,14 +39,33 @@ impl Default for StateContainer { password_state: Default::default(), back_button_state: Default::default(), submit_button_state: Default::default(), - show_advanced_options: false, - is_testnet: false, restore_from_seed: false, + restore_seed_state: Default::default(), + show_advanced_options: false, advanced_options_state: Default::default(), + is_testnet: false, + seed_length: 16, + + } } } +#[derive(Debug)] +pub struct RestoreSeedState { + seed_name_input_state: text_input::State, + seed_name_value: String, +} + +impl Default for RestoreSeedState { + fn default() -> Self { + Self { + seed_name_input_state: Default::default(), + seed_name_value: Default::default(), + } + } +} +#[derive(Debug)] pub struct AdvancedOptionsState { display_name_input_state: text_input::State, display_name_value: String, @@ -91,10 +114,14 @@ pub enum LocalViewInteraction { ToggleRestoreFromSeed(bool), ToggleAdvancedOptions(bool), ToggleIsTestnet(bool), + SeedLength(u8), DisplayName(String), + // Create Wallet CreateWallet, WalletCreatedOk((String, String, String)), WalletCreateError(Arc>>), + + RestoreWallet, ShowFolderPicker, } @@ -123,6 +150,9 @@ pub fn handle_message<'a>( LocalViewInteraction::ToggleRestoreFromSeed(_) => { state.restore_from_seed = !state.restore_from_seed } + LocalViewInteraction::SeedLength(seed_length) => { + state.seed_length = seed_length + } LocalViewInteraction::ToggleAdvancedOptions(_) => { state.show_advanced_options = !state.show_advanced_options } @@ -162,6 +192,7 @@ pub fn handle_message<'a>( password.clone(), state.advanced_options_state.top_level_directory.clone(), state.advanced_options_state.display_name_value.clone(), + state.seed_length.into() ) }; @@ -176,6 +207,19 @@ pub fn handle_message<'a>( } })); } + LocalViewInteraction::RestoreWallet => { + grin_gui.error.take(); + + log::debug!( + "setup::wallet::LocalViewInteraction::Restore {}", + state.advanced_options_state.display_name_value + ); + grin_gui.wallet_state.setup_state.setup_wallet_restore.password = state.password_state.input_value.clone(); + grin_gui.wallet_state.setup_state.setup_wallet_restore.top_level_directory = state.advanced_options_state.top_level_directory.clone(); + grin_gui.wallet_state.setup_state.setup_wallet_restore.display_name = state.advanced_options_state.display_name_value.clone(); + grin_gui.wallet_state.setup_state.mode = + crate::gui::element::wallet::setup::Mode::WalletInputSeedRestore; + } LocalViewInteraction::WalletCreatedOk((tld, mnemonic, display_name)) => { let mut saved_wallet = Wallet::default(); saved_wallet.tld = Some(PathBuf::from(&tld)); @@ -217,6 +261,10 @@ pub fn data_container<'a>( && !state.password_state.repeat_input_value.is_empty() }; + /*let medium = Regex::new(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})$").unwrap(); + let strong = Regex::new(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{12,})$").unwrap();*/ + + // Title row and back button let back_button_label_container = Container::new(Text::new(localized_string("back")).size(DEFAULT_FONT_SIZE)) @@ -232,7 +280,12 @@ pub fn data_container<'a>( )) .into(); - let title = Text::new(localized_string("setup-grin-wallet-title")) + let title_label = match state.restore_from_seed { + true => localized_string("restore-from-seed"), + false => localized_string("setup-grin-wallet-title"), + }; + + let title = Text::new(title_label) .size(DEFAULT_HEADER_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Center); let title_container = @@ -282,7 +335,7 @@ pub fn data_container<'a>( )) .size(DEFAULT_FONT_SIZE) .padding(6) - .width(Length::Units(200)) + .width(Length::Units(400)) .style(style::AddonsQueryInput(color_palette)) .password(); @@ -295,6 +348,7 @@ pub fn data_container<'a>( let password_entry_status = Text::new(password_entry_value) .size(DEFAULT_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Left); + let mut password_entry_status_container = Container::new(password_entry_status) //.width(Length::Fill) .style(style::NormalErrorBackgroundContainer(color_palette)); @@ -323,26 +377,45 @@ pub fn data_container<'a>( //.width(Length::Fill) .style(style::NormalBackgroundContainer(color_palette)); - let restore_from_seed_column = { - let checkbox = Checkbox::new( - state.restore_from_seed, - localized_string("restore-from-seed"), - |b| { - Interaction::WalletSetupWalletViewInteraction( - LocalViewInteraction::ToggleRestoreFromSeed(b), - ) - }, - ) - .style(style::DefaultCheckbox(color_palette)) + + + ////////////////////////////////////// + /* Radio Button 12 or 24 seed words */ + ////////////////////////////////////// + + let default_lenght = Some(state.seed_length); + let radio_short: Element = Radio::new( 16, "12 words", default_lenght, + |s| { + Interaction::WalletSetupWalletViewInteraction( + LocalViewInteraction::SeedLength(s), + ) + }) + .size(DEFAULT_FONT_SIZE) .text_size(DEFAULT_FONT_SIZE) - .spacing(10); + .into(); - let checkbox: Element = checkbox.into(); + let radio_long: Element = Radio::new(32, "24 words", default_lenght, + |s| { + Interaction::WalletSetupWalletViewInteraction( + LocalViewInteraction::SeedLength(s), + ) + }) + .size(DEFAULT_FONT_SIZE) + .text_size(DEFAULT_FONT_SIZE) + .into(); - let checkbox_container = Container::new(checkbox.map(Message::Interaction)) - .style(style::NormalBackgroundContainer(color_palette)); - Column::new().push(checkbox_container) - }; + let radio_column = Row::new() + .push(radio_short.map(Message::Interaction)) + .push(radio_long.map(Message::Interaction)) + .align_items(Alignment::Center) + .spacing(20); + + let radio_container = Container::new(radio_column).style(style::NormalBackgroundContainer(color_palette)); + + + //////////////////////////////// + /* Start of Advanced Options */ + //////////////////////////////// let show_advanced_options_column = { let checkbox = Checkbox::new( @@ -364,9 +437,6 @@ pub fn data_container<'a>( .style(style::NormalBackgroundContainer(color_palette)); Column::new().push(checkbox_container) }; - - // ** start hideable advanced options - //let display_name_label = let display_name = Text::new(localized_string("display-name")) .size(DEFAULT_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Left); @@ -445,12 +515,22 @@ pub fn data_container<'a>( .spacing(DEFAULT_PADDING) .push(Space::new(Length::Units(0), Length::Units(5))) .push(is_testnet_row) + .push(Space::new(Length::Units(0), Length::Units(5))) + .push(radio_container) + .push(Space::new(Length::Units(0), Length::Units(5))) .align_items(Alignment::Start); + //////////////////////////////// // ** end hideable advanced options + //////////////////////////////// + + let submit_label = match state.restore_from_seed { + true => localized_string("input-seed-btn"), + false => localized_string("setup-grin-create-wallet"), + }; let submit_button_label_container = Container::new( - Text::new(localized_string("setup-grin-create-wallet")).size(DEFAULT_FONT_SIZE), + Text::new(submit_label).size(DEFAULT_FONT_SIZE), ) .center_x() .align_x(alignment::Horizontal::Center); @@ -462,7 +542,12 @@ pub fn data_container<'a>( .style(style::DefaultBoxedButton(color_palette)); if check_password() { submit_button = submit_button.on_press(Interaction::WalletSetupWalletViewInteraction( - LocalViewInteraction::CreateWallet, + + match state.restore_from_seed { + true => LocalViewInteraction::RestoreWallet, + false => LocalViewInteraction::CreateWallet, + } + , )); } @@ -476,12 +561,10 @@ pub fn data_container<'a>( .push(description_container) .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) .push(password_column) - .push(Space::new( - Length::Units(0), - Length::Units(unit_spacing + 10), - )) - .push(restore_from_seed_column) - .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) + .push(Space::new(Length::Units(0),Length::Units(unit_spacing + 10))) + + //.push(restore_from_seed_column) + //.push(Space::new(Length::Units(0), Length::Units(unit_spacing))) .push(show_advanced_options_column) .push(Space::new( Length::Units(0), From 40dcf28a8ceaddfaaafd6ffbfc6e9421114b8e7b Mon Sep 17 00:00:00 2001 From: deevope Date: Sun, 18 Sep 2022 23:56:32 +0200 Subject: [PATCH 3/3] restore seed --- Cargo.lock | 1 + Cargo.toml | 1 + crates/core/src/wallet/mod.rs | 25 +- locale/en.json | 5 +- src/gui/element/wallet/setup/init.rs | 65 ++++- src/gui/element/wallet/setup/mod.rs | 12 + src/gui/element/wallet/setup/wallet_list.rs | 2 +- .../element/wallet/setup/wallet_restore.rs | 255 ++++++++++++++++++ src/gui/element/wallet/setup/wallet_setup.rs | 153 ++++++++--- .../element/wallet/setup/wallet_success.rs | 2 +- src/gui/mod.rs | 1 + src/gui/update.rs | 4 + 12 files changed, 470 insertions(+), 56 deletions(-) create mode 100644 src/gui/element/wallet/setup/wallet_restore.rs diff --git a/Cargo.lock b/Cargo.lock index 64b5dde..352411e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2369,6 +2369,7 @@ dependencies = [ "once_cell", "open", "opener", + "regex", "rfd", "serde", "strfmt", diff --git a/Cargo.toml b/Cargo.toml index b566a49..e35de7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ strfmt = "0.1.6" once_cell = "1.6.0" lazy_static = "1" serde = { version = "1.0", features=['derive'] } +regex = "1.6.0" [target.'cfg(target_os = "linux")'.dependencies] native-dialog = "0.5.5" diff --git a/crates/core/src/wallet/mod.rs b/crates/core/src/wallet/mod.rs index 0d4be8c..32326cb 100644 --- a/crates/core/src/wallet/mod.rs +++ b/crates/core/src/wallet/mod.rs @@ -219,18 +219,37 @@ where Ok(()) } + pub fn validate_mnemonic( + wallet_interface: Arc>>, + mmemonic: String, + ) -> Result<(), GrinWalletInterfaceError> { + + WalletInterface::inst_owner_api(wallet_interface.clone())?; + let w = wallet_interface.read().unwrap(); + + match w.owner_api.as_ref() { + Some(o) => { + let mut w_lock = o.wallet_inst.lock(); + let p = w_lock.lc_provider()?; + let is_valid = p.validate_mnemonic(mmemonic.into()).map_err(|_| GrinWalletInterfaceError::OwnerAPINotInstantiated); + return is_valid + } + None => return Err(GrinWalletInterfaceError::OwnerAPINotInstantiated), + }; + } + pub async fn init( wallet_interface: Arc>>, password: String, top_level_directory:PathBuf, - display_name:String + display_name:String, + list_length: usize ) -> Result<(String, String, String), GrinWalletInterfaceError> { WalletInterface::inst_owner_api(wallet_interface.clone())?; let w = wallet_interface.read().unwrap(); - let args = InitArgs { - list_length: 32, + list_length: list_length, password: "".into(), config: w.config.clone().unwrap().clone().members.unwrap().wallet, recovery_phrase: None, diff --git a/locale/en.json b/locale/en.json index e84d276..12dfa4b 100644 --- a/locale/en.json +++ b/locale/en.json @@ -74,12 +74,13 @@ "remote": "Latest version", "remote-release-channel": "Release channel", "reset-columns": "Reset Columns", - "restore-from-seed": "Restore this wallet from an existing seed phrase (TBD)", + "restore-from-seed": "Restore from seed", + "input-seed-btn": "Input seed word", "retry": "Retry", "scale": "Scale", "search-for-addon": "Search for an addon...", "select-account": "Select an Account", - "select-wallet-toml": "Select an Existing Wallet", + "select-wallet-toml": "Select Wallet File", "settings": "Settings", "setup-grin-autogenerate-wallet": "Create a New Wallet", "setup-grin-wallet-description": "To get started, you can either:", diff --git a/src/gui/element/wallet/setup/init.rs b/src/gui/element/wallet/setup/init.rs index 40c02f7..c31e481 100644 --- a/src/gui/element/wallet/setup/init.rs +++ b/src/gui/element/wallet/setup/init.rs @@ -10,10 +10,11 @@ use { Space, Text, }, }; - +#[derive(Debug)] pub struct StateContainer { pub setup_wallet_defaults_is_selected: bool, create_default_wallet_btn: button::State, + restore_from_seed_btn: button::State, select_wallet_toml_btn: button::State, execute_btn: button::State, } @@ -23,6 +24,7 @@ impl Default for StateContainer { Self { setup_wallet_defaults_is_selected: true, create_default_wallet_btn: Default::default(), + restore_from_seed_btn: Default::default(), select_wallet_toml_btn: Default::default(), execute_btn: Default::default(), } @@ -31,7 +33,8 @@ impl Default for StateContainer { #[derive(Debug, Clone)] pub enum LocalViewInteraction { - WalletSetup, + WalletSetupCreate, + WalletSetupRestore, WalletList, } @@ -41,7 +44,14 @@ pub fn handle_message( ) -> Result> { let state = &mut grin_gui.wallet_state.setup_state; match message { - LocalViewInteraction::WalletSetup => state.mode = super::Mode::CreateWallet, + LocalViewInteraction::WalletSetupCreate => { + state.setup_wallet_state.restore_from_seed = false; + state.mode = super::Mode::CreateWallet + } + LocalViewInteraction::WalletSetupRestore => { + state.setup_wallet_state.restore_from_seed = true; + state.mode = super::Mode::RestoreWallet + } LocalViewInteraction::WalletList => state.mode = super::Mode::ListWallets } Ok(Command::none()) @@ -72,7 +82,7 @@ pub fn data_container<'a>( .horizontal_alignment(alignment::Horizontal::Left); let description_container = Container::new(description).style(style::NormalBackgroundContainer(color_palette)); - + let or_text = Text::new(localized_string("or-caps")) .size(DEFAULT_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Center); @@ -80,8 +90,10 @@ pub fn data_container<'a>( let or_text_container = Container::new(or_text).style(style::NormalBackgroundContainer(color_palette)); + /* Create new wallet */ + let create_default_wallet_button_label_container = Container::new( - Text::new(localized_string("setup-grin-autogenerate-wallet")).size(DEFAULT_FONT_SIZE), + Text::new(localized_string("setup-grin-autogenerate-wallet")).size(18), ) .center_x() .align_x(alignment::Horizontal::Center); @@ -92,10 +104,30 @@ pub fn data_container<'a>( ) .style(style::DefaultBoxedButton(color_palette)) .on_press(Interaction::WalletSetupInitViewInteraction( - LocalViewInteraction::WalletSetup, + LocalViewInteraction::WalletSetupCreate, + )) + .into(); + + /* Restore from seed */ + + let restore_from_seed_button_label_container = Container::new( + Text::new(localized_string("restore-from-seed")).size(DEFAULT_FONT_SIZE), + ) + .center_x() + .align_x(alignment::Horizontal::Center); + + let restore_from_seed_button: Element = Button::new( + &mut state.restore_from_seed_btn, + restore_from_seed_button_label_container, + ) + .style(style::DefaultBoxedButton(color_palette)) + .on_press(Interaction::WalletSetupInitViewInteraction( + LocalViewInteraction::WalletSetupRestore, )) .into(); + /* Select from file */ + let select_wallet_button_label_container = Container::new(Text::new(localized_string("select-wallet-toml")).size(DEFAULT_FONT_SIZE)) .center_x() @@ -134,13 +166,11 @@ pub fn data_container<'a>( Column::new().push(checkbox_container) };*/ - let unit_spacing = 15; + let unit_spacing = 20; - let select_column = Column::new() - .push(create_default_wallet_button.map(Message::Interaction)) - .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) - .push(or_text_container) - .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) + let select_row = Row::new() + .push(restore_from_seed_button.map(Message::Interaction)) + .push(Space::new(Length::Units(unit_spacing), Length::Units(0))) .push(select_wallet_button_container) .align_items(Alignment::Center); @@ -149,11 +179,18 @@ pub fn data_container<'a>( .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) .push(description_container) .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) - .push(select_column) + .push(create_default_wallet_button.map(Message::Interaction)) + .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) + .push(or_text_container) + .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) + .push(select_row) .align_items(Alignment::Center); + + Container::new(colum) .center_y() .center_x() .width(Length::Fill) -} + .height(Length::Fill) +} \ No newline at end of file diff --git a/src/gui/element/wallet/setup/mod.rs b/src/gui/element/wallet/setup/mod.rs index a4e1eb0..d520e95 100644 --- a/src/gui/element/wallet/setup/mod.rs +++ b/src/gui/element/wallet/setup/mod.rs @@ -1,6 +1,7 @@ pub mod init; pub mod wallet_setup; pub mod wallet_success; +pub mod wallet_restore; pub mod wallet_list; use { @@ -12,11 +13,13 @@ use { iced::{Column, Command, Container, Length, Space}, }; +#[derive(Debug)] pub struct StateContainer { pub mode: Mode, pub setup_init_state: init::StateContainer, pub setup_wallet_state: wallet_setup::StateContainer, pub setup_wallet_success_state: wallet_success::StateContainer, + pub setup_wallet_restore: wallet_restore::StateContainer, pub setup_wallet_list_state: wallet_list::StateContainer } @@ -24,6 +27,8 @@ pub struct StateContainer { pub enum Mode { Init, CreateWallet, + RestoreWallet, + WalletInputSeedRestore, ListWallets, WalletCreateSuccess, } @@ -34,6 +39,7 @@ impl Default for StateContainer { mode: Mode::Init, setup_init_state: Default::default(), setup_wallet_state: Default::default(), + setup_wallet_restore: Default::default(), setup_wallet_success_state: Default::default(), setup_wallet_list_state: Default::default() } @@ -60,6 +66,12 @@ pub fn data_container<'a>( Mode::CreateWallet => { wallet_setup::data_container(color_palette, &mut state.setup_wallet_state) } + Mode::RestoreWallet => { + wallet_setup::data_container(color_palette, &mut state.setup_wallet_state) + } + Mode::WalletInputSeedRestore => { + wallet_restore::data_container(color_palette, &mut state.setup_wallet_restore) + } Mode::WalletCreateSuccess => { wallet_success::data_container(color_palette, &mut state.setup_wallet_success_state) } diff --git a/src/gui/element/wallet/setup/wallet_list.rs b/src/gui/element/wallet/setup/wallet_list.rs index 15dbf62..6c25baa 100644 --- a/src/gui/element/wallet/setup/wallet_list.rs +++ b/src/gui/element/wallet/setup/wallet_list.rs @@ -14,7 +14,7 @@ use { }; use grin_gui_widgets::{TableRow, table_row::StyleSheet}; - +#[derive(Debug)] pub struct StateContainer { pub back_button_state: button::State, selected_wallet_index: usize, diff --git a/src/gui/element/wallet/setup/wallet_restore.rs b/src/gui/element/wallet/setup/wallet_restore.rs new file mode 100644 index 0000000..d8b4efd --- /dev/null +++ b/src/gui/element/wallet/setup/wallet_restore.rs @@ -0,0 +1,255 @@ +use std::{path::PathBuf, sync::{Arc, RwLock}}; +use crate::log_error; +use grin_gui_core::{config::Wallet, fs::PersistentData}; +use iced::button::StyleSheet; +use iced_native::Widget; + +use { + super::super::super::{DEFAULT_FONT_SIZE, DEFAULT_HEADER_FONT_SIZE, DEFAULT_PADDING}, + crate::gui::{style, GrinGui, Interaction, Message}, + crate::localization::localized_string, + crate::Result, + anyhow::Context, + grin_gui_core::theme::ColorPalette, + grin_gui_core::{config::Config, wallet::WalletInterface}, + iced::{ + alignment, button, text_input, Alignment, Button, Checkbox, Column, Command, Container, + Element, Length, Row, Space, Text, TextInput, + }, + iced_aw::Card, +}; + +/* +TODO: +- search help in the wordlist +- card text input +*/ + +#[derive(Debug)] +pub struct StateContainer { + pub password: String, + pub top_level_directory: PathBuf, + pub display_name: String, + pub recovery_phrase_state: SeedState, + pub copy_button_state: button::State, + pub next_button_state: button::State, + pub is_valid: bool + // TODO: ZeroingString this + +} + +impl Default for StateContainer { + fn default() -> Self { + Self { + password: Default::default(), + top_level_directory: Default::default(), + display_name: Default::default(), + recovery_phrase_state: Default::default(), + copy_button_state: Default::default(), + next_button_state: Default::default(), + is_valid: false + } + } +} +#[derive(Debug)] +pub struct SeedState { + input_state: text_input::State, + input_value: String, +} + +impl Default for SeedState { + fn default() -> Self { + SeedState { + input_state: Default::default(), + input_value: Default::default(), + } + } +} + +#[derive(Debug, Clone)] +pub enum LocalViewInteraction { + SeedInput(String), + SeedInputEnterPressed, + Submit, + WalletCreatedOk, + WalletCreateError(Arc>>), +} + +pub fn handle_message( + grin_gui: &mut GrinGui, + message: LocalViewInteraction, +) -> Result> { + let state = &mut grin_gui.wallet_state.setup_state.setup_wallet_restore; + match message { + LocalViewInteraction::SeedInput(seed) => { + state.recovery_phrase_state.input_value = seed.clone(); + let words = seed.split_whitespace(); + let vec_words: Vec<&str> = words.collect(); + let size_seed = vec_words.len(); + if size_seed == 12 || size_seed == 24 { + let w = grin_gui.wallet_interface.clone(); + let pass = state.recovery_phrase_state.input_value.clone(); + match WalletInterface::validate_mnemonic(w, pass) { + Ok(e) => { + log::debug!("valid seed {:?}", e); + state.is_valid = true + }, + Err(e) => { + log::debug!("invalid seed {:?}", e); + state.is_valid = false + } + } + } + + } + LocalViewInteraction::SeedInputEnterPressed => { + state.recovery_phrase_state.input_state.unfocus(); + }, + LocalViewInteraction::Submit => { + let password = state.password.clone(); + let w = grin_gui.wallet_interface.clone(); + + + let fut = move || { + WalletInterface::init( + w, + password, + state.top_level_directory.clone(), + state.display_name.clone(), + 32 + ) + }; + } + LocalViewInteraction::WalletCreatedOk => { + grin_gui.wallet_state.setup_state.mode = + crate::gui::element::wallet::setup::Mode::WalletCreateSuccess; + let _ = grin_gui.config.save(); + } + LocalViewInteraction::WalletCreateError(err) => { + grin_gui.error = err.write().unwrap().take(); + if let Some(e) = grin_gui.error.as_ref() { + log_error(e); + } + } + + } + Ok(Command::none()) +} + +pub fn data_container<'a>( + color_palette: ColorPalette, + state: &'a mut StateContainer, +) -> Container<'a, Message> { + // Title row + let title = Text::new(localized_string("setup-grin-wallet-success")) + .size(DEFAULT_HEADER_FONT_SIZE) + .horizontal_alignment(alignment::Horizontal::Left); + + let title_container = + Container::new(title).style(style::BrightBackgroundContainer(color_palette)); + + let title_row = Row::new() + .push(title_container) + .align_items(Alignment::Center) + .spacing(20); + + let description = Text::new(localized_string("setup-grin-wallet-recovery-phrase")) + .size(DEFAULT_FONT_SIZE) + .horizontal_alignment(alignment::Horizontal::Center); + let description_container = + Container::new(description).style(style::NormalBackgroundContainer(color_palette)); + + let recovery_phrase_column = { + let recovery_phrase_input = TextInput::new( + &mut state.recovery_phrase_state.input_state, + &localized_string("password")[..], + &state.recovery_phrase_state.input_value, + |s| { + Interaction::WalletSetupWalletRestoreViewInteraction(LocalViewInteraction::SeedInput(s)) + }, + ) + .on_submit(Interaction::WalletSetupWalletRestoreViewInteraction( + LocalViewInteraction::SeedInputEnterPressed + )) + .size(DEFAULT_FONT_SIZE) + .padding(6) + .width(Length::Units(400)) + .style(style::AddonsQueryInput(color_palette)); + + let recovery_phrase_input: Element = recovery_phrase_input.into(); + + let recovery_phrase_input_col = Column::new() + .push(recovery_phrase_input.map(Message::Interaction)) + .spacing(DEFAULT_PADDING) + .align_items(Alignment::Center); + + /*let check_seed = || { + !state.recovery_phrase_state.input_value.is_empty() && state.is_valid + }; + + let mut seed_entry_value = localized_string(""); + if check_seed() { + seed_entry_value = localized_string("setup-grin-passwords-okay") + } + + let seed_entry_status = Text::new(seed_entry_value) + .size(DEFAULT_FONT_SIZE) + .horizontal_alignment(alignment::Horizontal::Left); + + let seed_entry_status_container = Container::new(seed_entry_status) + //.width(Length::Fill) + .style(style::NormalSuccessBackgroundContainer(color_palette));*/ + + Column::new().push(recovery_phrase_input_col)//.push(seed_entry_status_container) + + }; + + + + let submit_button_label_container = + Container::new(Text::new(localized_string("setup-grin-wallet-done")).size(DEFAULT_FONT_SIZE)) + .center_x() + .align_x(alignment::Horizontal::Center); + + let mut next_button = Button::new( + &mut state.next_button_state, + submit_button_label_container + ) + .style(style::DefaultBoxedButton(color_palette)); + + if state.is_valid { + next_button = next_button.on_press(Interaction::WalletSetupWalletRestoreViewInteraction( + LocalViewInteraction::Submit, + )); + + } + + + let next_button: Element = next_button.into(); + + let unit_spacing = 15; + + let colum = Column::new() + .push(title_row) + .push(Space::new( + Length::Units(0), + Length::Units(unit_spacing + 5), + )) + .push(description_container) + .push(Space::new( + Length::Units(0), + Length::Units(unit_spacing + 5), + )) + .push(recovery_phrase_column) + .push(Space::new( + Length::Units(0), + Length::Units(unit_spacing + 10), + )) + .push(next_button.map(Message::Interaction)) + .align_items(Alignment::Center); + + Container::new(colum) + .center_y() + .center_x() + .width(Length::Fill) +} diff --git a/src/gui/element/wallet/setup/wallet_setup.rs b/src/gui/element/wallet/setup/wallet_setup.rs index ef931f3..1f76484 100644 --- a/src/gui/element/wallet/setup/wallet_setup.rs +++ b/src/gui/element/wallet/setup/wallet_setup.rs @@ -3,6 +3,7 @@ use crate::log_error; //use iced_native::Widget; use native_dialog::FileDialog; use std::path::PathBuf; +use regex::Regex; use { super::super::super::{DEFAULT_FONT_SIZE, DEFAULT_HEADER_FONT_SIZE, DEFAULT_PADDING}, @@ -14,19 +15,22 @@ use { grin_gui_core::{config::Wallet, fs::PersistentData, wallet::WalletInterface}, iced::{ alignment, button, text_input, Alignment, Button, Checkbox, Column, Command, Container, - Element, Length, Row, Space, Text, TextInput, + Element, Length, Radio, Row, Space, Text, TextInput, }, std::sync::{Arc, RwLock}, }; - +#[derive(Debug)] pub struct StateContainer { pub password_state: PasswordState, pub back_button_state: button::State, pub submit_button_state: button::State, pub restore_from_seed: bool, + pub restore_seed_state: RestoreSeedState, pub show_advanced_options: bool, - pub is_testnet: bool, pub advanced_options_state: AdvancedOptionsState, + pub is_testnet: bool, + pub seed_length: u8, + } impl Default for StateContainer { @@ -35,14 +39,33 @@ impl Default for StateContainer { password_state: Default::default(), back_button_state: Default::default(), submit_button_state: Default::default(), - show_advanced_options: false, - is_testnet: false, restore_from_seed: false, + restore_seed_state: Default::default(), + show_advanced_options: false, advanced_options_state: Default::default(), + is_testnet: false, + seed_length: 16, + + } } } +#[derive(Debug)] +pub struct RestoreSeedState { + seed_name_input_state: text_input::State, + seed_name_value: String, +} + +impl Default for RestoreSeedState { + fn default() -> Self { + Self { + seed_name_input_state: Default::default(), + seed_name_value: Default::default(), + } + } +} +#[derive(Debug)] pub struct AdvancedOptionsState { display_name_input_state: text_input::State, display_name_value: String, @@ -91,10 +114,14 @@ pub enum LocalViewInteraction { ToggleRestoreFromSeed(bool), ToggleAdvancedOptions(bool), ToggleIsTestnet(bool), + SeedLength(u8), DisplayName(String), + // Create Wallet CreateWallet, WalletCreatedOk((String, String, String)), WalletCreateError(Arc>>), + + RestoreWallet, ShowFolderPicker, } @@ -123,6 +150,9 @@ pub fn handle_message<'a>( LocalViewInteraction::ToggleRestoreFromSeed(_) => { state.restore_from_seed = !state.restore_from_seed } + LocalViewInteraction::SeedLength(seed_length) => { + state.seed_length = seed_length + } LocalViewInteraction::ToggleAdvancedOptions(_) => { state.show_advanced_options = !state.show_advanced_options } @@ -162,6 +192,7 @@ pub fn handle_message<'a>( password.clone(), state.advanced_options_state.top_level_directory.clone(), state.advanced_options_state.display_name_value.clone(), + state.seed_length.into() ) }; @@ -176,6 +207,19 @@ pub fn handle_message<'a>( } })); } + LocalViewInteraction::RestoreWallet => { + grin_gui.error.take(); + + log::debug!( + "setup::wallet::LocalViewInteraction::Restore {}", + state.advanced_options_state.display_name_value + ); + grin_gui.wallet_state.setup_state.setup_wallet_restore.password = state.password_state.input_value.clone(); + grin_gui.wallet_state.setup_state.setup_wallet_restore.top_level_directory = state.advanced_options_state.top_level_directory.clone(); + grin_gui.wallet_state.setup_state.setup_wallet_restore.display_name = state.advanced_options_state.display_name_value.clone(); + grin_gui.wallet_state.setup_state.mode = + crate::gui::element::wallet::setup::Mode::WalletInputSeedRestore; + } LocalViewInteraction::WalletCreatedOk((tld, mnemonic, display_name)) => { let mut saved_wallet = Wallet::default(); saved_wallet.tld = Some(PathBuf::from(&tld)); @@ -217,6 +261,10 @@ pub fn data_container<'a>( && !state.password_state.repeat_input_value.is_empty() }; + /*let medium = Regex::new(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})$").unwrap(); + let strong = Regex::new(r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#\$%\^&\*])(?=.{12,})$").unwrap();*/ + + // Title row and back button let back_button_label_container = Container::new(Text::new(localized_string("back")).size(DEFAULT_FONT_SIZE)) @@ -232,7 +280,12 @@ pub fn data_container<'a>( )) .into(); - let title = Text::new(localized_string("setup-grin-wallet-title")) + let title_label = match state.restore_from_seed { + true => localized_string("restore-from-seed"), + false => localized_string("setup-grin-wallet-title"), + }; + + let title = Text::new(title_label) .size(DEFAULT_HEADER_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Center); let title_container = @@ -282,7 +335,7 @@ pub fn data_container<'a>( )) .size(DEFAULT_FONT_SIZE) .padding(6) - .width(Length::Units(200)) + .width(Length::Units(400)) .style(style::AddonsQueryInput(color_palette)) .password(); @@ -295,6 +348,7 @@ pub fn data_container<'a>( let password_entry_status = Text::new(password_entry_value) .size(DEFAULT_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Left); + let mut password_entry_status_container = Container::new(password_entry_status) //.width(Length::Fill) .style(style::NormalErrorBackgroundContainer(color_palette)); @@ -323,26 +377,45 @@ pub fn data_container<'a>( //.width(Length::Fill) .style(style::NormalBackgroundContainer(color_palette)); - let restore_from_seed_column = { - let checkbox = Checkbox::new( - state.restore_from_seed, - localized_string("restore-from-seed"), - |b| { - Interaction::WalletSetupWalletViewInteraction( - LocalViewInteraction::ToggleRestoreFromSeed(b), - ) - }, - ) - .style(style::DefaultCheckbox(color_palette)) + + + ////////////////////////////////////// + /* Radio Button 12 or 24 seed words */ + ////////////////////////////////////// + + let default_lenght = Some(state.seed_length); + let radio_short: Element = Radio::new( 16, "12 words", default_lenght, + |s| { + Interaction::WalletSetupWalletViewInteraction( + LocalViewInteraction::SeedLength(s), + ) + }) + .size(DEFAULT_FONT_SIZE) .text_size(DEFAULT_FONT_SIZE) - .spacing(10); + .into(); - let checkbox: Element = checkbox.into(); + let radio_long: Element = Radio::new(32, "24 words", default_lenght, + |s| { + Interaction::WalletSetupWalletViewInteraction( + LocalViewInteraction::SeedLength(s), + ) + }) + .size(DEFAULT_FONT_SIZE) + .text_size(DEFAULT_FONT_SIZE) + .into(); - let checkbox_container = Container::new(checkbox.map(Message::Interaction)) - .style(style::NormalBackgroundContainer(color_palette)); - Column::new().push(checkbox_container) - }; + let radio_column = Row::new() + .push(radio_short.map(Message::Interaction)) + .push(radio_long.map(Message::Interaction)) + .align_items(Alignment::Center) + .spacing(20); + + let radio_container = Container::new(radio_column).style(style::NormalBackgroundContainer(color_palette)); + + + //////////////////////////////// + /* Start of Advanced Options */ + //////////////////////////////// let show_advanced_options_column = { let checkbox = Checkbox::new( @@ -364,9 +437,6 @@ pub fn data_container<'a>( .style(style::NormalBackgroundContainer(color_palette)); Column::new().push(checkbox_container) }; - - // ** start hideable advanced options - //let display_name_label = let display_name = Text::new(localized_string("display-name")) .size(DEFAULT_FONT_SIZE) .horizontal_alignment(alignment::Horizontal::Left); @@ -445,12 +515,22 @@ pub fn data_container<'a>( .spacing(DEFAULT_PADDING) .push(Space::new(Length::Units(0), Length::Units(5))) .push(is_testnet_row) + .push(Space::new(Length::Units(0), Length::Units(5))) + .push(radio_container) + .push(Space::new(Length::Units(0), Length::Units(5))) .align_items(Alignment::Start); + //////////////////////////////// // ** end hideable advanced options + //////////////////////////////// + + let submit_label = match state.restore_from_seed { + true => localized_string("input-seed-btn"), + false => localized_string("setup-grin-create-wallet"), + }; let submit_button_label_container = Container::new( - Text::new(localized_string("setup-grin-create-wallet")).size(DEFAULT_FONT_SIZE), + Text::new(submit_label).size(DEFAULT_FONT_SIZE), ) .center_x() .align_x(alignment::Horizontal::Center); @@ -462,7 +542,12 @@ pub fn data_container<'a>( .style(style::DefaultBoxedButton(color_palette)); if check_password() { submit_button = submit_button.on_press(Interaction::WalletSetupWalletViewInteraction( - LocalViewInteraction::CreateWallet, + + match state.restore_from_seed { + true => LocalViewInteraction::RestoreWallet, + false => LocalViewInteraction::CreateWallet, + } + , )); } @@ -476,12 +561,10 @@ pub fn data_container<'a>( .push(description_container) .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) .push(password_column) - .push(Space::new( - Length::Units(0), - Length::Units(unit_spacing + 10), - )) - .push(restore_from_seed_column) - .push(Space::new(Length::Units(0), Length::Units(unit_spacing))) + .push(Space::new(Length::Units(0),Length::Units(unit_spacing + 10))) + + //.push(restore_from_seed_column) + //.push(Space::new(Length::Units(0), Length::Units(unit_spacing))) .push(show_advanced_options_column) .push(Space::new( Length::Units(0), diff --git a/src/gui/element/wallet/setup/wallet_success.rs b/src/gui/element/wallet/setup/wallet_success.rs index 0bc58bb..494874e 100644 --- a/src/gui/element/wallet/setup/wallet_success.rs +++ b/src/gui/element/wallet/setup/wallet_success.rs @@ -14,7 +14,7 @@ use { }, iced_aw::Card, }; - +#[derive(Debug)] pub struct StateContainer { pub copy_button_state: button::State, pub next_button_state: button::State, diff --git a/src/gui/mod.rs b/src/gui/mod.rs index ff6afcb..7783bfb 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -421,6 +421,7 @@ pub enum Interaction { WalletSetupInitViewInteraction(element::wallet::setup::init::LocalViewInteraction), WalletSetupWalletViewInteraction(element::wallet::setup::wallet_setup::LocalViewInteraction), WalletListWalletViewInteraction(element::wallet::setup::wallet_list::LocalViewInteraction), + WalletSetupWalletRestoreViewInteraction(element::wallet::setup::wallet_restore::LocalViewInteraction), WalletSetupWalletSuccessViewInteraction(element::wallet::setup::wallet_success::LocalViewInteraction), WalletOperationOpenViewInteraction(element::wallet::operation::open::LocalViewInteraction), WalletOperationHomeViewInteraction(element::wallet::operation::home::LocalViewInteraction), diff --git a/src/gui/update.rs b/src/gui/update.rs index a1065c3..6c95211 100644 --- a/src/gui/update.rs +++ b/src/gui/update.rs @@ -142,6 +142,10 @@ pub fn handle_message(grin_gui: &mut GrinGui, message: Message) -> Result { return element::wallet::setup::wallet_success::handle_message(grin_gui, l); } + // Setup -> Wallet Success Settings + Message::Interaction(Interaction::WalletSetupWalletRestoreViewInteraction(l)) => { + return element::wallet::setup::wallet_restore::handle_message(grin_gui, l); + } // Wallet -> Operation -> Open Settings Message::Interaction(Interaction::WalletOperationOpenViewInteraction(l)) => { return element::wallet::operation::open::handle_message(grin_gui, l);