From 998e73a5ed0dabbe3c3ae69a3a158e49f4ed9e0b Mon Sep 17 00:00:00 2001 From: kn0sys Date: Mon, 16 Sep 2024 21:16:09 -0400 Subject: [PATCH] Wait for i2p http proxy on remote node usage. Generate npmk after wallet creation. --- .gitignore | 5 +++ neveko-core/src/args.rs | 4 +-- neveko-core/src/dispute.rs | 32 ++++++++++------- neveko-core/src/i2p.rs | 62 +++++++++++++++----------------- neveko-core/src/lib.rs | 7 ++-- neveko-core/src/message.rs | 2 +- neveko-core/src/utils.rs | 72 +++++++++++++++++++++++++++---------- neveko-gui/src/apps/home.rs | 8 ----- 8 files changed, 112 insertions(+), 80 deletions(-) diff --git a/.gitignore b/.gitignore index 22cb432..52a0af6 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,8 @@ router.keys.dat prngseed.rnd hostsdb.blockfile logs/log-1.txt +certificates/ +netDb/* +peerProfiles/* +*.dat +ssu2tokens.txt diff --git a/neveko-core/src/args.rs b/neveko-core/src/args.rs index 45e13db..292a643 100644 --- a/neveko-core/src/args.rs +++ b/neveko-core/src/args.rs @@ -90,14 +90,14 @@ pub struct Args { #[arg( long, help = "i2p http proxy host", - default_value = "http://localhost:4444" + default_value = "http://localhost:4455" )] pub i2p_proxy_host: String, /// i2p wallet proxy host (i2p socks) #[arg( long, help = "i2p remote node socks proxy host", - default_value = "http://localhost:9051" + default_value = "http://localhost:9055" )] pub i2p_socks_proxy_host: String, /// Connect wallet rpc for a remote-node, WARNING: may harm privacy diff --git a/neveko-core/src/dispute.rs b/neveko-core/src/dispute.rs index b6fc2ec..b2e15d8 100644 --- a/neveko-core/src/dispute.rs +++ b/neveko-core/src/dispute.rs @@ -63,22 +63,24 @@ pub fn create(d: Json) -> Result { } /// Dispute lookup -pub fn find(did: &String) -> Result { +pub fn find(did: &String) -> Result { let db = &DATABASE_LOCK; - let r = db::DatabaseEnvironment::read(&db.env, &db.handle, &did.as_bytes().to_vec())?; + let r = db::DatabaseEnvironment::read(&db.env, &db.handle, &did.as_bytes().to_vec()) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; if r.is_empty() { error!("dispute not found"); - return Err(MdbError::NotFound); + return Err(NevekoError::Database(MdbError::Panic)); } let result: Dispute = bincode::deserialize(&r[..]).unwrap_or_default(); Ok(result) } /// Lookup all disputes -pub fn find_all() -> Result, MdbError> { +pub fn find_all() -> Result, NevekoError> { let db = &DATABASE_LOCK; let d_list_key = crate::DISPUTE_LIST_DB_KEY; - let d_r = db::DatabaseEnvironment::read(&db.env, &db.handle, &d_list_key.as_bytes().to_vec())?; + let d_r = db::DatabaseEnvironment::read(&db.env, &db.handle, &d_list_key.as_bytes().to_vec()) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; if d_r.is_empty() { error!("dispute index not found"); } @@ -114,7 +116,7 @@ pub fn delete(did: &String) -> Result<(), MdbError> { /// creation date of the dispute plus the one week /// /// grace period then the dispute is auto-settled. -pub async fn settle_dispute() -> Result<(), MdbError> { +pub async fn settle_dispute() -> Result<(), NevekoError> { let tick: std::sync::mpsc::Receiver<()> = schedule_recv::periodic_ms(crate::DISPUTE_CHECK_INTERVAL); loop { @@ -122,9 +124,11 @@ pub async fn settle_dispute() -> Result<(), MdbError> { tick.recv().unwrap(); let db = &DATABASE_LOCK; let list_key = crate::DISPUTE_LIST_DB_KEY; - let r = db::DatabaseEnvironment::read(&db.env, &db.handle, &list_key.as_bytes().to_vec())?; + let r = db::DatabaseEnvironment::read(&db.env, &db.handle, &list_key.as_bytes().to_vec()) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; if r.is_empty() { info!("dispute index not found"); + return Err(NevekoError::Database(MdbError::NotFound)); } let str_r: String = bincode::deserialize(&r[..]).unwrap_or_default(); let v_mid = str_r.split(","); @@ -134,7 +138,8 @@ pub async fn settle_dispute() -> Result<(), MdbError> { if cleared { // index was created but cleared info!("terminating dispute auto-settle thread"); - let _ = db::DatabaseEnvironment::delete(&db.env, &db.handle, list_key.as_bytes())?; + let _ = db::DatabaseEnvironment::delete(&db.env, &db.handle, list_key.as_bytes()) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; return Ok(()); } for d in d_vec { @@ -154,7 +159,7 @@ pub async fn settle_dispute() -> Result<(), MdbError> { return Ok(()); } // remove the dispute from the db - remove_from_auto_settle(dispute.did)?; + remove_from_auto_settle(dispute.did).map(|_| NevekoError::Dispute)?; } } } @@ -174,11 +179,12 @@ fn is_dispute_clear(r: String) -> bool { } /// clear dispute from index -fn remove_from_auto_settle(did: String) -> Result<(), MdbError> { +fn remove_from_auto_settle(did: String) -> Result<(), NevekoError> { info!("removing id {} from disputes", &did); let db = &DATABASE_LOCK; let list_key = crate::DISPUTE_LIST_DB_KEY; - let r = db::DatabaseEnvironment::read(&db.env, &db.handle, &list_key.as_bytes().to_vec())?; + let r = db::DatabaseEnvironment::read(&db.env, &db.handle, &list_key.as_bytes().to_vec()) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; if r.is_empty() { debug!("dispute list index is empty"); } @@ -198,13 +204,13 @@ fn remove_from_auto_settle(did: String) -> Result<(), MdbError> { "writing dipsute index {} for id: {}", dispute_list, list_key ); - db::write_chunks( &db.env, &db.handle, list_key.as_bytes(), dispute_list.as_bytes(), - )?; + ) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; Ok(()) } diff --git a/neveko-core/src/i2p.rs b/neveko-core/src/i2p.rs index b13c803..a13b938 100644 --- a/neveko-core/src/i2p.rs +++ b/neveko-core/src/i2p.rs @@ -8,7 +8,7 @@ use crate::{ error::NevekoError, monero::get_anon_inbound_port, utils, - DEFAULT_APP_PORT, + DEFAULT_HTTP_PROXY_PORT, DEFAULT_SOCKS_PORT, }; use j4i2prs::{ @@ -67,7 +67,7 @@ pub struct HttpProxyStatus { pub open: bool, } -#[derive(Debug, Serialize, PartialEq)] +#[derive(Debug, Deserialize, Serialize, PartialEq)] pub enum ProxyStatus { Opening, Open, @@ -109,9 +109,12 @@ pub fn get_destination(st: ServerTunnelType) -> Result { &crate::APP_ANON_IN_B32_DEST.as_bytes().to_vec(), ) .map_err(|_| NevekoError::Database(MdbError::Panic))?; - let r_app_b32_dest = - db::DatabaseEnvironment::read(&db.env, &db.handle, &crate::APP_I2P_SK.as_bytes().to_vec()) - .map_err(|_| NevekoError::Database(MdbError::Panic))?; + let r_app_b32_dest = db::DatabaseEnvironment::read( + &db.env, + &db.handle, + &crate::APP_B32_DEST.as_bytes().to_vec(), + ) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; let anon_b32_dest: String = bincode::deserialize(&r_anon_b32_dest[..]).unwrap_or_default(); let app_b32_dest: String = bincode::deserialize(&&r_app_b32_dest[..]).unwrap_or_default(); match st { @@ -122,35 +125,16 @@ pub fn get_destination(st: ServerTunnelType) -> Result { /// Ping our base 32 destination address over the http proxy pub async fn check_connection() -> Result { - let host = utils::get_i2p_http_proxy(); - let proxy = reqwest::Proxy::http(&host).map_err(|_| NevekoError::I2P)?; - let client = reqwest::Client::builder().proxy(proxy).build(); - let b32_dest = get_destination(ServerTunnelType::App)?; - match client - .map_err(|_| NevekoError::I2P)? - .get(format!("http://{}/status", b32_dest)) - .send() - .await - { - Ok(response) => { - let res = response.json::().await; - debug!("check_connection response: {:?}", res); - return match res { - Ok(r) => { - if r.open { - Ok(ProxyStatus::Open) - } else { - Ok(ProxyStatus::Opening) - } - } - _ => Err(NevekoError::I2P), - }; - } - Err(e) => { - error!("failed to generate invoice due to: {:?}", e); - return Err(NevekoError::I2P); - } + let db = &DATABASE_LOCK; + let r = + db::DatabaseEnvironment::read(&db.env, &db.handle, &crate::I2P_STATUS.as_bytes().to_vec()) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; + if r.is_empty() { + error!("i2p status not found"); + return Err(NevekoError::Database(MdbError::NotFound)); } + let result: ProxyStatus = bincode::deserialize(&r[..]).unwrap_or(ProxyStatus::Opening); + Ok(result) } #[derive(PartialEq)] @@ -198,7 +182,7 @@ fn create_server_tunnel(st: ServerTunnelType) -> Result pub fn start() -> Result<(), NevekoError> { let http_proxy_port: u16 = get_i2p_proxy_port() .parse::() - .unwrap_or(DEFAULT_APP_PORT); + .unwrap_or(DEFAULT_HTTP_PROXY_PORT); let socks_port: u16 = get_i2p_socks_proxy_port() .parse::() .unwrap_or(DEFAULT_SOCKS_PORT); @@ -296,6 +280,16 @@ pub fn start() -> Result<(), NevekoError> { .unwrap_or_default(); let _ = anon_tunnel.start(Some(String::from(&anon_in_sk))); } + let db = &DATABASE_LOCK; + let v = bincode::serialize(&ProxyStatus::Open).unwrap_or_default(); + db::write_chunks( + &db.env, + &db.handle, + crate::I2P_STATUS.as_bytes(), + &v, + ) + .map_err(|_| NevekoError::Database(MdbError::Panic)) + .unwrap_or_else(|_| log::error!("failed to write i2p status.")); } } } diff --git a/neveko-core/src/lib.rs b/neveko-core/src/lib.rs index 063638c..ec5af5c 100644 --- a/neveko-core/src/lib.rs +++ b/neveko-core/src/lib.rs @@ -62,16 +62,15 @@ pub const MONERO_WALLET_RPC_HOST: &str = "MONERO_WALLET_RPC_HOST"; /// Reference to check if gui set remote node flag pub const GUI_REMOTE_NODE: &str = "GUI_REMOTE_NODE"; pub const GUI_SET_REMOTE_NODE: &str = "1"; - -pub const LMDB_MAPSIZE: u64 = 1024 * 1024 * 1024; -pub const I2P_CONNECTIVITY_CHECK_INTERVAL: u32 = 600000; pub const FTS_RETRY_INTERVAL: u32 = 60000; /// There is a one week grace period for manual intervention of disputes pub const DISPUTE_AUTO_SETTLE: u32 = 1000 * 60 * 60 * 24 * 7; /// Daily dispute auto-settle check interval pub const DISPUTE_CHECK_INTERVAL: u32 = 1000 * 60 * 60 * 24; /// Default app port -pub const DEFAULT_APP_PORT: u16 = 9000; +pub const DEFAULT_HTTP_PROXY_PORT: u16 = 4455; /// Default app port pub const DEFAULT_SOCKS_PORT: u16 = 9051; +/// I2P CONNECTION CHECK +pub const I2P_STATUS: &str = "I2P_STATUS"; // DO NOT EDIT BELOW THIS LINE diff --git a/neveko-core/src/message.rs b/neveko-core/src/message.rs index 5338ce6..8a60d01 100644 --- a/neveko-core/src/message.rs +++ b/neveko-core/src/message.rs @@ -519,7 +519,7 @@ pub async fn retry_fts() -> Result<(), NevekoError> { .map_err(|_| NevekoError::Database(MdbError::Panic))?; if r.is_empty() { info!("fts message index not found"); - break Err(NevekoError::Database(MdbError::NotFound)); // terminate fts if no message to send + return Err(NevekoError::Database(MdbError::NotFound)); // terminate fts if no message to send } let s_r: String = bincode::deserialize(&r[..]).unwrap_or_default(); let v_mid = s_r.split(","); diff --git a/neveko-core/src/utils.rs b/neveko-core/src/utils.rs index 46893f0..24623a6 100644 --- a/neveko-core/src/utils.rs +++ b/neveko-core/src/utils.rs @@ -9,7 +9,10 @@ use crate::{ }, dispute, error::NevekoError, - i2p, + i2p::{ + self, + ProxyStatus, + }, message, models, monero, @@ -83,16 +86,16 @@ impl Default for Connections { fn default() -> Self { Connections { blockchain_dir: String::from("/home/user/.bitmonero"), - daemon_host: String::from("http://127.0.0.1:38081"), - i2p_proxy_host: String::from("http://127.0.0.1:4444"), - i2p_socks_host: String::from("http://127.0.0.1:9051"), + daemon_host: String::from("http://127.0.0.1:18081"), + i2p_proxy_host: String::from("http://127.0.0.1:4455"), + i2p_socks_host: String::from("http://127.0.0.1:9055"), is_remote_node: false, is_i2p_advanced: false, mainnet: true, monero_location: String::from("/home/user/monero-x86_64-linux-gnu-v0.18.3.4"), rpc_credential: String::from("pass"), rpc_username: String::from("user"), - rpc_host: String::from("http://127.0.0.1:38083"), + rpc_host: String::from("http://127.0.0.1:18083"), } } } @@ -391,11 +394,13 @@ fn gen_signing_keys() -> Result<(), NevekoError> { let mut data = [0u8; 32]; rand::thread_rng().fill_bytes(&mut data); let db = &DATABASE_LOCK; + let h = hex::encode(data); + let v = bincode::serialize(&h).unwrap_or_default(); db::write_chunks( &db.env, &db.handle, crate::NEVEKO_JWP_SECRET_KEY.as_bytes(), - &data, + &v, ) .map_err(|_| NevekoError::Database(MdbError::Panic))?; } @@ -403,11 +408,13 @@ fn gen_signing_keys() -> Result<(), NevekoError> { let mut data = [0u8; 32]; rand::thread_rng().fill_bytes(&mut data); let db = &DATABASE_LOCK; + let h = hex::encode(data); + let v = bincode::serialize(&h).unwrap_or_default(); db::write_chunks( &db.env, &db.handle, crate::NEVEKO_JWT_SECRET_KEY.as_bytes(), - &data, + &v, ) .map_err(|_| NevekoError::Database(MdbError::Panic))?; } @@ -480,20 +487,34 @@ async fn generate_nmpk() -> Result<(), NevekoError> { let db = &DATABASE_LOCK; if nmpk.is_empty() { let nmk: neveko25519::NevekoMessageKeys = neveko25519::generate_neveko_message_keys().await; - db::write_chunks( - &db.env, - &db.handle, - crate::NEVEKO_NMPK.as_bytes(), - nmk.hex_nmpk.as_bytes(), - ) - .map_err(|_| NevekoError::Database(MdbError::Panic))?; + let v = bincode::serialize(&nmk.hex_nmpk).unwrap_or_default(); + db::write_chunks(&db.env, &db.handle, crate::NEVEKO_NMPK.as_bytes(), &v) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; } Ok(()) } +fn reset_i2p_status() -> Result<(), NevekoError> { + let db = &DATABASE_LOCK; + let v = bincode::serialize(&ProxyStatus::Opening).unwrap_or_default(); + db::write_chunks(&db.env, &db.handle, crate::I2P_STATUS.as_bytes(), &v) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; + Ok(()) +} + /// Put all app pre-checks here pub async fn start_up() -> Result<(), NevekoError> { + let db = &DATABASE_LOCK; + db::write_chunks( + &db.env, + &db.handle, + crate::NEVEKO_NMPK.as_bytes(), + &Vec::new(), + ) + .map_err(|_| NevekoError::Database(MdbError::Panic))?; + info!("neveko is starting up"); + let _ = reset_i2p_status()?; warn!("monero multisig is experimental and usage of neveko may lead to loss of funds"); let args = args::Args::parse(); if args.clear_fts { @@ -522,16 +543,31 @@ pub async fn start_up() -> Result<(), NevekoError> { wallet_password = read_password().unwrap(); std::env::set_var(crate::MONERO_WALLET_PASSWORD, &wallet_password); } - generate_nmpk().await?; let env: String = get_release_env().value(); if !args.i2p_advanced { - let _ = i2p::start(); + // let _ = i2p::start(); } - gen_app_wallet(&wallet_password).await; // start async background tasks here { - tokio::spawn(async { + tokio::spawn(async move { let _ = message::retry_fts().await; + // wait for the i2p http proxy tunnel since remote nodes are forced over i2p + if is_using_remote_node() { + loop { + let is_i2p_online = i2p::check_connection().await; + let i2p_status = is_i2p_online.unwrap_or(ProxyStatus::Opening); + if i2p_status == ProxyStatus::Opening { + log::error!("i2p has not warmed up yet, check wrapper.log"); + } else { + break; + } + std::thread::sleep(std::time::Duration::from_secs(60)); + } + } + gen_app_wallet(&wallet_password).await; + generate_nmpk() + .await + .unwrap_or_else(|_| log::debug!("unable to generate neveko message keys")); let _ = dispute::settle_dispute().await; }); } diff --git a/neveko-gui/src/apps/home.rs b/neveko-gui/src/apps/home.rs index 8c52c56..76920a6 100644 --- a/neveko-gui/src/apps/home.rs +++ b/neveko-gui/src/apps/home.rs @@ -387,14 +387,6 @@ impl eframe::App for HomeApp { } } } - if !self.is_core_running && !self.is_installing && !self.connections.is_remote_node && !self.connections.is_i2p_advanced - && (self.s_xmr_rpc_ver.result.version == 0 || self.s_i2p_status == i2p::ProxyStatus::Opening) { - if !self.is_loading { - if ui.button("Install Software").clicked() { - self.is_installing = true; - } - } - } }); } }