Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Welcome route #58

Merged
merged 2 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -381,11 +382,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 @@ -394,7 +409,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 @@ -435,6 +450,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 @@ -475,6 +491,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 @@ -577,6 +659,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 @@ -34,7 +34,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 @@ -63,6 +62,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 @@ -91,6 +99,7 @@ pub enum Message {
SetIsMax(bool),
SendStateReset,
PasswordInputChanged(String),
SeedInputChanged(String),
MintInviteCodeInputChanged(String),
DonateAmountChanged(String),
CopyToClipboard(String),
Expand All @@ -105,6 +114,7 @@ pub enum Message {
GenerateInvoice,
GenerateAddress,
Unlock(String),
Init(String), // TODO add seed option
AddFederation(String),
PeekFederation(String),
Donate,
Expand All @@ -129,6 +139,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 @@ -218,6 +232,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 @@ -264,6 +286,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 @@ -312,6 +335,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 @@ -449,6 +476,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 @@ -626,6 +664,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 @@ -663,6 +725,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,6 +25,9 @@ pub use history::*;
pub mod settings;
pub use settings::*;

pub mod welcome;
pub use welcome::*;

#[derive(Default, PartialEq, Debug, Clone, Copy)]
pub enum MintSubroute {
#[default]
Expand All @@ -35,6 +38,7 @@ pub enum MintSubroute {
#[derive(Default, PartialEq, Debug, Clone, Copy)]
pub enum Route {
#[default]
Welcome,
Unlock,
Home,
Mints(MintSubroute),
Expand Down
Loading