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
hostsdb.blockfile
logs/log-1.txt
certificates/
netDb/*
peerProfiles/*
*.dat
ssu2tokens.txt

View file

@ -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

View file

@ -63,22 +63,24 @@ pub fn create(d: Json<Dispute>) -> Result<Dispute, MdbError> {
}
/// Dispute lookup
pub fn find(did: &String) -> Result<Dispute, MdbError> {
pub fn find(did: &String) -> Result<Dispute, NevekoError> {
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<Vec<Dispute>, MdbError> {
pub fn find_all() -> Result<Vec<Dispute>, 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(())
}

View file

@ -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<String, NevekoError> {
&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<String, NevekoError> {
/// Ping our base 32 destination address over the http proxy
pub async fn check_connection() -> Result<ProxyStatus, NevekoError> {
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::<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 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<tc::Tunnel, NevekoError>
pub fn start() -> Result<(), NevekoError> {
let http_proxy_port: u16 = get_i2p_proxy_port()
.parse::<u16>()
.unwrap_or(DEFAULT_APP_PORT);
.unwrap_or(DEFAULT_HTTP_PROXY_PORT);
let socks_port: u16 = get_i2p_socks_proxy_port()
.parse::<u16>()
.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."));
}
}
}

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
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

View file

@ -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(",");

View file

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

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