Skip to content

Commit

Permalink
Welcome route
Browse files Browse the repository at this point in the history
  • Loading branch information
TonyGiorgio committed Jun 5, 2024
1 parent 2d8f4a2 commit db22775
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 4 deletions.
20 changes: 20 additions & 0 deletions src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ pub enum UICoreMsg {
GetFederationInfo(InviteCode),
AddFederation(InviteCode),
Unlock(String),
Init {
password: String,
seed: Option<String>,
},
GetSeedWords,
}

Expand Down Expand Up @@ -63,6 +67,11 @@ pub enum CoreUIMsg {
FederationInfo(ClientConfig),
AddFederationSuccess,
FederationListUpdated(Vec<FederationItem>),
NeedsInit,
Initing,
InitSuccess,
InitFailed(String),
Locked,
Unlocking,
UnlockSuccess,
UnlockFailed(String),
Expand Down Expand Up @@ -128,6 +137,17 @@ impl UIHandle {
.await;
}

pub async fn init(&self, id: Uuid, password: String) {
self.msg_send(UICoreMsgPacket {
msg: UICoreMsg::Init {
password,
seed: None,
},
id,
})
.await;
}

pub async fn add_federation(&self, id: Uuid, invite: InviteCode) {
self.msg_send(UICoreMsgPacket {
msg: UICoreMsg::AddFederation(invite),
Expand Down
18 changes: 17 additions & 1 deletion src/conf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ pub fn data_dir(network: Network) -> PathBuf {
}
}

// todo store in encrypted database
pub fn get_mnemonic(db: Arc<dyn DBConnection + Send + Sync>) -> anyhow::Result<Mnemonic> {
match db.get_seed()? {
Some(m) => {
Expand All @@ -41,3 +40,20 @@ pub fn get_mnemonic(db: Arc<dyn DBConnection + Send + Sync>) -> anyhow::Result<M
}
}
}

pub fn generate_mnemonic(
db: Arc<dyn DBConnection + Send + Sync>,
words: Option<String>,
) -> anyhow::Result<Mnemonic> {
let mnemonic_words = words.unwrap_or(Mnemonic::generate_in(Language::English, 12)?.to_string());

let new_profile = NewProfile {
id: uuid::Uuid::new_v4().to_string(),
seed_words: mnemonic_words,
};

let p = db.insert_new_profile(new_profile)?;

info!("creating new seed");
Ok(Mnemonic::from_str(&p.seed_words)?)
}
89 changes: 87 additions & 2 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ use tokio::sync::RwLock;
use tokio::task::spawn_blocking;
use uuid::Uuid;

use crate::components::FederationItem;
use crate::db::check_password;
use crate::fedimint_client::{
spawn_onchain_payment_subscription, spawn_onchain_receive_subscription, FederationInviteOrId,
Expand All @@ -35,6 +34,7 @@ use crate::{
db::DBConnection,
Message,
};
use crate::{components::FederationItem, conf::generate_mnemonic};
use crate::{
db::setup_db,
fedimint_client::{
Expand All @@ -44,6 +44,7 @@ use crate::{
};

const PEG_IN_TIMEOUT_YEAR: Duration = Duration::from_secs(86400 * 365);
const HARBOR_FILE_NAME: &str = "harbor.sqlite";

#[derive(Clone)]
struct HarborCore {
Expand Down Expand Up @@ -377,11 +378,25 @@ pub fn run_core() -> Subscription<Message> {
std::fs::create_dir_all(path.clone()).expect("Could not create datadir");
log::info!("Using datadir: {path:?}");

tokio::time::sleep(Duration::from_secs(1)).await;

// Check if the database file exists already, if so tell UI to unlock
if std::fs::metadata(path.join(HARBOR_FILE_NAME)).is_ok() {
tx.send(Message::core_msg(None, CoreUIMsg::Locked))
.await
.expect("should send");
} else {
tx.send(Message::core_msg(None, CoreUIMsg::NeedsInit))
.await
.expect("should send");
}

loop {
let msg = core_handle.recv().await;

let id = msg.as_ref().map(|m| m.id);

// Watch for either Unlock or Init, ignore everything else until started
match msg.map(|m| m.msg) {
Some(UICoreMsg::Unlock(password)) => {
log::info!("Sending unlock message");
Expand All @@ -390,7 +405,7 @@ pub fn run_core() -> Subscription<Message> {
.expect("should send");

// attempting to unlock
let db_path = path.join("harbor.sqlite");
let db_path = path.join(HARBOR_FILE_NAME);

let db_path = db_path.to_str().unwrap().to_string();

Expand Down Expand Up @@ -431,6 +446,7 @@ pub fn run_core() -> Subscription<Message> {
}
let db = db.expect("no error");

// TODO do not automatically generate words anymore
let mnemonic = get_mnemonic(db.clone()).expect("should get seed");

let stop = Arc::new(AtomicBool::new(false));
Expand Down Expand Up @@ -471,6 +487,72 @@ pub fn run_core() -> Subscription<Message> {

process_core(&mut core_handle, &core).await;
}
Some(UICoreMsg::Init { password, seed }) => {
// TODO refactor and make this about init
log::info!("Sending init message");
tx.send(Message::core_msg(id, CoreUIMsg::Initing))
.await
.expect("should send");

// attempting to unlock
let db_path = path.join(HARBOR_FILE_NAME);
let db =
spawn_blocking(move || setup_db(db_path.to_str().unwrap(), password))
.await
.expect("Could not create join handle");

if let Err(e) = db {
// probably invalid password
error!("error setting password: {e}");

tx.send(Message::core_msg(id, CoreUIMsg::InitFailed(e.to_string())))
.await
.expect("should send");
continue;
}
let db = db.expect("no error");

let mnemonic =
generate_mnemonic(db.clone(), seed).expect("should generate words");
let stop = Arc::new(AtomicBool::new(false));

// check db for fedimints
let mut clients = HashMap::new();
let federation_ids = db
.list_federations()
.expect("should load initial fedimints");
for f in federation_ids {
let client = FedimintClient::new(
db.clone(),
FederationInviteOrId::Id(
FederationId::from_str(&f).expect("should parse federation id"),
),
&mnemonic,
network,
stop.clone(),
)
.await
.expect("Could not create fedimint client");

clients.insert(client.fedimint_client.federation_id(), client);
}

let core = HarborCore {
storage: db.clone(),
tx: tx.clone(),
mnemonic,
network,
clients: Arc::new(RwLock::new(clients)),
stop,
};

tx.send(Message::core_msg(id, CoreUIMsg::InitSuccess))
.await
.expect("should send");

process_core(&mut core_handle, &core).await;
}

_ => {
warn!("Ignoring unrelated message to locked core")
}
Expand Down Expand Up @@ -573,6 +655,9 @@ async fn process_core(core_handle: &mut bridge::CoreHandle, core: &HarborCore) {
UICoreMsg::Unlock(_password) => {
unreachable!("should already be unlocked")
}
UICoreMsg::Init { .. } => {
unreachable!("should already be inited")
}
UICoreMsg::GetSeedWords => {
let seed_words = core.get_seed_words().await;
core.msg(Some(msg.id), CoreUIMsg::SeedWords(seed_words))
Expand Down
65 changes: 64 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub mod routes;
pub fn main() -> iced::Result {
pretty_env_logger::init();
program("Harbor", HarborWallet::update, HarborWallet::view)
// .load(HarborWallet::load)
.font(include_bytes!("../assets/fonts/Inter-Regular.ttf").as_slice())
.font(include_bytes!("../assets/fonts/Inter-Bold.ttf").as_slice())
.theme(HarborWallet::theme)
Expand Down Expand Up @@ -62,6 +61,15 @@ enum ReceiveStatus {
WaitingToReceive,
}

#[derive(Default, Debug, Clone, PartialEq)]
enum WelcomeStatus {
#[default]
Loading,
NeedsInit,
Inited,
Initing,
}

#[derive(Default, Debug, Clone, PartialEq)]
enum UnlockStatus {
#[default]
Expand Down Expand Up @@ -90,6 +98,7 @@ pub enum Message {
SetIsMax(bool),
SendStateReset,
PasswordInputChanged(String),
SeedInputChanged(String),
MintInviteCodeInputChanged(String),
DonateAmountChanged(String),
CopyToClipboard(String),
Expand All @@ -103,6 +112,7 @@ pub enum Message {
GenerateInvoice,
GenerateAddress,
Unlock(String),
Init(String), // TODO add seed option
AddFederation(String),
PeekFederation(String),
Donate,
Expand All @@ -127,6 +137,10 @@ pub struct HarborWallet {
balance_sats: u64,
transaction_history: Vec<TransactionItem>,
federation_list: Vec<FederationItem>,
// Welcome screen
init_status: WelcomeStatus,
seed_input_str: String,
init_failure_reason: Option<String>,
// Lock screen
password_input_str: String,
unlock_status: UnlockStatus,
Expand Down Expand Up @@ -216,6 +230,14 @@ impl HarborWallet {
}
}

async fn async_init(ui_handle: Option<Arc<bridge::UIHandle>>, id: Uuid, password: String) {
if let Some(ui_handle) = ui_handle {
ui_handle.clone().init(id, password).await;
} else {
panic!("UI handle is None");
}
}

async fn async_add_federation(
ui_handle: Option<Arc<bridge::UIHandle>>,
id: Uuid,
Expand Down Expand Up @@ -255,6 +277,7 @@ impl HarborWallet {
self.ui_handle = Some(ui_handle);
println!("Core loaded");

// TODO I don't think this is the best place for it
focus_input_id("password_unlock_input")

// Command::none()
Expand Down Expand Up @@ -292,6 +315,10 @@ impl HarborWallet {
self.password_input_str = input;
Command::none()
}
Message::SeedInputChanged(input) => {
self.seed_input_str = input;
Command::none()
}
Message::MintInviteCodeInputChanged(input) => {
self.mint_invite_code_str = input;
Command::none()
Expand Down Expand Up @@ -423,6 +450,17 @@ impl HarborWallet {
)
}
},
Message::Init(password) => match self.unlock_status {
UnlockStatus::Unlocking => Command::none(),
_ => {
self.unlock_failure_reason = None;
let id = Uuid::new_v4(); // todo use this id somewhere
Command::perform(
Self::async_init(self.ui_handle.clone(), id, password),
|_| Message::Noop,
)
}
},
Message::AddFederation(invite_code) => {
let invite = InviteCode::from_str(&invite_code);
if let Ok(invite) = invite {
Expand Down Expand Up @@ -577,6 +615,30 @@ impl HarborWallet {
self.receive_address = Some(address);
Command::none()
}
CoreUIMsg::Locked => {
info!("Got locked message");
self.active_route = Route::Unlock;
Command::none()
}
CoreUIMsg::NeedsInit => {
info!("Got init message");
self.init_status = WelcomeStatus::NeedsInit;
Command::none()
}
CoreUIMsg::Initing => {
self.init_status = WelcomeStatus::Initing;
Command::none()
}
CoreUIMsg::InitSuccess => {
self.init_status = WelcomeStatus::Inited;
self.active_route = Route::Home;
Command::none()
}
CoreUIMsg::InitFailed(reason) => {
self.init_status = WelcomeStatus::NeedsInit;
self.init_failure_reason = Some(reason);
Command::none()
}
CoreUIMsg::Unlocking => {
info!("Got unlocking message");
self.unlock_status = UnlockStatus::Unlocking;
Expand Down Expand Up @@ -614,6 +676,7 @@ impl HarborWallet {
Route::History => row![sidebar, crate::routes::history(self)].into(),
Route::Transfer => row![sidebar, crate::routes::transfer(self)].into(),
Route::Settings => row![sidebar, crate::routes::settings(self)].into(),
Route::Welcome => crate::routes::welcome(self),
};

ToastManager::new(active_route, &self.toasts, Message::CloseToast).into()
Expand Down
4 changes: 4 additions & 0 deletions src/routes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@ pub use history::*;
pub mod settings;
pub use settings::*;

pub mod welcome;
pub use welcome::*;

#[derive(Default, PartialEq, Debug, Clone, Copy)]
pub enum Route {
#[default]
Welcome,
Unlock,
Home,
Mints,
Expand Down
Loading

0 comments on commit db22775

Please sign in to comment.