simplify key storage

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-03-15 15:06:40 -04:00
parent 69b651bbc5
commit 1e05fa551d
6 changed files with 67 additions and 146 deletions

View File

@@ -1,8 +1,7 @@
use tracing::{debug, error, info}; use tracing::{debug, error, info};
use crate::{ use crate::{
KeyStorageResponse, KeyStorageType, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds, FileKeyStorage, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount,
UserAccount,
}; };
use enostr::{ClientMessage, FilledKeypair, Keypair, RelayPool}; use enostr::{ClientMessage, FilledKeypair, Keypair, RelayPool};
use nostrdb::{Filter, Ndb, Note, NoteBuilder, NoteKey, Subscription, Transaction}; use nostrdb::{Filter, Ndb, Note, NoteBuilder, NoteKey, Subscription, Transaction};
@@ -309,7 +308,7 @@ pub struct AccountData {
pub struct Accounts { pub struct Accounts {
currently_selected_account: Option<usize>, currently_selected_account: Option<usize>,
accounts: Vec<UserAccount>, accounts: Vec<UserAccount>,
key_store: KeyStorageType, key_store: Option<FileKeyStorage>,
account_data: BTreeMap<[u8; 32], AccountData>, account_data: BTreeMap<[u8; 32], AccountData>,
forced_relays: BTreeSet<RelaySpec>, forced_relays: BTreeSet<RelaySpec>,
bootstrap_relays: BTreeSet<RelaySpec>, bootstrap_relays: BTreeSet<RelaySpec>,
@@ -317,14 +316,24 @@ pub struct Accounts {
} }
impl Accounts { impl Accounts {
pub fn new(key_store: KeyStorageType, forced_relays: Vec<String>) -> Self { pub fn new(key_store: Option<FileKeyStorage>, forced_relays: Vec<String>) -> Self {
let accounts = if let KeyStorageResponse::ReceivedResult(res) = key_store.get_keys() { let accounts = match &key_store {
res.unwrap_or_default() Some(keystore) => match keystore.get_keys() {
} else { Ok(k) => k,
Vec::new() Err(e) => {
tracing::error!("could not get keys: {e}");
Vec::new()
}
},
None => Vec::new(),
};
let currently_selected_account = if let Some(key_store) = &key_store {
get_selected_index(&accounts, key_store)
} else {
None
}; };
let currently_selected_account = get_selected_index(&accounts, &key_store);
let account_data = BTreeMap::new(); let account_data = BTreeMap::new();
let forced_relays: BTreeSet<RelaySpec> = forced_relays let forced_relays: BTreeSet<RelaySpec> = forced_relays
.into_iter() .into_iter()
@@ -367,7 +376,12 @@ impl Accounts {
pub fn remove_account(&mut self, index: usize) { pub fn remove_account(&mut self, index: usize) {
if let Some(account) = self.accounts.get(index) { if let Some(account) = self.accounts.get(index) {
let _ = self.key_store.remove_key(account); if let Some(key_store) = &self.key_store {
if let Err(e) = key_store.remove_key(account) {
tracing::error!("Could not remove account at index {index}: {e}");
}
}
self.accounts.remove(index); self.accounts.remove(index);
if let Some(selected_index) = self.currently_selected_account { if let Some(selected_index) = self.currently_selected_account {
@@ -426,7 +440,12 @@ impl Accounts {
"user provided nsec, but we already have npub {}. Upgrading to nsec", "user provided nsec, but we already have npub {}. Upgrading to nsec",
pubkey pubkey
); );
let _ = self.key_store.add_key(&account);
if let Some(key_store) = &self.key_store {
if let Err(e) = key_store.add_key(&account) {
tracing::error!("Could not add key for {:?}: {e}", account.pubkey);
}
}
self.accounts[contains_acc.index] = account; self.accounts[contains_acc.index] = account;
} else { } else {
@@ -435,7 +454,11 @@ impl Accounts {
contains_acc.index contains_acc.index
} else { } else {
info!("adding new account {}", pubkey); info!("adding new account {}", pubkey);
let _ = self.key_store.add_key(&account); if let Some(key_store) = &self.key_store {
if let Err(e) = key_store.add_key(&account) {
tracing::error!("Could not add key for {:?}: {e}", account.pubkey);
}
}
self.accounts.push(account); self.accounts.push(account);
self.accounts.len() - 1 self.accounts.len() - 1
}; };
@@ -493,13 +516,21 @@ impl Accounts {
pub fn select_account(&mut self, index: usize) { pub fn select_account(&mut self, index: usize) {
if let Some(account) = self.accounts.get(index) { if let Some(account) = self.accounts.get(index) {
self.currently_selected_account = Some(index); self.currently_selected_account = Some(index);
self.key_store.select_key(Some(account.pubkey)); if let Some(key_store) = &self.key_store {
if let Err(e) = key_store.select_key(Some(account.pubkey)) {
tracing::error!("Could not select key {:?}: {e}", account.pubkey);
}
}
} }
} }
pub fn clear_selected_account(&mut self) { pub fn clear_selected_account(&mut self) {
self.currently_selected_account = None; self.currently_selected_account = None;
self.key_store.select_key(None); if let Some(key_store) = &self.key_store {
if let Err(e) = key_store.select_key(None) {
tracing::error!("Could not select None key: {e}");
}
}
} }
pub fn mutefun(&self) -> Box<MuteFun> { pub fn mutefun(&self) -> Box<MuteFun> {
@@ -794,14 +825,13 @@ enum RelayAction {
Remove, Remove,
} }
fn get_selected_index(accounts: &[UserAccount], keystore: &KeyStorageType) -> Option<usize> { fn get_selected_index(accounts: &[UserAccount], keystore: &FileKeyStorage) -> Option<usize> {
match keystore.get_selected_key() { match keystore.get_selected_key() {
KeyStorageResponse::ReceivedResult(Ok(Some(pubkey))) => { Ok(Some(pubkey)) => {
return accounts.iter().position(|account| account.pubkey == pubkey); return accounts.iter().position(|account| account.pubkey == pubkey);
} }
Ok(None) => {}
KeyStorageResponse::ReceivedResult(Err(e)) => error!("Error getting selected key: {}", e), Err(e) => error!("Error getting selected key: {}", e),
KeyStorageResponse::Waiting | KeyStorageResponse::ReceivedResult(Ok(None)) => {}
}; };
None None

View File

@@ -1,7 +1,7 @@
use crate::persist::{AppSizeHandler, ZoomHandler}; use crate::persist::{AppSizeHandler, ZoomHandler};
use crate::{ use crate::{
Accounts, AppContext, Args, DataPath, DataPathType, Directory, FileKeyStorage, Images, Accounts, AppContext, Args, DataPath, DataPathType, Directory, FileKeyStorage, Images,
KeyStorageType, NoteCache, RelayDebugView, ThemeHandler, UnknownIds, NoteCache, RelayDebugView, ThemeHandler, UnknownIds,
}; };
use egui::ThemePreference; use egui::ThemePreference;
use egui_winit::clipboard::Clipboard; use egui_winit::clipboard::Clipboard;
@@ -149,12 +149,12 @@ impl Notedeck {
let keystore = if parsed_args.use_keystore { let keystore = if parsed_args.use_keystore {
let keys_path = path.path(DataPathType::Keys); let keys_path = path.path(DataPathType::Keys);
let selected_key_path = path.path(DataPathType::SelectedKey); let selected_key_path = path.path(DataPathType::SelectedKey);
KeyStorageType::FileSystem(FileKeyStorage::new( Some(FileKeyStorage::new(
Directory::new(keys_path), Directory::new(keys_path),
Directory::new(selected_key_path), Directory::new(selected_key_path),
)) ))
} else { } else {
KeyStorageType::None None
}; };
let mut accounts = Accounts::new(keystore, parsed_args.relays.clone()); let mut accounts = Accounts::new(keystore, parsed_args.relays.clone());

View File

@@ -44,9 +44,7 @@ pub use persist::*;
pub use relay_debug::RelayDebugView; pub use relay_debug::RelayDebugView;
pub use relayspec::RelaySpec; pub use relayspec::RelaySpec;
pub use result::Result; pub use result::Result;
pub use storage::{ pub use storage::{DataPath, DataPathType, Directory, FileKeyStorage};
DataPath, DataPathType, Directory, FileKeyStorage, KeyStorageResponse, KeyStorageType,
};
pub use style::NotedeckTextStyle; pub use style::NotedeckTextStyle;
pub use theme::ColorTheme; pub use theme::ColorTheme;
pub use time::time_ago_since; pub use time::time_ago_since;

View File

@@ -1,10 +1,7 @@
use crate::Result; use crate::Result;
use enostr::{Keypair, Pubkey, SerializableKeypair}; use enostr::{Keypair, Pubkey, SerializableKeypair};
use super::{ use super::file_storage::{delete_file, write_file, Directory};
file_storage::{delete_file, write_file, Directory},
key_storage_impl::KeyStorageResponse,
};
static SELECTED_PUBKEY_FILE_NAME: &str = "selected_pubkey"; static SELECTED_PUBKEY_FILE_NAME: &str = "selected_pubkey";
@@ -23,7 +20,7 @@ impl FileKeyStorage {
} }
} }
fn add_key_internal(&self, key: &Keypair) -> Result<()> { pub fn add_key(&self, key: &Keypair) -> Result<()> {
write_file( write_file(
&self.keys_directory.file_path, &self.keys_directory.file_path,
key.pubkey.hex(), key.pubkey.hex(),
@@ -31,7 +28,7 @@ impl FileKeyStorage {
) )
} }
fn get_keys_internal(&self) -> Result<Vec<Keypair>> { pub fn get_keys(&self) -> Result<Vec<Keypair>> {
let keys = self let keys = self
.keys_directory .keys_directory
.get_files()? .get_files()?
@@ -42,11 +39,11 @@ impl FileKeyStorage {
Ok(keys) Ok(keys)
} }
fn remove_key_internal(&self, key: &Keypair) -> Result<()> { pub fn remove_key(&self, key: &Keypair) -> Result<()> {
delete_file(&self.keys_directory.file_path, key.pubkey.hex()) delete_file(&self.keys_directory.file_path, key.pubkey.hex())
} }
fn get_selected_pubkey(&self) -> Result<Option<Pubkey>> { pub fn get_selected_key(&self) -> Result<Option<Pubkey>> {
match self match self
.selected_key_directory .selected_key_directory
.get_file(SELECTED_PUBKEY_FILE_NAME.to_owned()) .get_file(SELECTED_PUBKEY_FILE_NAME.to_owned())
@@ -57,7 +54,7 @@ impl FileKeyStorage {
} }
} }
fn select_pubkey(&self, pubkey: Option<Pubkey>) -> Result<()> { pub fn select_key(&self, pubkey: Option<Pubkey>) -> Result<()> {
if let Some(pubkey) = pubkey { if let Some(pubkey) = pubkey {
write_file( write_file(
&self.selected_key_directory.file_path, &self.selected_key_directory.file_path,
@@ -80,28 +77,6 @@ impl FileKeyStorage {
} }
} }
impl FileKeyStorage {
pub fn get_keys(&self) -> KeyStorageResponse<Vec<enostr::Keypair>> {
KeyStorageResponse::ReceivedResult(self.get_keys_internal())
}
pub fn add_key(&self, key: &enostr::Keypair) -> KeyStorageResponse<()> {
KeyStorageResponse::ReceivedResult(self.add_key_internal(key))
}
pub fn remove_key(&self, key: &enostr::Keypair) -> KeyStorageResponse<()> {
KeyStorageResponse::ReceivedResult(self.remove_key_internal(key))
}
pub fn get_selected_key(&self) -> KeyStorageResponse<Option<Pubkey>> {
KeyStorageResponse::ReceivedResult(self.get_selected_pubkey())
}
pub fn select_key(&self, key: Option<Pubkey>) -> KeyStorageResponse<()> {
KeyStorageResponse::ReceivedResult(self.select_pubkey(key))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::path::PathBuf; use std::path::PathBuf;
@@ -128,27 +103,21 @@ mod tests {
let storage = FileKeyStorage::mock().unwrap(); let storage = FileKeyStorage::mock().unwrap();
let resp = storage.add_key(&kp); let resp = storage.add_key(&kp);
assert_eq!(resp, KeyStorageResponse::ReceivedResult(Ok(()))); assert!(resp.is_ok());
assert_num_storage(&storage.get_keys(), 1); assert_num_storage(&storage.get_keys(), 1);
assert_eq!( assert!(storage.remove_key(&kp).is_ok());
storage.remove_key(&kp),
KeyStorageResponse::ReceivedResult(Ok(()))
);
assert_num_storage(&storage.get_keys(), 0); assert_num_storage(&storage.get_keys(), 0);
} }
fn assert_num_storage(keys_response: &KeyStorageResponse<Vec<Keypair>>, n: usize) { fn assert_num_storage(keys_response: &Result<Vec<Keypair>>, n: usize) {
match keys_response { match keys_response {
KeyStorageResponse::ReceivedResult(Ok(keys)) => { Ok(keys) => {
assert_eq!(keys.len(), n); assert_eq!(keys.len(), n);
} }
KeyStorageResponse::ReceivedResult(Err(_e)) => { Err(_e) => {
panic!("could not get keys"); panic!("could not get keys");
} }
KeyStorageResponse::Waiting => {
panic!("did not receive result");
}
} }
} }
@@ -160,10 +129,10 @@ mod tests {
let _ = storage.add_key(&kp); let _ = storage.add_key(&kp);
assert_num_storage(&storage.get_keys(), 1); assert_num_storage(&storage.get_keys(), 1);
let resp = storage.select_pubkey(Some(kp.pubkey)); let resp = storage.select_key(Some(kp.pubkey));
assert!(resp.is_ok()); assert!(resp.is_ok());
let resp = storage.get_selected_pubkey(); let resp = storage.get_selected_key();
assert!(resp.is_ok()); assert!(resp.is_ok());
} }
@@ -174,7 +143,7 @@ mod tests {
// Should return Ok(None) when no key has been selected // Should return Ok(None) when no key has been selected
match storage.get_selected_key() { match storage.get_selected_key() {
KeyStorageResponse::ReceivedResult(Ok(None)) => (), // This is what we expect Ok(None) => (), // This is what we expect
other => panic!("Expected Ok(None), got {:?}", other), other => panic!("Expected Ok(None), got {:?}", other),
} }
} }

View File

@@ -1,73 +0,0 @@
use enostr::{Keypair, Pubkey};
use super::file_key_storage::FileKeyStorage;
use crate::Result;
#[derive(Debug, PartialEq)]
pub enum KeyStorageType {
None,
FileSystem(FileKeyStorage),
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum KeyStorageResponse<R> {
Waiting,
ReceivedResult(Result<R>),
}
impl<R: PartialEq> PartialEq for KeyStorageResponse<R> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(KeyStorageResponse::Waiting, KeyStorageResponse::Waiting) => true,
(
KeyStorageResponse::ReceivedResult(Ok(r1)),
KeyStorageResponse::ReceivedResult(Ok(r2)),
) => r1 == r2,
(
KeyStorageResponse::ReceivedResult(Err(_)),
KeyStorageResponse::ReceivedResult(Err(_)),
) => true,
_ => false,
}
}
}
impl KeyStorageType {
pub fn get_keys(&self) -> KeyStorageResponse<Vec<Keypair>> {
match self {
Self::None => KeyStorageResponse::ReceivedResult(Ok(Vec::new())),
Self::FileSystem(f) => f.get_keys(),
}
}
pub fn add_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
let _ = key;
match self {
Self::None => KeyStorageResponse::ReceivedResult(Ok(())),
Self::FileSystem(f) => f.add_key(key),
}
}
pub fn remove_key(&self, key: &Keypair) -> KeyStorageResponse<()> {
let _ = key;
match self {
Self::None => KeyStorageResponse::ReceivedResult(Ok(())),
Self::FileSystem(f) => f.remove_key(key),
}
}
pub fn get_selected_key(&self) -> KeyStorageResponse<Option<Pubkey>> {
match self {
Self::None => KeyStorageResponse::ReceivedResult(Ok(None)),
Self::FileSystem(f) => f.get_selected_key(),
}
}
pub fn select_key(&self, key: Option<Pubkey>) -> KeyStorageResponse<()> {
match self {
Self::None => KeyStorageResponse::ReceivedResult(Ok(())),
Self::FileSystem(f) => f.select_key(key),
}
}
}

View File

@@ -3,6 +3,3 @@ mod file_storage;
pub use file_key_storage::FileKeyStorage; pub use file_key_storage::FileKeyStorage;
pub use file_storage::{delete_file, write_file, DataPath, DataPathType, Directory}; pub use file_storage::{delete_file, write_file, DataPath, DataPathType, Directory};
pub mod key_storage_impl;
pub use key_storage_impl::{KeyStorageResponse, KeyStorageType};