From a09eb404e243e086c2aa847b2205ecbc31865849 Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Thu, 9 Jan 2025 16:11:14 +0100 Subject: [PATCH] Add save processes and members to disk --- src/commit.rs | 24 +++++++++--- src/main.rs | 103 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 103 insertions(+), 24 deletions(-) diff --git a/src/commit.rs b/src/commit.rs index aed46dc..84c5ded 100644 --- a/src/commit.rs +++ b/src/commit.rs @@ -18,7 +18,7 @@ use sdk_common::process::{lock_processes, Process, ProcessState, CACHEDPROCESSES use serde_json::json; use serde_json::Value; -use crate::{lock_freezed_utxos, MutexExt, DAEMON, WALLET}; +use crate::{lock_freezed_utxos, MutexExt, DAEMON, STORAGE, WALLET}; pub(crate) fn handle_commit_request(commit_msg: CommitMessage) -> Result { // Attempt to process the initial transaction or reference @@ -96,6 +96,9 @@ fn handle_initial_transaction(tx: Transaction, commit_msg: &CommitMessage) -> Re new_process, ); + // Dump to disk + dump_cached_processes()?; + // Add to frozen UTXOs lock_freezed_utxos()?.insert(root_commitment); @@ -146,10 +149,7 @@ fn handle_member_list(pcd_commitment: &Value, roles: &HashMap Result { - let mut commitments = CACHEDPROCESSES - .get() - .ok_or(Error::msg("CACHEDPROCESSES not initialized"))? - .lock_anyhow()?; + let mut commitments = lock_processes()?; let commitment = commitments .get_mut(&outpoint) .ok_or(Error::msg("Commitment not found"))?; @@ -161,6 +161,20 @@ fn handle_existing_commitment(outpoint: OutPoint, commit_msg: CommitMessage) -> } } +pub fn dump_cached_processes() -> Result<(), anyhow::Error> { + let processes = lock_processes()?.clone(); + + let storage = STORAGE.get().ok_or(Error::msg("STORAGE is not initialized"))?.lock_anyhow()?; + + let processes_file = &storage.processes_file; + + processes_file.save(&processes)?; + + log::debug!("saved processes"); + + Ok(()) +} + // Register a new state when validation tokens are empty fn register_new_state(commitment: &mut Process, commit_msg: CommitMessage) -> Result { let concurrent_states = commitment.get_latest_concurrent_states()?; diff --git a/src/main.rs b/src/main.rs index 8cb9d2e..08080c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,11 +11,12 @@ use std::{ }; use bitcoincore_rpc::json::{self as bitcoin_json}; +use commit::MEMBERLIST; use futures_util::{future, pin_mut, stream::TryStreamExt, FutureExt, StreamExt}; use log::{debug, error, warn}; use message::{broadcast_message, process_message, BroadcastType, MessageCache, MESSAGECACHE}; use scan::{check_transaction_alone, compute_partial_tweak_to_transaction}; -use sdk_common::{network::InitMessage, process::CACHEDPROCESSES, sp_client::{bitcoin::{ +use sdk_common::{network::InitMessage, pcd::Member, process::{Process, CACHEDPROCESSES}, sp_client::{bitcoin::{ consensus::deserialize, hex::{DisplayHex, FromHex}, Amount, Network, Transaction, @@ -31,6 +32,7 @@ use sdk_common::{ }; use sdk_common::sp_client::spclient::{derive_keys_from_seed, SpClient, SpendKey}; +use serde::{Deserialize, Serialize}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio_stream::wrappers::UnboundedReceiverStream; @@ -75,11 +77,11 @@ pub struct PublicLists { pub static LISTS: OnceLock> = OnceLock::new(); #[derive(Debug)] -struct WalletFile { +pub struct StateFile { path: PathBuf, } -impl WalletFile { +impl StateFile { fn new(path: PathBuf) -> Self { Self { path } } @@ -95,46 +97,56 @@ impl WalletFile { return Err(Error::msg("wallet file has no parent dir")); } - // check that parent exists + // Ensure the parent directory exists if !parent.exists() { fs::create_dir_all(parent)?; } - fs::File::create_new(&self.path)?; + // Create a new file + fs::File::create(&self.path)?; Ok(()) } - fn save(&self, new_value: &SpWallet) -> Result<()> { + fn save(&self, data: &T) -> Result<()> { let mut f = fs::File::options() .write(true) .truncate(true) .open(&self.path)?; - let json = serde_json::to_string(new_value)?; + let json = serde_json::to_string(data)?; f.write_all(json.as_bytes())?; Ok(()) } - fn load(&self) -> Result { + fn load Deserialize<'de>>(&self) -> Result { let mut f = fs::File::open(&self.path)?; let mut content = vec![]; f.read_to_end(&mut content)?; - let res: SpWallet = serde_json::from_slice(&content)?; + let res: T = serde_json::from_slice(&content)?; Ok(res) } } +#[derive(Debug)] +pub struct DiskStorage { + // wallet_file: StateFile, + pub processes_file: StateFile, + pub members_file: StateFile, +} + +pub static STORAGE: OnceLock> = OnceLock::new(); + const FAUCET_AMT: Amount = Amount::from_sat(100_000); #[derive(Debug)] struct SilentPaymentWallet { sp_wallet: Mutex, - storage: Mutex, + storage: Mutex, } impl SilentPaymentWallet { @@ -143,7 +155,7 @@ impl SilentPaymentWallet { } pub fn save(&self) -> Result<()> { - let wallet = self.sp_wallet.lock_anyhow()?; + let wallet = self.sp_wallet.lock_anyhow()?.clone(); self.storage.lock_anyhow()?.save(&wallet) } } @@ -329,10 +341,6 @@ async fn main() -> Result<()> { warn!("Running on mainnet, you're on your own"); } - CACHEDPROCESSES - .set(Mutex::new(HashMap::new())) - .expect("CachedProcesses initialization failed"); - MESSAGECACHE .set(MessageCache::new()) .expect("Message Cache initialization failed"); @@ -361,19 +369,34 @@ async fn main() -> Result<()> { app_dir.push(".4nk"); let mut wallet_file = app_dir.clone(); wallet_file.push(&config.wallet_name); + let mut processes_file = app_dir.clone(); + processes_file.push("processes"); + let mut members_file = app_dir.clone(); + members_file.push("members"); - let wallet_file = WalletFile::new(wallet_file); + let wallet_file = StateFile::new(wallet_file); + let processes_file = StateFile::new(processes_file); + let members_file = StateFile::new(members_file); // load an existing sp_wallet, or create a new one let sp_wallet = match wallet_file.load() { + Ok(wallet) => { + // TODO: Verify the wallet is compatible with the current network + wallet + } Err(_) => { + // Create a new wallet file if it doesn't exist or fails to load wallet_file.create()?; + + // Generate a seed and derive keys let mut seed = [0u8; 64]; thread_rng().fill(&mut seed); + let (scan_sk, spend_sk) = derive_keys_from_seed(&seed, config.network) .expect("Couldn't generate a new sp_wallet"); + let new_client = SpClient::new( - config.wallet_name, + config.wallet_name.clone(), scan_sk, SpendKey::Secret(spend_sk), None, @@ -381,16 +404,41 @@ async fn main() -> Result<()> { ) .expect("Failed to create a new SpClient"); + // Initialize a new wallet with the generated client let mut wallet = SpWallet::new(new_client, None, vec![])?; - // set birthday to avoid unnecessary scanning + // Set birthday and update scan information let outputs = wallet.get_mut_outputs(); outputs.set_birthday(current_tip); outputs.update_last_scan(current_tip); + // Save the newly created wallet to disk + wallet_file.save(&wallet)?; + wallet } - Ok(wallet) => wallet, // TODO check network + }; + + let cached_processes: HashMap = match processes_file.load() { + Ok(processes) => { + processes + } + Err(_) => { + debug!("creating process file at {}", processes_file.path.display()); + processes_file.create()?; + + HashMap::new() + } + }; + + let members: HashMap = match members_file.load() { + Ok(members) => members, + Err(_) => { + debug!("creating members file at {}", members_file.path.display()); + members_file.create()?; + + HashMap::new() + } }; let our_sp_address = sp_wallet.get_client().get_receiving_address(); @@ -418,6 +466,23 @@ async fn main() -> Result<()> { WALLET.get().unwrap().save().unwrap(); + CACHEDPROCESSES + .set(Mutex::new(cached_processes)) + .expect("Failed to initialize CACHEDPROCESSES"); + + MEMBERLIST + .set(Mutex::new(members)) + .expect("Failed to initialize MEMBERLIST"); + + let storage = DiskStorage { + processes_file, + members_file, + }; + + STORAGE + .set(Mutex::new(storage)) + .unwrap(); + if last_scan < current_tip { log::info!("Scanning for our outputs"); scan_blocks(current_tip - last_scan, &config.electrum_url)?;