From 4380a4ec574a207be8c94688d8cfe6dbeb06ef44 Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Tue, 21 May 2024 15:04:16 +0530 Subject: [PATCH] feat(auditor): cache beta participants to the disk --- sn_auditor/src/dag_db.rs | 4 ++- sn_auditor/src/main.rs | 78 ++++++++++++++++++++++++++++------------ sn_auditor/src/routes.rs | 46 ++++++++++++++++++++---- 3 files changed, 98 insertions(+), 30 deletions(-) diff --git a/sn_auditor/src/dag_db.rs b/sn_auditor/src/dag_db.rs index f1033ce92f..4cd0334ebb 100644 --- a/sn_auditor/src/dag_db.rs +++ b/sn_auditor/src/dag_db.rs @@ -28,13 +28,15 @@ use std::{ pub const SPEND_DAG_FILENAME: &str = "spend_dag"; #[cfg(feature = "svg-dag")] pub const SPEND_DAG_SVG_FILENAME: &str = "spend_dag.svg"; +/// Store a locally copy to restore on restart +pub const BETA_PARTICIPANTS_FILENAME: &str = "beta_participants.txt"; /// Abstraction for the Spend DAG database /// Currently in memory, with disk backup, but should probably be a real DB at scale #[derive(Clone)] pub struct SpendDagDb { client: Option, - path: PathBuf, + pub(crate) path: PathBuf, dag: Arc>, forwarded_payments: Arc>, pub(crate) beta_participants: Arc>>, diff --git a/sn_auditor/src/main.rs b/sn_auditor/src/main.rs index 946492dcbb..f8c7da2cbd 100644 --- a/sn_auditor/src/main.rs +++ b/sn_auditor/src/main.rs @@ -12,11 +12,10 @@ extern crate tracing; mod dag_db; mod routes; -use dag_db::SpendDagDb; - use bls::SecretKey; use clap::Parser; use color_eyre::eyre::{eyre, Result}; +use dag_db::SpendDagDb; use sn_client::Client; use sn_logging::{Level, LogBuilder, LogFormat, LogOutputDest}; use sn_peers_acquisition::get_peers_from_args; @@ -83,23 +82,7 @@ async fn main() -> Result<()> { let log_builder = logging_init(opt.log_output_dest, opt.log_format)?; let _log_handles = log_builder.initialize()?; - let beta_participants = if let Some(participants_file) = opt.beta_participants { - let raw_data = std::fs::read_to_string(&participants_file)?; - // instead of serde_json, just use a line separated file - let discord_names = raw_data - .lines() - .map(|line| line.trim().to_string()) - .collect::>(); - - println!( - "Tracking beta rewards for the {} discord usernames provided in {:?}", - discord_names.len(), - participants_file - ); - discord_names - } else { - Vec::new() - }; + let beta_participants = load_and_update_beta_participants(opt.beta_participants)?; let maybe_sk = if let Some(sk_str) = opt.beta_encryption_key { match SecretKey::from_hex(&sk_str) { @@ -208,10 +191,7 @@ async fn initialize_background_spend_dag_collection( foundation_sk: Option, ) -> Result { println!("Initialize spend dag..."); - let path = dirs_next::data_dir() - .ok_or(eyre!("Could not obtain data directory path"))? - .join("safe") - .join("auditor"); + let path = get_auditor_data_dir_path()?; // clean the local spend DAG if requested if clean { @@ -318,3 +298,55 @@ async fn start_server(dag: SpendDagDb) -> Result<()> { } Ok(()) } + +// get the data dir path for auditor +fn get_auditor_data_dir_path() -> Result { + let path = dirs_next::data_dir() + .ok_or(eyre!("Could not obtain data directory path"))? + .join("safe") + .join("auditor"); + + Ok(path) +} + +fn load_and_update_beta_participants( + provided_participants_file: Option, +) -> Result> { + let mut beta_participants = if let Some(participants_file) = provided_participants_file { + let raw_data = std::fs::read_to_string(&participants_file)?; + // instead of serde_json, just use a line separated file + let discord_names = raw_data + .lines() + .map(|line| line.trim().to_string()) + .collect::>(); + println!( + "Tracking beta rewards for the {} discord usernames provided in {:?}", + discord_names.len(), + participants_file + ); + discord_names + } else { + vec![] + }; + // restore beta participants from local cached copy + let local_participants_file = + get_auditor_data_dir_path()?.join(dag_db::BETA_PARTICIPANTS_FILENAME); + if local_participants_file.exists() { + let raw_data = std::fs::read_to_string(&local_participants_file)?; + let discord_names = raw_data + .lines() + .map(|line| line.trim().to_string()) + .collect::>(); + println!( + "Restoring beta rewards for the {} discord usernames from {:?}", + discord_names.len(), + local_participants_file + ); + beta_participants.extend(discord_names); + } + // write the beta participants to disk + let _ = std::fs::write(local_participants_file, beta_participants.join("\n")) + .map_err(|e| eprintln!("Failed to write beta participants to disk: {e}")); + + Ok(beta_participants) +} diff --git a/sn_auditor/src/routes.rs b/sn_auditor/src/routes.rs index 5f8802ea66..edc9892246 100644 --- a/sn_auditor/src/routes.rs +++ b/sn_auditor/src/routes.rs @@ -6,9 +6,15 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. +use crate::dag_db::{self, SpendDagDb}; use color_eyre::eyre::{eyre, Result}; use sn_client::transfers::SpendAddress; -use std::{io::Cursor, str::FromStr}; +use std::{ + fs::{File, OpenOptions}, + io::{Cursor, Write}, + path::PathBuf, + str::FromStr, +}; use tiny_http::{Request, Response}; pub(crate) fn spend_dag_svg(_dag: &SpendDagDb) -> Result>>> { @@ -87,10 +93,38 @@ pub(crate) fn add_participant( ); } - match dag.track_new_beta_participants(vec![discord_id.to_owned()]) { - Ok(()) => Ok(Response::from_string("Added participant")), - Err(e) => Ok( - Response::from_string(format!("Failed to add participant: {e}")).with_status_code(500), - ), + if let Err(err) = dag.track_new_beta_participants(vec![discord_id.to_owned()]) { + return Ok( + Response::from_string(format!("Failed to add participant: {err}")) + .with_status_code(400), + ); + } + + // append the new participant to the local file + let local_participants_file = dag.path.join(dag_db::BETA_PARTICIPANTS_FILENAME); + if let Err(err) = + write_discord_id_to_local_file(&local_participants_file, discord_id.to_owned()) + { + return Ok( + Response::from_string(format!("Failed to cache participant to file: {err}")) + .with_status_code(400), + ); + } + + Ok(Response::from_string("Successfully added participant ")) +} + +fn write_discord_id_to_local_file(path: &PathBuf, id: String) -> Result<()> { + if path.exists() { + let mut file = OpenOptions::new() + .append(true) + .open(path) + .map_err(|e| eyre!("Failed to open file: {e}"))?; + writeln!(file, "{id}")?; + } else { + let mut file = File::create(path).map_err(|e| eyre!("Failed to create file: {e}"))?; + writeln!(file, "{id}")?; } + + Ok(()) }