Wait for i2p http proxy on remote node usage.

Generate npmk after wallet creation.
This commit is contained in:
kn0sys 2024-09-16 21:16:09 -04:00
parent 7f688e4e8b
commit 998e73a5ed
No known key found for this signature in database
GPG key ID: 3BDB674C95F103FA
8 changed files with 112 additions and 80 deletions

5
.gitignore vendored
View file

@ -29,3 +29,8 @@ router.keys.dat
prngseed.rnd prngseed.rnd
hostsdb.blockfile hostsdb.blockfile
logs/log-1.txt logs/log-1.txt
certificates/
netDb/*
peerProfiles/*
*.dat
ssu2tokens.txt

View file

@ -90,14 +90,14 @@ pub struct Args {
#[arg( #[arg(
long, long,
help = "i2p http proxy host", help = "i2p http proxy host",
default_value = "http://localhost:4444" default_value = "http://localhost:4455"
)] )]
pub i2p_proxy_host: String, pub i2p_proxy_host: String,
/// i2p wallet proxy host (i2p socks) /// i2p wallet proxy host (i2p socks)
#[arg( #[arg(
long, long,
help = "i2p remote node socks proxy host", help = "i2p remote node socks proxy host",
default_value = "http://localhost:9051" default_value = "http://localhost:9055"
)] )]
pub i2p_socks_proxy_host: String, pub i2p_socks_proxy_host: String,
/// Connect wallet rpc for a remote-node, WARNING: may harm privacy /// Connect wallet rpc for a remote-node, WARNING: may harm privacy

View file

@ -63,22 +63,24 @@ pub fn create(d: Json<Dispute>) -> Result<Dispute, MdbError> {
} }
/// Dispute lookup /// Dispute lookup
pub fn find(did: &String) -> Result<Dispute, MdbError> { pub fn find(did: &String) -> Result<Dispute, NevekoError> {
let db = &DATABASE_LOCK; 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() { if r.is_empty() {
error!("dispute not found"); error!("dispute not found");
return Err(MdbError::NotFound); return Err(NevekoError::Database(MdbError::Panic));
} }
let result: Dispute = bincode::deserialize(&r[..]).unwrap_or_default(); let result: Dispute = bincode::deserialize(&r[..]).unwrap_or_default();
Ok(result) Ok(result)
} }
/// Lookup all disputes /// Lookup all disputes
pub fn find_all() -> Result<Vec<Dispute>, MdbError> { pub fn find_all() -> Result<Vec<Dispute>, NevekoError> {
let db = &DATABASE_LOCK; let db = &DATABASE_LOCK;
let d_list_key = crate::DISPUTE_LIST_DB_KEY; 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() { if d_r.is_empty() {
error!("dispute index not found"); 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 /// creation date of the dispute plus the one week
/// ///
/// grace period then the dispute is auto-settled. /// 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<()> = let tick: std::sync::mpsc::Receiver<()> =
schedule_recv::periodic_ms(crate::DISPUTE_CHECK_INTERVAL); schedule_recv::periodic_ms(crate::DISPUTE_CHECK_INTERVAL);
loop { loop {
@ -122,9 +124,11 @@ pub async fn settle_dispute() -> Result<(), MdbError> {
tick.recv().unwrap(); tick.recv().unwrap();
let db = &DATABASE_LOCK; let db = &DATABASE_LOCK;
let list_key = crate::DISPUTE_LIST_DB_KEY; 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() { if r.is_empty() {
info!("dispute index not found"); info!("dispute index not found");
return Err(NevekoError::Database(MdbError::NotFound));
} }
let str_r: String = bincode::deserialize(&r[..]).unwrap_or_default(); let str_r: String = bincode::deserialize(&r[..]).unwrap_or_default();
let v_mid = str_r.split(","); let v_mid = str_r.split(",");
@ -134,7 +138,8 @@ pub async fn settle_dispute() -> Result<(), MdbError> {
if cleared { if cleared {
// index was created but cleared // index was created but cleared
info!("terminating dispute auto-settle thread"); 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(()); return Ok(());
} }
for d in d_vec { for d in d_vec {
@ -154,7 +159,7 @@ pub async fn settle_dispute() -> Result<(), MdbError> {
return Ok(()); return Ok(());
} }
// remove the dispute from the db // 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 /// 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); info!("removing id {} from disputes", &did);
let db = &DATABASE_LOCK; let db = &DATABASE_LOCK;
let list_key = crate::DISPUTE_LIST_DB_KEY; 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() { if r.is_empty() {
debug!("dispute list index 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: {}", "writing dipsute index {} for id: {}",
dispute_list, list_key dispute_list, list_key
); );
db::write_chunks( db::write_chunks(
&db.env, &db.env,
&db.handle, &db.handle,
list_key.as_bytes(), list_key.as_bytes(),
dispute_list.as_bytes(), dispute_list.as_bytes(),
)?; )
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
Ok(()) Ok(())
} }

View file

@ -8,7 +8,7 @@ use crate::{
error::NevekoError, error::NevekoError,
monero::get_anon_inbound_port, monero::get_anon_inbound_port,
utils, utils,
DEFAULT_APP_PORT, DEFAULT_HTTP_PROXY_PORT,
DEFAULT_SOCKS_PORT, DEFAULT_SOCKS_PORT,
}; };
use j4i2prs::{ use j4i2prs::{
@ -67,7 +67,7 @@ pub struct HttpProxyStatus {
pub open: bool, pub open: bool,
} }
#[derive(Debug, Serialize, PartialEq)] #[derive(Debug, Deserialize, Serialize, PartialEq)]
pub enum ProxyStatus { pub enum ProxyStatus {
Opening, Opening,
Open, Open,
@ -109,9 +109,12 @@ pub fn get_destination(st: ServerTunnelType) -> Result<String, NevekoError> {
&crate::APP_ANON_IN_B32_DEST.as_bytes().to_vec(), &crate::APP_ANON_IN_B32_DEST.as_bytes().to_vec(),
) )
.map_err(|_| NevekoError::Database(MdbError::Panic))?; .map_err(|_| NevekoError::Database(MdbError::Panic))?;
let r_app_b32_dest = let r_app_b32_dest = db::DatabaseEnvironment::read(
db::DatabaseEnvironment::read(&db.env, &db.handle, &crate::APP_I2P_SK.as_bytes().to_vec()) &db.env,
.map_err(|_| NevekoError::Database(MdbError::Panic))?; &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 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(); let app_b32_dest: String = bincode::deserialize(&&r_app_b32_dest[..]).unwrap_or_default();
match st { match st {
@ -122,35 +125,16 @@ pub fn get_destination(st: ServerTunnelType) -> Result<String, NevekoError> {
/// Ping our base 32 destination address over the http proxy /// Ping our base 32 destination address over the http proxy
pub async fn check_connection() -> Result<ProxyStatus, NevekoError> { pub async fn check_connection() -> Result<ProxyStatus, NevekoError> {
let host = utils::get_i2p_http_proxy(); let db = &DATABASE_LOCK;
let proxy = reqwest::Proxy::http(&host).map_err(|_| NevekoError::I2P)?; let r =
let client = reqwest::Client::builder().proxy(proxy).build(); db::DatabaseEnvironment::read(&db.env, &db.handle, &crate::I2P_STATUS.as_bytes().to_vec())
let b32_dest = get_destination(ServerTunnelType::App)?; .map_err(|_| NevekoError::Database(MdbError::Panic))?;
match client if r.is_empty() {
.map_err(|_| NevekoError::I2P)? error!("i2p status not found");
.get(format!("http://{}/status", b32_dest)) return Err(NevekoError::Database(MdbError::NotFound));
.send()
.await
{
Ok(response) => {
let res = response.json::<HttpProxyStatus>().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 result: ProxyStatus = bincode::deserialize(&r[..]).unwrap_or(ProxyStatus::Opening);
Ok(result)
} }
#[derive(PartialEq)] #[derive(PartialEq)]
@ -198,7 +182,7 @@ fn create_server_tunnel(st: ServerTunnelType) -> Result<tc::Tunnel, NevekoError>
pub fn start() -> Result<(), NevekoError> { pub fn start() -> Result<(), NevekoError> {
let http_proxy_port: u16 = get_i2p_proxy_port() let http_proxy_port: u16 = get_i2p_proxy_port()
.parse::<u16>() .parse::<u16>()
.unwrap_or(DEFAULT_APP_PORT); .unwrap_or(DEFAULT_HTTP_PROXY_PORT);
let socks_port: u16 = get_i2p_socks_proxy_port() let socks_port: u16 = get_i2p_socks_proxy_port()
.parse::<u16>() .parse::<u16>()
.unwrap_or(DEFAULT_SOCKS_PORT); .unwrap_or(DEFAULT_SOCKS_PORT);
@ -296,6 +280,16 @@ pub fn start() -> Result<(), NevekoError> {
.unwrap_or_default(); .unwrap_or_default();
let _ = anon_tunnel.start(Some(String::from(&anon_in_sk))); 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."));
} }
} }
} }

View file

@ -62,16 +62,15 @@ pub const MONERO_WALLET_RPC_HOST: &str = "MONERO_WALLET_RPC_HOST";
/// Reference to check if gui set remote node flag /// Reference to check if gui set remote node flag
pub const GUI_REMOTE_NODE: &str = "GUI_REMOTE_NODE"; pub const GUI_REMOTE_NODE: &str = "GUI_REMOTE_NODE";
pub const GUI_SET_REMOTE_NODE: &str = "1"; 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; pub const FTS_RETRY_INTERVAL: u32 = 60000;
/// There is a one week grace period for manual intervention of disputes /// There is a one week grace period for manual intervention of disputes
pub const DISPUTE_AUTO_SETTLE: u32 = 1000 * 60 * 60 * 24 * 7; pub const DISPUTE_AUTO_SETTLE: u32 = 1000 * 60 * 60 * 24 * 7;
/// Daily dispute auto-settle check interval /// Daily dispute auto-settle check interval
pub const DISPUTE_CHECK_INTERVAL: u32 = 1000 * 60 * 60 * 24; pub const DISPUTE_CHECK_INTERVAL: u32 = 1000 * 60 * 60 * 24;
/// Default app port /// Default app port
pub const DEFAULT_APP_PORT: u16 = 9000; pub const DEFAULT_HTTP_PROXY_PORT: u16 = 4455;
/// Default app port /// Default app port
pub const DEFAULT_SOCKS_PORT: u16 = 9051; pub const DEFAULT_SOCKS_PORT: u16 = 9051;
/// I2P CONNECTION CHECK
pub const I2P_STATUS: &str = "I2P_STATUS";
// DO NOT EDIT BELOW THIS LINE // DO NOT EDIT BELOW THIS LINE

View file

@ -519,7 +519,7 @@ pub async fn retry_fts() -> Result<(), NevekoError> {
.map_err(|_| NevekoError::Database(MdbError::Panic))?; .map_err(|_| NevekoError::Database(MdbError::Panic))?;
if r.is_empty() { if r.is_empty() {
info!("fts message index not found"); 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 s_r: String = bincode::deserialize(&r[..]).unwrap_or_default();
let v_mid = s_r.split(","); let v_mid = s_r.split(",");

View file

@ -9,7 +9,10 @@ use crate::{
}, },
dispute, dispute,
error::NevekoError, error::NevekoError,
i2p, i2p::{
self,
ProxyStatus,
},
message, message,
models, models,
monero, monero,
@ -83,16 +86,16 @@ impl Default for Connections {
fn default() -> Self { fn default() -> Self {
Connections { Connections {
blockchain_dir: String::from("/home/user/.bitmonero"), blockchain_dir: String::from("/home/user/.bitmonero"),
daemon_host: String::from("http://127.0.0.1:38081"), daemon_host: String::from("http://127.0.0.1:18081"),
i2p_proxy_host: String::from("http://127.0.0.1:4444"), i2p_proxy_host: String::from("http://127.0.0.1:4455"),
i2p_socks_host: String::from("http://127.0.0.1:9051"), i2p_socks_host: String::from("http://127.0.0.1:9055"),
is_remote_node: false, is_remote_node: false,
is_i2p_advanced: false, is_i2p_advanced: false,
mainnet: true, mainnet: true,
monero_location: String::from("/home/user/monero-x86_64-linux-gnu-v0.18.3.4"), monero_location: String::from("/home/user/monero-x86_64-linux-gnu-v0.18.3.4"),
rpc_credential: String::from("pass"), rpc_credential: String::from("pass"),
rpc_username: String::from("user"), 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]; let mut data = [0u8; 32];
rand::thread_rng().fill_bytes(&mut data); rand::thread_rng().fill_bytes(&mut data);
let db = &DATABASE_LOCK; let db = &DATABASE_LOCK;
let h = hex::encode(data);
let v = bincode::serialize(&h).unwrap_or_default();
db::write_chunks( db::write_chunks(
&db.env, &db.env,
&db.handle, &db.handle,
crate::NEVEKO_JWP_SECRET_KEY.as_bytes(), crate::NEVEKO_JWP_SECRET_KEY.as_bytes(),
&data, &v,
) )
.map_err(|_| NevekoError::Database(MdbError::Panic))?; .map_err(|_| NevekoError::Database(MdbError::Panic))?;
} }
@ -403,11 +408,13 @@ fn gen_signing_keys() -> Result<(), NevekoError> {
let mut data = [0u8; 32]; let mut data = [0u8; 32];
rand::thread_rng().fill_bytes(&mut data); rand::thread_rng().fill_bytes(&mut data);
let db = &DATABASE_LOCK; let db = &DATABASE_LOCK;
let h = hex::encode(data);
let v = bincode::serialize(&h).unwrap_or_default();
db::write_chunks( db::write_chunks(
&db.env, &db.env,
&db.handle, &db.handle,
crate::NEVEKO_JWT_SECRET_KEY.as_bytes(), crate::NEVEKO_JWT_SECRET_KEY.as_bytes(),
&data, &v,
) )
.map_err(|_| NevekoError::Database(MdbError::Panic))?; .map_err(|_| NevekoError::Database(MdbError::Panic))?;
} }
@ -480,20 +487,34 @@ async fn generate_nmpk() -> Result<(), NevekoError> {
let db = &DATABASE_LOCK; let db = &DATABASE_LOCK;
if nmpk.is_empty() { if nmpk.is_empty() {
let nmk: neveko25519::NevekoMessageKeys = neveko25519::generate_neveko_message_keys().await; let nmk: neveko25519::NevekoMessageKeys = neveko25519::generate_neveko_message_keys().await;
db::write_chunks( let v = bincode::serialize(&nmk.hex_nmpk).unwrap_or_default();
&db.env, db::write_chunks(&db.env, &db.handle, crate::NEVEKO_NMPK.as_bytes(), &v)
&db.handle, .map_err(|_| NevekoError::Database(MdbError::Panic))?;
crate::NEVEKO_NMPK.as_bytes(),
nmk.hex_nmpk.as_bytes(),
)
.map_err(|_| NevekoError::Database(MdbError::Panic))?;
} }
Ok(()) 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 /// Put all app pre-checks here
pub async fn start_up() -> Result<(), NevekoError> { 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"); info!("neveko is starting up");
let _ = reset_i2p_status()?;
warn!("monero multisig is experimental and usage of neveko may lead to loss of funds"); warn!("monero multisig is experimental and usage of neveko may lead to loss of funds");
let args = args::Args::parse(); let args = args::Args::parse();
if args.clear_fts { if args.clear_fts {
@ -522,16 +543,31 @@ pub async fn start_up() -> Result<(), NevekoError> {
wallet_password = read_password().unwrap(); wallet_password = read_password().unwrap();
std::env::set_var(crate::MONERO_WALLET_PASSWORD, &wallet_password); std::env::set_var(crate::MONERO_WALLET_PASSWORD, &wallet_password);
} }
generate_nmpk().await?;
let env: String = get_release_env().value(); let env: String = get_release_env().value();
if !args.i2p_advanced { if !args.i2p_advanced {
let _ = i2p::start(); // let _ = i2p::start();
} }
gen_app_wallet(&wallet_password).await;
// start async background tasks here // start async background tasks here
{ {
tokio::spawn(async { tokio::spawn(async move {
let _ = message::retry_fts().await; 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; let _ = dispute::settle_dispute().await;
}); });
} }

View file

@ -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;
}
}
}
}); });
} }
} }