commit f357935cca316514bddf53c69933316c6574a9bc
parent 10d6d740b837427c687c68f95702351184ad78f8
Author: kernelkind <kernelkind@gmail.com>
Date: Wed, 25 Jun 2025 16:49:24 -0400
move (de)serialization of wallets & accounts to own structs
for easy cloning
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
5 files changed, 188 insertions(+), 86 deletions(-)
diff --git a/crates/notedeck/src/account/accounts.rs b/crates/notedeck/src/account/accounts.rs
@@ -3,6 +3,7 @@ use tracing::{debug, error, info};
use crate::account::cache::AccountCache;
use crate::account::mute::AccountMutedData;
use crate::account::relay::{AccountRelayData, RelayDefaults};
+use crate::user_account::UserAccountSerializable;
use crate::{AccountStorage, MuteFun, RelaySpec, SingleUnkIdAction, UserAccount};
use enostr::{ClientMessage, FilledKeypair, Keypair, Pubkey, RelayPool};
use nostrdb::{Ndb, Note, Transaction};
@@ -32,7 +33,8 @@ impl Accounts {
match keystore.get_accounts() {
Ok(accounts) => {
for account in accounts {
- cache.add(account);
+ // TODO(kernelkind): this will get processed in a later commit
+ let _ = add_account_from_storage(&mut cache, account);
}
}
Err(e) => {
@@ -94,7 +96,7 @@ impl Accounts {
};
if let Some(key_store) = &self.key_store {
- if let Err(e) = key_store.write_account(acc.get_acc()) {
+ if let Err(e) = key_store.write_account(&acc.get_acc().into()) {
tracing::error!("Could not add key for {:?}: {e}", kp.pubkey);
}
}
@@ -118,7 +120,7 @@ impl Accounts {
return false;
};
- if let Err(err) = key_store.write_account(cur_acc) {
+ if let Err(err) = key_store.write_account(&cur_acc.into()) {
tracing::error!("Could not add account {:?} to storage: {err}", cur_acc.key);
return false;
}
@@ -450,6 +452,40 @@ impl<'a> AccType<'a> {
}
}
+fn add_account_from_storage(
+ cache: &mut AccountCache,
+ user_account_serializable: UserAccountSerializable,
+) -> SingleUnkIdAction {
+ let Some(acc) = get_acc_from_storage(user_account_serializable) else {
+ return SingleUnkIdAction::NoAction;
+ };
+
+ let pk = acc.key.pubkey;
+ cache.add(acc);
+
+ SingleUnkIdAction::pubkey(pk)
+}
+
+fn get_acc_from_storage(user_account_serializable: UserAccountSerializable) -> Option<UserAccount> {
+ let keypair = user_account_serializable.key;
+
+ let mut wallet = None;
+ if let Some(wallet_s) = user_account_serializable.wallet {
+ let m_wallet: Result<crate::ZapWallet, crate::Error> = wallet_s.into();
+ match m_wallet {
+ Ok(w) => wallet = Some(w),
+ Err(e) => {
+ tracing::error!("Problem creating wallet from disk: {e}");
+ }
+ };
+ }
+
+ Some(UserAccount {
+ key: keypair,
+ wallet,
+ })
+}
+
enum RelayAction {
Add,
Remove,
diff --git a/crates/notedeck/src/storage/account_storage.rs b/crates/notedeck/src/storage/account_storage.rs
@@ -1,4 +1,4 @@
-use crate::{Result, UserAccount};
+use crate::{user_account::UserAccountSerializable, Result};
use enostr::{Keypair, Pubkey, SerializableKeypair};
use tokenator::{TokenParser, TokenSerializable, TokenWriter};
@@ -21,7 +21,7 @@ impl AccountStorage {
}
}
- pub fn write_account(&self, account: &UserAccount) -> Result<()> {
+ pub fn write_account(&self, account: &UserAccountSerializable) -> Result<()> {
let mut writer = TokenWriter::new("\t");
account.serialize_tokens(&mut writer);
write_file(
@@ -31,7 +31,7 @@ impl AccountStorage {
)
}
- pub fn get_accounts(&self) -> Result<Vec<UserAccount>> {
+ pub fn get_accounts(&self) -> Result<Vec<UserAccountSerializable>> {
let keys = self
.accounts_directory
.get_files()?
@@ -79,16 +79,18 @@ impl AccountStorage {
}
}
-fn deserialize_storage(serialized: &str) -> Result<UserAccount> {
+fn deserialize_storage(serialized: &str) -> Result<UserAccountSerializable> {
let data = serialized.split("\t").collect::<Vec<&str>>();
let mut parser = TokenParser::new(&data);
- if let Ok(acc) = UserAccount::parse_from_tokens(&mut parser) {
+ if let Ok(acc) = UserAccountSerializable::parse_from_tokens(&mut parser) {
return Ok(acc);
}
// try old deserialization way
- Ok(UserAccount::new(old_deserialization(serialized)?))
+ Ok(UserAccountSerializable::new(old_deserialization(
+ serialized,
+ )?))
}
fn old_deserialization(serialized: &str) -> Result<Keypair> {
@@ -118,7 +120,7 @@ mod tests {
fn test_basic() {
let kp = enostr::FullKeypair::generate().to_keypair();
let storage = AccountStorage::mock().unwrap();
- let resp = storage.write_account(&UserAccount::new(kp.clone()));
+ let resp = storage.write_account(&UserAccountSerializable::new(kp.clone()));
assert!(resp.is_ok());
assert_num_storage(&storage.get_accounts(), 1);
@@ -127,7 +129,7 @@ mod tests {
assert_num_storage(&storage.get_accounts(), 0);
}
- fn assert_num_storage(keys_response: &Result<Vec<UserAccount>>, n: usize) {
+ fn assert_num_storage(keys_response: &Result<Vec<UserAccountSerializable>>, n: usize) {
match keys_response {
Ok(keys) => {
assert_eq!(keys.len(), n);
@@ -143,7 +145,7 @@ mod tests {
let kp = enostr::FullKeypair::generate().to_keypair();
let storage = AccountStorage::mock().unwrap();
- let _ = storage.write_account(&UserAccount::new(kp.clone()));
+ let _ = storage.write_account(&UserAccountSerializable::new(kp.clone()));
assert_num_storage(&storage.get_accounts(), 1);
let resp = storage.select_key(Some(kp.pubkey));
diff --git a/crates/notedeck/src/user_account.rs b/crates/notedeck/src/user_account.rs
@@ -1,7 +1,7 @@
use enostr::{Keypair, KeypairUnowned};
use tokenator::{ParseError, TokenParser, TokenSerializable};
-use crate::wallet::ZapWallet;
+use crate::wallet::{WalletSerializable, ZapWallet};
pub struct UserAccount {
pub key: Keypair,
@@ -26,12 +26,37 @@ impl UserAccount {
}
}
+pub struct UserAccountSerializable {
+ pub key: Keypair,
+ pub wallet: Option<WalletSerializable>,
+}
+
+impl UserAccountSerializable {
+ pub fn new(key: Keypair) -> Self {
+ Self { key, wallet: None }
+ }
+
+ pub fn with_wallet(mut self, wallet: WalletSerializable) -> Self {
+ self.wallet = Some(wallet);
+ self
+ }
+}
+
+impl From<&UserAccount> for UserAccountSerializable {
+ fn from(value: &UserAccount) -> Self {
+ Self {
+ key: value.key.clone(),
+ wallet: value.wallet.as_ref().map(|z| z.into()),
+ }
+ }
+}
+
enum UserAccountRoute {
Key(Keypair),
- Wallet(ZapWallet),
+ Wallet(WalletSerializable),
}
-impl TokenSerializable for UserAccount {
+impl TokenSerializable for UserAccountSerializable {
fn parse_from_tokens<'a>(
parser: &mut tokenator::TokenParser<'a>,
) -> Result<Self, tokenator::ParseError<'a>> {
@@ -43,7 +68,11 @@ impl TokenSerializable for UserAccount {
parser,
&[
|p| Ok(UserAccountRoute::Key(Keypair::parse_from_tokens(p)?)),
- |p| Ok(UserAccountRoute::Wallet(ZapWallet::parse_from_tokens(p)?)),
+ |p| {
+ Ok(UserAccountRoute::Wallet(
+ WalletSerializable::parse_from_tokens(p)?,
+ ))
+ },
],
);
@@ -63,7 +92,7 @@ impl TokenSerializable for UserAccount {
return Err(ParseError::DecodeFailed);
};
- let mut user_acc = UserAccount::new(key);
+ let mut user_acc = UserAccountSerializable::new(key);
if let Some(wallet) = m_wallet {
user_acc = user_acc.with_wallet(wallet);
@@ -88,17 +117,15 @@ mod tests {
use enostr::FullKeypair;
use tokenator::{TokenParser, TokenSerializable, TokenWriter};
- use crate::Wallet;
-
- use super::UserAccount;
+ use crate::{user_account::UserAccountSerializable, wallet::WalletSerializable};
const URI: &str = "nostr+walletconnect://b889ff5b1513b641e2a139f661a661364979c5beee91842f8f0ef42ab558e9d4?relay=wss%3A%2F%2Frelay.damus.io&secret=71a8c14c1407c113601079c4302dab36460f0ccd0ad506f1f2dc73b5100e4f3c&lud16=nostr%40nostr.com";
#[test]
fn test_user_account_serialize_deserialize() {
let kp = FullKeypair::generate();
- let acc = UserAccount::new(kp.to_keypair())
- .with_wallet(Wallet::new(URI.to_owned()).unwrap().into());
+ let acc = UserAccountSerializable::new(kp.to_keypair())
+ .with_wallet(WalletSerializable::new(URI.to_owned()));
let mut writer = TokenWriter::new("\t");
acc.serialize_tokens(&mut writer);
@@ -107,7 +134,7 @@ mod tests {
let data = &serialized.split("\t").collect::<Vec<&str>>();
let mut parser = TokenParser::new(data);
- let m_new_acc = UserAccount::parse_from_tokens(&mut parser);
+ let m_new_acc = UserAccountSerializable::parse_from_tokens(&mut parser);
assert!(m_new_acc.is_ok());
let new_acc = m_new_acc.unwrap();
@@ -118,6 +145,6 @@ mod tests {
panic!();
};
- assert_eq!(wallet.wallet.uri, URI);
+ assert_eq!(wallet.uri, URI);
}
}
diff --git a/crates/notedeck/src/wallet.rs b/crates/notedeck/src/wallet.rs
@@ -62,6 +62,21 @@ pub struct Wallet {
balance: Option<Promise<Result<u64, nwc::Error>>>,
}
+#[derive(Clone)]
+pub struct WalletSerializable {
+ pub uri: String,
+ pub default_mzap: Option<UserZapMsats>,
+}
+
+impl WalletSerializable {
+ pub fn new(uri: String) -> Self {
+ Self {
+ uri,
+ default_mzap: None,
+ }
+ }
+}
+
impl std::fmt::Debug for Wallet {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Wallet({})", self.uri)
@@ -127,26 +142,6 @@ fn pay_invoice(
promise
}
-impl TokenSerializable for Wallet {
- fn parse_from_tokens<'a>(
- parser: &mut tokenator::TokenParser<'a>,
- ) -> Result<Self, tokenator::ParseError<'a>> {
- parser.parse_token("nwc_uri")?;
-
- let raw_uri = parser.pull_token()?;
-
- let wallet =
- Wallet::new(raw_uri.to_owned()).map_err(|_| tokenator::ParseError::DecodeFailed)?;
-
- Ok(wallet)
- }
-
- fn serialize_tokens(&self, writer: &mut tokenator::TokenWriter) {
- writer.write_token("nwc_uri");
- writer.write_token(&self.uri);
- }
-}
-
pub struct GlobalWallet {
pub wallet: Option<ZapWallet>,
pub ui_state: WalletUIState,
@@ -176,7 +171,8 @@ impl GlobalWallet {
return;
};
- match self.wallet_handler.save(wallet, "\t") {
+ let serializable: WalletSerializable = wallet.into();
+ match self.wallet_handler.save(&serializable, "\t") {
Ok(_) => {}
Err(e) => tracing::error!("Could not save global wallet: {e}"),
}
@@ -184,12 +180,15 @@ impl GlobalWallet {
}
fn construct_global_wallet(wallet_handler: &TokenHandler) -> Option<ZapWallet> {
- let Ok(res) = wallet_handler.load::<ZapWallet>("\t") else {
+ let Ok(res) = wallet_handler.load::<WalletSerializable>("\t") else {
return None;
};
let wallet = match res {
- Ok(wallet) => wallet,
+ Ok(wallet) => {
+ let m_zap_wallet: Result<ZapWallet, crate::Error> = wallet.into();
+ m_zap_wallet.ok()?
+ }
Err(e) => {
tracing::error!("Error parsing wallet: {:?}", e);
return None;
@@ -206,11 +205,49 @@ pub struct ZapWallet {
}
enum ZapWalletRoute {
- Wallet(Wallet),
+ Wallet(String),
DefaultZapMsats(UserZapMsats),
}
-impl TokenSerializable for ZapWallet {
+impl ZapWallet {
+ pub fn new(wallet: Wallet) -> Self {
+ Self {
+ wallet,
+ default_zap: DefaultZapMsats::default(),
+ }
+ }
+
+ pub fn with_default_zap_msats(mut self, msats: u64) -> Self {
+ self.default_zap.set_user_selection(msats);
+ self
+ }
+}
+
+impl From<Wallet> for ZapWallet {
+ fn from(value: Wallet) -> Self {
+ ZapWallet::new(value)
+ }
+}
+
+impl From<&ZapWallet> for WalletSerializable {
+ fn from(value: &ZapWallet) -> Self {
+ Self {
+ uri: value.wallet.uri.to_string(),
+ default_mzap: value.default_zap.try_into_user(),
+ }
+ }
+}
+
+impl From<WalletSerializable> for Result<ZapWallet, crate::Error> {
+ fn from(value: WalletSerializable) -> Result<ZapWallet, crate::Error> {
+ Ok(ZapWallet {
+ wallet: Wallet::new(value.uri)?,
+ default_zap: DefaultZapMsats::from_user(value.default_mzap),
+ })
+ }
+}
+
+impl TokenSerializable for WalletSerializable {
fn parse_from_tokens<'a>(
parser: &mut tokenator::TokenParser<'a>,
) -> Result<Self, tokenator::ParseError<'a>> {
@@ -220,7 +257,12 @@ impl TokenSerializable for ZapWallet {
let res = TokenParser::alt(
parser,
&[
- |p| Ok(ZapWalletRoute::Wallet(Wallet::parse_from_tokens(p)?)),
+ |p| {
+ p.parse_token("nwc_uri")?;
+ let raw_uri = p.pull_token()?;
+
+ Ok(ZapWalletRoute::Wallet(raw_uri.to_string()))
+ },
|p| {
Ok(ZapWalletRoute::DefaultZapMsats(
UserZapMsats::parse_from_tokens(p)?,
@@ -245,49 +287,27 @@ impl TokenSerializable for ZapWallet {
return Err(ParseError::DecodeFailed);
};
- let mut zap_wallet = ZapWallet::new(wallet);
-
- let default_zap = DefaultZapMsats::from_user(m_default_zap);
-
- zap_wallet.default_zap = default_zap;
-
- Ok(zap_wallet)
+ Ok(WalletSerializable {
+ uri: wallet,
+ default_mzap: m_default_zap,
+ })
}
fn serialize_tokens(&self, writer: &mut tokenator::TokenWriter) {
- self.wallet.serialize_tokens(writer);
-
- if let Some(user_zap_msats) = self.default_zap.try_into_user() {
- user_zap_msats.serialize_tokens(writer);
- }
- }
-}
+ writer.write_token("nwc_uri");
+ writer.write_token(&self.uri);
-impl ZapWallet {
- pub fn new(wallet: Wallet) -> Self {
- Self {
- wallet,
- default_zap: DefaultZapMsats::default(),
+ if let Some(msats) = &self.default_mzap {
+ msats.serialize_tokens(writer);
}
}
-
- pub fn with_default_zap_msats(mut self, msats: u64) -> Self {
- self.default_zap.set_user_selection(msats);
- self
- }
-}
-
-impl From<Wallet> for ZapWallet {
- fn from(value: Wallet) -> Self {
- ZapWallet::new(value)
- }
}
#[cfg(test)]
mod tests {
use tokenator::{TokenParser, TokenSerializable, TokenWriter};
- use crate::Wallet;
+ use crate::{wallet::WalletSerializable, Wallet};
use super::ZapWallet;
@@ -301,7 +321,7 @@ mod tests {
#[test]
fn test_wallet_serialize_deserialize() {
- let wallet = Wallet::new(URI.to_owned()).unwrap();
+ let wallet = WalletSerializable::new(URI.to_owned());
let mut writer = TokenWriter::new("\t");
wallet.serialize_tokens(&mut writer);
@@ -309,7 +329,7 @@ mod tests {
let data = &serialized.split("\t").collect::<Vec<&str>>();
let mut parser = TokenParser::new(data);
- let m_new_wallet = Wallet::parse_from_tokens(&mut parser);
+ let m_new_wallet = WalletSerializable::parse_from_tokens(&mut parser);
assert!(m_new_wallet.is_ok());
@@ -325,13 +345,20 @@ mod tests {
ZapWallet::new(Wallet::new(URI.to_owned()).unwrap()).with_default_zap_msats(MSATS);
let mut writer = TokenWriter::new("\t");
- zap_wallet.serialize_tokens(&mut writer);
+
+ let serializable: WalletSerializable = (&zap_wallet).into();
+ serializable.serialize_tokens(&mut writer);
let serialized = writer.str();
let data = &serialized.split("\t").collect::<Vec<&str>>();
let mut parser = TokenParser::new(data);
- let m_new_zap_wallet = ZapWallet::parse_from_tokens(&mut parser);
+ let m_deserialized = WalletSerializable::parse_from_tokens(&mut parser);
+ assert!(m_deserialized.is_ok());
+
+ let deserialized = m_deserialized.unwrap();
+
+ let m_new_zap_wallet: Result<ZapWallet, crate::Error> = deserialized.into();
assert!(m_new_zap_wallet.is_ok());
diff --git a/crates/notedeck/src/zaps/default_zap.rs b/crates/notedeck/src/zaps/default_zap.rs
@@ -11,6 +11,16 @@ pub struct DefaultZapMsats {
}
impl DefaultZapMsats {
+ pub fn from_msats(msats: Option<u64>) -> Self {
+ let mut default = DefaultZapMsats::default();
+
+ if let Some(msats) = msats {
+ default.set_user_selection(msats);
+ default.pending.write_msats(msats);
+ }
+
+ default
+ }
pub fn from_user(value: Option<UserZapMsats>) -> Self {
let mut obj = match value {
Some(user_msats) => {
@@ -50,7 +60,7 @@ impl DefaultZapMsats {
}
}
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct UserZapMsats {
pub msats: u64,
}