mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-10 23:40:23 -04:00
* feat(asb + cli): Redact logs + unify tracing infrastructure (#1733) Applies the changes from https://github.com/comit-network/xmr-btc-swap/pull/1733 to this fork --------- Co-authored-by: Einliterflasche <81313171+Einliterflasche@users.noreply.github.com>
This commit is contained in:
parent
1fe6391b7b
commit
792fbbf746
20 changed files with 1780 additions and 1396 deletions
|
@ -1,5 +1,6 @@
|
|||
[target.armv7-unknown-linux-gnueabihf]
|
||||
linker = "arm-linux-gnueabihf-gcc"
|
||||
|
||||
# windows defaults to smaller stack sizes which isn't enough
|
||||
[target.'cfg(windows)']
|
||||
rustflags = ["-C", "link-args=/STACK:8388608"]
|
|
@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
- ASB + CLI: You can now use the `logs` command to retrieve logs stored in the past, redacting addresses and id's using `logs --redact`.
|
||||
- ASB: The `--disable-timestamp` flag has been removed
|
||||
- Introduced a cooperative Monero redeem feature for Bob to request from Alice if Bob is punished for not refunding in time. Alice can choose to cooperate but is not obligated to do so. This change is backwards compatible. To attempt recovery, resume a swap in the "Bitcoin punished" state. Success depends on Alice being active and still having a record of the swap. Note that Alice's cooperation is voluntary and recovery is not guaranteed
|
||||
|
||||
## [0.13.2] - 2024-07-02
|
||||
|
|
2447
Cargo.lock
generated
2447
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -97,7 +97,7 @@ fn setup(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
|||
.with_monero(Monero {
|
||||
monero_daemon_address: None,
|
||||
})
|
||||
.with_json(true)
|
||||
.with_json(false)
|
||||
.with_debug(true)
|
||||
.with_tauri(TauriHandle::new(app.app_handle().to_owned()))
|
||||
.build()
|
||||
|
|
|
@ -58,11 +58,13 @@ libp2p = { version = "0.42.2", default-features = false, features = [
|
|||
] }
|
||||
monero = { version = "0.12", features = [ "serde_support" ] }
|
||||
monero-rpc = { path = "../monero-rpc" }
|
||||
once_cell = "1.19"
|
||||
pem = "3.0"
|
||||
proptest = "1"
|
||||
qrcode = "0.14"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
regex = "1.10"
|
||||
reqwest = { version = "0.12", features = [
|
||||
"http2",
|
||||
"rustls-tls",
|
||||
|
|
|
@ -4,7 +4,6 @@ mod event_loop;
|
|||
mod network;
|
||||
mod rate;
|
||||
mod recovery;
|
||||
pub mod tracing;
|
||||
|
||||
pub use event_loop::{EventLoop, EventLoopHandle, FixedRate, KrakenRate, LatestRate};
|
||||
pub use network::behaviour::{Behaviour, OutEvent};
|
||||
|
|
|
@ -19,7 +19,6 @@ where
|
|||
let args = RawArguments::from_clap(&matches);
|
||||
|
||||
let json = args.json;
|
||||
let disable_timestamp = args.disable_timestamp;
|
||||
let testnet = args.testnet;
|
||||
let config = args.config;
|
||||
let command: RawCommand = args.cmd;
|
||||
|
@ -28,7 +27,6 @@ where
|
|||
RawCommand::Start { resume_only } => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Start { resume_only },
|
||||
|
@ -36,15 +34,28 @@ where
|
|||
RawCommand::History => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::History,
|
||||
},
|
||||
RawCommand::Logs {
|
||||
logs_dir: dir_path,
|
||||
swap_id,
|
||||
redact,
|
||||
} => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Logs {
|
||||
logs_dir: dir_path,
|
||||
swap_id,
|
||||
redact,
|
||||
},
|
||||
},
|
||||
RawCommand::WithdrawBtc { amount, address } => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::WithdrawBtc {
|
||||
|
@ -55,7 +66,6 @@ where
|
|||
RawCommand::Balance => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Balance,
|
||||
|
@ -63,7 +73,6 @@ where
|
|||
RawCommand::Config => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Config,
|
||||
|
@ -71,7 +80,6 @@ where
|
|||
RawCommand::ExportBitcoinWallet => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::ExportBitcoinWallet,
|
||||
|
@ -82,7 +90,6 @@ where
|
|||
}) => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Redeem {
|
||||
|
@ -96,7 +103,6 @@ where
|
|||
}) => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Cancel { swap_id },
|
||||
|
@ -106,7 +112,6 @@ where
|
|||
}) => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Refund { swap_id },
|
||||
|
@ -116,7 +121,6 @@ where
|
|||
}) => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::Punish { swap_id },
|
||||
|
@ -124,7 +128,6 @@ where
|
|||
RawCommand::ManualRecovery(ManualRecovery::SafelyAbort { swap_id }) => Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path: config_path(config, testnet)?,
|
||||
env_config: env_config(testnet),
|
||||
cmd: Command::SafelyAbort { swap_id },
|
||||
|
@ -184,7 +187,6 @@ pub struct BitcoinAddressNetworkMismatch {
|
|||
pub struct Arguments {
|
||||
pub testnet: bool,
|
||||
pub json: bool,
|
||||
pub disable_timestamp: bool,
|
||||
pub config_path: PathBuf,
|
||||
pub env_config: env::Config,
|
||||
pub cmd: Command,
|
||||
|
@ -197,6 +199,11 @@ pub enum Command {
|
|||
},
|
||||
History,
|
||||
Config,
|
||||
Logs {
|
||||
logs_dir: Option<PathBuf>,
|
||||
swap_id: Option<Uuid>,
|
||||
redact: bool,
|
||||
},
|
||||
WithdrawBtc {
|
||||
amount: Option<Amount>,
|
||||
address: Address,
|
||||
|
@ -268,6 +275,25 @@ pub enum RawCommand {
|
|||
)]
|
||||
resume_only: bool,
|
||||
},
|
||||
#[structopt(about = "Prints all logging messages issued in the past.")]
|
||||
Logs {
|
||||
#[structopt(
|
||||
short = "d",
|
||||
help = "Print the logs from this directory instead of the default one."
|
||||
)]
|
||||
logs_dir: Option<PathBuf>,
|
||||
#[structopt(
|
||||
help = "Redact swap-ids, Bitcoin and Monero addresses.",
|
||||
long = "redact"
|
||||
)]
|
||||
redact: bool,
|
||||
#[structopt(
|
||||
long = "swap-id",
|
||||
help = "Filter for logs concerning this swap.",
|
||||
long_help = "This checks whether each logging message contains the swap id. Some messages might be skipped when they don't contain the swap id even though they're relevant."
|
||||
)]
|
||||
swap_id: Option<Uuid>,
|
||||
},
|
||||
#[structopt(about = "Prints swap-id and the state of each swap ever made.")]
|
||||
History,
|
||||
#[structopt(about = "Prints the current config")]
|
||||
|
@ -366,7 +392,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::Start { resume_only: false },
|
||||
|
@ -384,7 +409,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::History,
|
||||
|
@ -402,7 +426,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::Balance,
|
||||
|
@ -424,7 +447,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::WithdrawBtc {
|
||||
|
@ -451,7 +473,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::Cancel {
|
||||
|
@ -477,7 +498,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::Refund {
|
||||
|
@ -503,7 +523,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::Punish {
|
||||
|
@ -529,7 +548,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::SafelyAbort {
|
||||
|
@ -549,7 +567,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::Start { resume_only: false },
|
||||
|
@ -567,7 +584,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::History,
|
||||
|
@ -585,7 +601,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::Balance,
|
||||
|
@ -609,7 +624,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::WithdrawBtc {
|
||||
|
@ -636,7 +650,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::Cancel {
|
||||
|
@ -663,7 +676,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::Refund {
|
||||
|
@ -690,7 +702,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::Punish {
|
||||
|
@ -717,7 +728,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: true,
|
||||
json: false,
|
||||
disable_timestamp: false,
|
||||
config_path: default_testnet_conf_path,
|
||||
env_config: testnet_env_config,
|
||||
cmd: Command::SafelyAbort {
|
||||
|
@ -737,7 +747,6 @@ mod tests {
|
|||
let expected_args = Arguments {
|
||||
testnet: false,
|
||||
json: false,
|
||||
disable_timestamp: true,
|
||||
config_path: default_mainnet_conf_path,
|
||||
env_config: mainnet_env_config,
|
||||
cmd: Command::Start { resume_only: false },
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
use tracing_subscriber::fmt::time::UtcTime;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
pub fn init(level: LevelFilter, json_format: bool, timestamp: bool) -> Result<()> {
|
||||
if level == LevelFilter::OFF {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
|
||||
let builder = FmtSubscriber::builder()
|
||||
.with_env_filter(format!("asb={},swap={}", level, level))
|
||||
.with_writer(std::io::stderr)
|
||||
.with_ansi(is_terminal)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.with_target(false);
|
||||
|
||||
match (json_format, timestamp) {
|
||||
(true, true) => builder.json().init(),
|
||||
(true, false) => builder.json().without_time().init(),
|
||||
(false, true) => builder.init(),
|
||||
(false, false) => builder.without_time().init(),
|
||||
}
|
||||
|
||||
tracing::info!(%level, "Initialized tracing");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -29,14 +29,15 @@ use swap::asb::config::{
|
|||
initial_setup, query_user_for_initial_config, read_config, Config, ConfigNotInitialized,
|
||||
};
|
||||
use swap::asb::{cancel, punish, redeem, refund, safely_abort, EventLoop, Finality, KrakenRate};
|
||||
use swap::common::check_latest_version;
|
||||
use swap::common::tracing_util::Format;
|
||||
use swap::common::{self, check_latest_version, get_logs};
|
||||
use swap::database::open_db;
|
||||
use swap::network::rendezvous::XmrBtcNamespace;
|
||||
use swap::network::swarm;
|
||||
use swap::protocol::alice::{run, AliceState};
|
||||
use swap::seed::Seed;
|
||||
use swap::tor::AuthenticatedClient;
|
||||
use swap::{asb, bitcoin, kraken, monero, tor};
|
||||
use swap::{bitcoin, kraken, monero, tor};
|
||||
use tracing_subscriber::filter::LevelFilter;
|
||||
|
||||
const DEFAULT_WALLET_NAME: &str = "asb-wallet";
|
||||
|
@ -46,34 +47,29 @@ pub async fn main() -> Result<()> {
|
|||
let Arguments {
|
||||
testnet,
|
||||
json,
|
||||
disable_timestamp,
|
||||
config_path,
|
||||
env_config,
|
||||
cmd,
|
||||
} = match parse_args(env::args_os()) {
|
||||
Ok(args) => args,
|
||||
Err(e) => {
|
||||
// make sure to display the clap error message it exists
|
||||
if let Some(clap_err) = e.downcast_ref::<clap::Error>() {
|
||||
match clap_err.kind {
|
||||
ErrorKind::HelpDisplayed | ErrorKind::VersionDisplayed => {
|
||||
println!("{}", clap_err.message);
|
||||
std::process::exit(0);
|
||||
}
|
||||
_ => {
|
||||
bail!(e);
|
||||
}
|
||||
if let ErrorKind::HelpDisplayed | ErrorKind::VersionDisplayed = clap_err.kind {
|
||||
println!("{}", clap_err.message);
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
bail!(e);
|
||||
}
|
||||
};
|
||||
|
||||
// warn if we're not on the latest version
|
||||
if let Err(e) = check_latest_version(env!("CARGO_PKG_VERSION")).await {
|
||||
eprintln!("{}", e);
|
||||
}
|
||||
|
||||
asb::tracing::init(LevelFilter::DEBUG, json, !disable_timestamp).expect("initialize tracing");
|
||||
|
||||
// read config from the specified path
|
||||
let config = match read_config(config_path.clone())? {
|
||||
Ok(config) => config,
|
||||
Err(ConfigNotInitialized {}) => {
|
||||
|
@ -82,6 +78,12 @@ pub async fn main() -> Result<()> {
|
|||
}
|
||||
};
|
||||
|
||||
// initialize tracing
|
||||
let format = if json { Format::Json } else { Format::Raw };
|
||||
let log_dir = config.data.dir.join("logs");
|
||||
common::tracing_util::init(LevelFilter::DEBUG, format, log_dir).expect("initialize tracing");
|
||||
|
||||
// check for conflicting env / config values
|
||||
if config.monero.network != env_config.monero_network {
|
||||
bail!(format!(
|
||||
"Expected monero network in config file to be {:?} but was {:?}",
|
||||
|
@ -108,6 +110,7 @@ pub async fn main() -> Result<()> {
|
|||
rendezvous_addrs.sort();
|
||||
rendezvous_addrs.dedup();
|
||||
let new_len = rendezvous_addrs.len();
|
||||
|
||||
if new_len < prev_len {
|
||||
tracing::warn!(
|
||||
"`rendezvous_point` config has {} duplicate entries, they are being ignored.",
|
||||
|
@ -115,9 +118,12 @@ pub async fn main() -> Result<()> {
|
|||
);
|
||||
}
|
||||
|
||||
// initialize monero wallet
|
||||
let monero_wallet = init_monero_wallet(&config, env_config).await?;
|
||||
let monero_address = monero_wallet.get_main_address();
|
||||
tracing::info!(%monero_address, "Monero wallet address");
|
||||
|
||||
// check monero balance
|
||||
let monero = monero_wallet.get_balance().await?;
|
||||
match (monero.balance, monero.unlocked_balance) {
|
||||
(0, _) => {
|
||||
|
@ -140,6 +146,7 @@ pub async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
// init bitcoin wallet
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
tracing::info!(%bitcoin_balance, "Bitcoin wallet balance");
|
||||
|
@ -240,6 +247,19 @@ pub async fn main() -> Result<()> {
|
|||
let config_json = serde_json::to_string_pretty(&config)?;
|
||||
println!("{}", config_json);
|
||||
}
|
||||
Command::Logs {
|
||||
logs_dir,
|
||||
swap_id,
|
||||
redact,
|
||||
} => {
|
||||
let dir = logs_dir.unwrap_or(config.data.dir.join("logs"));
|
||||
|
||||
let log_messages = get_logs(dir, swap_id, redact).await?;
|
||||
|
||||
for msg in log_messages {
|
||||
println!("{msg}");
|
||||
}
|
||||
}
|
||||
Command::WithdrawBtc { amount, address } => {
|
||||
let bitcoin_wallet = init_bitcoin_wallet(&config, &seed, env_config).await?;
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ pub mod cancel_and_refund;
|
|||
pub mod command;
|
||||
mod event_loop;
|
||||
mod list_sellers;
|
||||
pub mod tracing;
|
||||
pub mod transport;
|
||||
|
||||
pub use behaviour::{Behaviour, OutEvent};
|
||||
|
|
|
@ -2,14 +2,16 @@ pub mod request;
|
|||
pub mod tauri_bindings;
|
||||
|
||||
use crate::cli::command::{Bitcoin, Monero, Tor};
|
||||
use crate::common::tracing_util::Format;
|
||||
use crate::database::open_db;
|
||||
use crate::env::{Config as EnvConfig, GetConfig, Mainnet, Testnet};
|
||||
use crate::fs::system_data_dir;
|
||||
use crate::network::rendezvous::XmrBtcNamespace;
|
||||
use crate::protocol::Database;
|
||||
use crate::seed::Seed;
|
||||
use crate::{bitcoin, cli, monero};
|
||||
use anyhow::{anyhow, bail, Context as AnyContext, Error, Result};
|
||||
use crate::{bitcoin, common, monero};
|
||||
use anyhow::anyhow;
|
||||
use anyhow::{bail, Context as AnyContext, Error, Result};
|
||||
use futures::future::try_join_all;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
|
@ -18,6 +20,8 @@ use std::sync::{Arc, Mutex as SyncMutex, Once};
|
|||
use tauri_bindings::TauriHandle;
|
||||
use tokio::sync::{broadcast, broadcast::Sender, Mutex as TokioMutex, RwLock};
|
||||
use tokio::task::JoinHandle;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing::Level;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -274,8 +278,15 @@ impl ContextBuilder {
|
|||
let data_dir = data::data_dir_from(self.data, self.is_testnet)?;
|
||||
let env_config = env_config_from(self.is_testnet);
|
||||
|
||||
let format = if self.json { Format::Json } else { Format::Raw };
|
||||
let level_filter = if self.debug {
|
||||
LevelFilter::from_level(Level::DEBUG)
|
||||
} else {
|
||||
LevelFilter::from_level(Level::INFO)
|
||||
};
|
||||
|
||||
START.call_once(|| {
|
||||
let _ = cli::tracing::init(self.debug, self.json, data_dir.join("logs"));
|
||||
let _ = common::tracing_util::init(level_filter, format, data_dir.join("logs"));
|
||||
});
|
||||
|
||||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::bitcoin::{CancelTimelock, ExpiredTimelocks, PunishTimelock, TxLock};
|
|||
use crate::cli::api::tauri_bindings::{TauriEmitter, TauriSwapProgressEvent};
|
||||
use crate::cli::api::Context;
|
||||
use crate::cli::{list_sellers as list_sellers_impl, EventLoop, SellerStatus};
|
||||
use crate::common::get_logs;
|
||||
use crate::libp2p_ext::MultiAddrExt;
|
||||
use crate::network::quote::{BidQuote, ZeroQuoteReceived};
|
||||
use crate::network::swarm;
|
||||
|
@ -21,6 +22,7 @@ use std::cmp::min;
|
|||
use std::convert::TryInto;
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tracing::Instrument;
|
||||
|
@ -354,6 +356,36 @@ impl Request for GetSwapInfosAllArgs {
|
|||
}
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct GetLogsArgs {
|
||||
pub swap_id: Option<Uuid>,
|
||||
pub redact: bool,
|
||||
#[typeshare(serialized_as = "string")]
|
||||
pub logs_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct GetLogsResponse {
|
||||
logs: Vec<String>,
|
||||
}
|
||||
|
||||
impl Request for GetLogsArgs {
|
||||
type Response = GetLogsResponse;
|
||||
|
||||
async fn request(self, ctx: Arc<Context>) -> Result<Self::Response> {
|
||||
let dir = self.logs_dir.unwrap_or(ctx.config.data_dir.join("logs"));
|
||||
let logs = get_logs(dir, self.swap_id, self.redact).await?;
|
||||
|
||||
for msg in &logs {
|
||||
println!("{msg}");
|
||||
}
|
||||
|
||||
Ok(GetLogsResponse { logs })
|
||||
}
|
||||
}
|
||||
|
||||
#[tracing::instrument(fields(method = "suspend_current_swap"), skip(context))]
|
||||
pub async fn suspend_current_swap(context: Arc<Context>) -> Result<SuspendCurrentSwapResponse> {
|
||||
let swap_id = context.swap_lock.get_current_swap_id().await;
|
||||
|
|
|
@ -18,6 +18,7 @@ use structopt::{clap, StructOpt};
|
|||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::api::request::GetLogsArgs;
|
||||
use super::api::ContextBuilder;
|
||||
|
||||
// See: https://moneroworld.com/
|
||||
|
@ -116,6 +117,30 @@ where
|
|||
|
||||
Ok(context)
|
||||
}
|
||||
CliCommand::Logs {
|
||||
logs_dir,
|
||||
redact,
|
||||
swap_id,
|
||||
} => {
|
||||
let context = Arc::new(
|
||||
ContextBuilder::new(is_testnet)
|
||||
.with_data_dir(data)
|
||||
.with_debug(debug)
|
||||
.with_json(json)
|
||||
.build()
|
||||
.await?,
|
||||
);
|
||||
|
||||
GetLogsArgs {
|
||||
logs_dir,
|
||||
redact,
|
||||
swap_id,
|
||||
}
|
||||
.request(context.clone())
|
||||
.await?;
|
||||
|
||||
Ok(context)
|
||||
}
|
||||
CliCommand::Config => {
|
||||
let context = Arc::new(
|
||||
ContextBuilder::new(is_testnet)
|
||||
|
@ -366,6 +391,25 @@ enum CliCommand {
|
|||
},
|
||||
/// Show a list of past, ongoing and completed swaps
|
||||
History,
|
||||
/// Output all logging messages that have been issued.
|
||||
Logs {
|
||||
#[structopt(
|
||||
short = "d",
|
||||
help = "Print the logs from this directory instead of the default one."
|
||||
)]
|
||||
logs_dir: Option<PathBuf>,
|
||||
#[structopt(
|
||||
help = "Redact swap-ids, Bitcoin and Monero addresses.",
|
||||
long = "redact"
|
||||
)]
|
||||
redact: bool,
|
||||
#[structopt(
|
||||
long = "swap-id",
|
||||
help = "Filter for logs concerning this swap.",
|
||||
long_help = "This checks whether each logging message contains the swap id. Some messages might be skipped when they don't contain the swap id even though they're relevant."
|
||||
)]
|
||||
swap_id: Option<Uuid>,
|
||||
},
|
||||
#[structopt(about = "Prints the current config")]
|
||||
Config,
|
||||
#[structopt(about = "Allows withdrawing BTC from the internal Bitcoin wallet.")]
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use tracing::subscriber::set_global_default;
|
||||
use tracing::{Event, Level, Subscriber};
|
||||
use tracing_subscriber::fmt::format::{DefaultFields, Format, JsonFields};
|
||||
use tracing_subscriber::fmt::time::UtcTime;
|
||||
use tracing_subscriber::layer::{Context, SubscriberExt};
|
||||
use tracing_subscriber::{fmt, EnvFilter, Layer, Registry};
|
||||
|
||||
pub fn init(debug: bool, json: bool, dir: impl AsRef<Path>) -> Result<()> {
|
||||
let level_filter = EnvFilter::try_new("swap=debug")?;
|
||||
let registry = Registry::default().with(level_filter);
|
||||
|
||||
let appender = tracing_appender::rolling::never(dir.as_ref(), "swap-all.log");
|
||||
|
||||
let file_logger = registry.with(
|
||||
fmt::layer()
|
||||
.with_ansi(false)
|
||||
.with_target(false)
|
||||
.json()
|
||||
.with_writer(appender),
|
||||
);
|
||||
|
||||
if json && debug {
|
||||
set_global_default(file_logger.with(debug_json_terminal_printer()))?;
|
||||
} else if json && !debug {
|
||||
set_global_default(file_logger.with(info_json_terminal_printer()))?;
|
||||
} else if !json && debug {
|
||||
set_global_default(file_logger.with(debug_terminal_printer()))?;
|
||||
} else {
|
||||
set_global_default(file_logger.with(info_terminal_printer()))?;
|
||||
}
|
||||
|
||||
tracing::info!("Logging initialized to {}", dir.as_ref().display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct StdErrPrinter<L> {
|
||||
inner: L,
|
||||
level: Level,
|
||||
}
|
||||
|
||||
type StdErrLayer<S, T> =
|
||||
fmt::Layer<S, DefaultFields, Format<fmt::format::Full, T>, fn() -> std::io::Stderr>;
|
||||
|
||||
type StdErrJsonLayer<S, T> =
|
||||
fmt::Layer<S, JsonFields, Format<fmt::format::Json, T>, fn() -> std::io::Stderr>;
|
||||
|
||||
fn debug_terminal_printer<S>() -> StdErrPrinter<StdErrLayer<S, UtcTime<Rfc3339>>> {
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
StdErrPrinter {
|
||||
inner: fmt::layer()
|
||||
.with_ansi(is_terminal)
|
||||
.with_target(false)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.with_writer(std::io::stderr),
|
||||
level: Level::DEBUG,
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_json_terminal_printer<S>() -> StdErrPrinter<StdErrJsonLayer<S, UtcTime<Rfc3339>>> {
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
StdErrPrinter {
|
||||
inner: fmt::layer()
|
||||
.with_ansi(is_terminal)
|
||||
.with_target(false)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.json()
|
||||
.with_writer(std::io::stderr),
|
||||
level: Level::DEBUG,
|
||||
}
|
||||
}
|
||||
|
||||
fn info_terminal_printer<S>() -> StdErrPrinter<StdErrLayer<S, ()>> {
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
StdErrPrinter {
|
||||
inner: fmt::layer()
|
||||
.with_ansi(is_terminal)
|
||||
.with_target(false)
|
||||
.with_level(false)
|
||||
.without_time()
|
||||
.with_writer(std::io::stderr),
|
||||
level: Level::INFO,
|
||||
}
|
||||
}
|
||||
|
||||
fn info_json_terminal_printer<S>() -> StdErrPrinter<StdErrJsonLayer<S, ()>> {
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
StdErrPrinter {
|
||||
inner: fmt::layer()
|
||||
.with_ansi(is_terminal)
|
||||
.with_target(false)
|
||||
.with_level(false)
|
||||
.without_time()
|
||||
.json()
|
||||
.with_writer(std::io::stderr),
|
||||
level: Level::INFO,
|
||||
}
|
||||
}
|
||||
|
||||
impl<L, S> Layer<S> for StdErrPrinter<L>
|
||||
where
|
||||
L: 'static + Layer<S>,
|
||||
S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
|
||||
{
|
||||
fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
|
||||
if self.level.ge(event.metadata().level()) {
|
||||
self.inner.on_event(event, ctx);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
use anyhow::anyhow;
|
||||
|
||||
const LATEST_RELEASE_URL: &str = "https://github.com/comit-network/xmr-btc-swap/releases/latest";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Version {
|
||||
Current,
|
||||
Available,
|
||||
}
|
||||
|
||||
/// Check the latest release from GitHub API.
|
||||
pub async fn check_latest_version(current_version: &str) -> anyhow::Result<Version> {
|
||||
let response = reqwest::get(LATEST_RELEASE_URL).await?;
|
||||
let e = "Failed to get latest release.";
|
||||
let download_url = response.url();
|
||||
let segments = download_url.path_segments().ok_or_else(|| anyhow!(e))?;
|
||||
let latest_version = segments.last().ok_or_else(|| anyhow!(e))?;
|
||||
|
||||
let result = if is_latest_version(current_version, latest_version) {
|
||||
Version::Current
|
||||
} else {
|
||||
tracing::warn!(%current_version, %latest_version, %download_url,
|
||||
"You are not on the latest version",
|
||||
);
|
||||
Version::Available
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// todo: naive implementation can be improved using semver
|
||||
fn is_latest_version(current: &str, latest: &str) -> bool {
|
||||
current == latest
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_compares_versions() {
|
||||
assert!(is_latest_version("0.10.2", "0.10.2"));
|
||||
assert!(!is_latest_version("0.10.2", "0.10.3"));
|
||||
assert!(!is_latest_version("0.10.2", "0.11.0"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "For local testing, makes http requests to github."]
|
||||
async fn it_compares_with_github() {
|
||||
let result = check_latest_version("0.11.0").await.unwrap();
|
||||
assert_eq!(result, Version::Available);
|
||||
|
||||
let result = check_latest_version("0.11.1").await.unwrap();
|
||||
assert_eq!(result, Version::Current);
|
||||
}
|
||||
}
|
221
swap/src/common/mod.rs
Normal file
221
swap/src/common/mod.rs
Normal file
|
@ -0,0 +1,221 @@
|
|||
pub mod tracing_util;
|
||||
|
||||
use std::{collections::HashMap, path::PathBuf};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use tokio::{
|
||||
fs::{read_dir, File},
|
||||
io::{AsyncBufReadExt, BufReader},
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
const LATEST_RELEASE_URL: &str = "https://github.com/comit-network/xmr-btc-swap/releases/latest";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Version {
|
||||
Current,
|
||||
Available,
|
||||
}
|
||||
|
||||
/// Check the latest release from GitHub API.
|
||||
pub async fn check_latest_version(current_version: &str) -> anyhow::Result<Version> {
|
||||
let response = reqwest::get(LATEST_RELEASE_URL).await?;
|
||||
let e = "Failed to get latest release.";
|
||||
let download_url = response.url();
|
||||
let segments = download_url.path_segments().ok_or_else(|| anyhow!(e))?;
|
||||
let latest_version = segments.last().ok_or_else(|| anyhow!(e))?;
|
||||
|
||||
let result = if is_latest_version(current_version, latest_version) {
|
||||
Version::Current
|
||||
} else {
|
||||
tracing::warn!(%current_version, %latest_version, %download_url,
|
||||
"You are not on the latest version",
|
||||
);
|
||||
Version::Available
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// todo: naive implementation can be improved using semver
|
||||
fn is_latest_version(current: &str, latest: &str) -> bool {
|
||||
current == latest
|
||||
}
|
||||
|
||||
/// helper macro for [`redact`]... eldrich sorcery
|
||||
/// the macro does in essence the following:
|
||||
/// 1. create a static regex automaton for the pattern
|
||||
/// 2. find all matching patterns using regex
|
||||
/// 3. create a placeholder for each distinct matching pattern
|
||||
/// 4. add the placeholder to the hashmap
|
||||
macro_rules! regex_find_placeholders {
|
||||
($pattern:expr, $create_placeholder:expr, $replacements:expr, $input:expr) => {{
|
||||
// compile the regex pattern
|
||||
static REGEX: once_cell::sync::Lazy<regex::Regex> = once_cell::sync::Lazy::new(|| {
|
||||
tracing::debug!("initializing regex");
|
||||
regex::Regex::new($pattern).expect("invalid regex pattern")
|
||||
});
|
||||
|
||||
// keep count of count patterns to generate distinct placeholders
|
||||
let mut counter: usize = 0;
|
||||
|
||||
// for every matched address check whether we already found it
|
||||
// and if we didn't, generate a placeholder for it
|
||||
for address in REGEX.find_iter($input) {
|
||||
if !$replacements.contains_key(address.as_str()) {
|
||||
#[allow(clippy::redundant_closure_call)]
|
||||
$replacements.insert(address.as_str().to_owned(), $create_placeholder(counter));
|
||||
counter += 1;
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Print the logs from the specified logs or from the default location
|
||||
/// to the specified path or the terminal.
|
||||
///
|
||||
/// If specified, filter by swap id or redact addresses.
|
||||
pub async fn get_logs(
|
||||
logs_dir: PathBuf,
|
||||
swap_id: Option<Uuid>,
|
||||
redact_addresses: bool,
|
||||
) -> anyhow::Result<Vec<String>> {
|
||||
tracing::debug!("reading logfiles from {}", logs_dir.display());
|
||||
|
||||
// get all files in the directory
|
||||
let mut log_files = read_dir(&logs_dir).await?;
|
||||
|
||||
let mut log_messages = Vec::new();
|
||||
// when we redact we need to store the placeholder
|
||||
let mut placeholders = HashMap::new();
|
||||
|
||||
// print all lines from every log file. TODO: sort files by date?
|
||||
while let Some(entry) = log_files.next_entry().await? {
|
||||
// get the file path
|
||||
let file_path = entry.path();
|
||||
|
||||
// filter for .log files
|
||||
let file_name = file_path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.unwrap_or("");
|
||||
|
||||
if !file_name.ends_with(".log") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// use BufReader to stay easy on memory and then read line by line
|
||||
let buf_reader = BufReader::new(File::open(&file_path).await?);
|
||||
let mut lines = buf_reader.lines();
|
||||
|
||||
// print each line, redacted if the flag is set
|
||||
while let Some(line) = lines.next_line().await? {
|
||||
// if we should filter by swap id, check if the line contains it
|
||||
if let Some(swap_id) = swap_id {
|
||||
// we only want lines which contain the swap id
|
||||
if !line.contains(&swap_id.to_string()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// redact if necessary
|
||||
let line = if redact_addresses {
|
||||
redact_with(&line, &mut placeholders)
|
||||
} else {
|
||||
line
|
||||
};
|
||||
// save redacted message
|
||||
log_messages.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(log_messages)
|
||||
}
|
||||
|
||||
/// Redact logs, etc. by replacing Bitcoin and Monero addresses
|
||||
/// with generic placeholders.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// use swap::common::redact;
|
||||
///
|
||||
/// let redacted = redact("a9165a1e-d26d-4b56-bf6d-ca9658825c44");
|
||||
/// assert_eq!(redacted, "<swap_id_0>");
|
||||
/// ```
|
||||
pub fn redact(input: &str) -> String {
|
||||
let mut replacements = HashMap::new();
|
||||
redact_with(input, &mut replacements)
|
||||
}
|
||||
|
||||
/// Same as [`redact`] but retrieves palceholders from and stores them
|
||||
/// in a specified hashmap.
|
||||
pub fn redact_with(input: &str, replacements: &mut HashMap<String, String>) -> String {
|
||||
// TODO: verify regex patterns
|
||||
const MONERO_ADDR_REGEX: &str = r#"[48][1-9A-HJ-NP-Za-km-z]{94}"#;
|
||||
const BITCOIN_ADDR_REGEX: &str = r#"\b[13][a-km-zA-HJ-NP-Z1-9]{25,34}\b"#;
|
||||
// Both XMR and BTC transactions have
|
||||
// a 64 bit hex id so they aren't distinguishible
|
||||
const TX_ID_REGEX: &str = r#"\b[a-fA-F0-9]{64}\b"#;
|
||||
const SWAP_ID_REGEX: &str =
|
||||
r#"\b[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89aAbB][a-f0-9]{3}-[a-f0-9]{12}\b"#;
|
||||
|
||||
// use the macro to find all addresses and generate placeholders
|
||||
// has to be a macro in order to create the regex automata only once.
|
||||
regex_find_placeholders!(
|
||||
MONERO_ADDR_REGEX,
|
||||
|count| format!("<monero_address_{count}>"),
|
||||
replacements,
|
||||
input
|
||||
);
|
||||
regex_find_placeholders!(
|
||||
BITCOIN_ADDR_REGEX,
|
||||
|count| format!("<bitcoin_address_{count}>"),
|
||||
replacements,
|
||||
input
|
||||
);
|
||||
regex_find_placeholders!(
|
||||
TX_ID_REGEX,
|
||||
|count| format!("<tx_id_{count}>"),
|
||||
replacements,
|
||||
input
|
||||
);
|
||||
regex_find_placeholders!(
|
||||
SWAP_ID_REGEX,
|
||||
|count| format!("<swap_id_{count}>"),
|
||||
replacements,
|
||||
input
|
||||
);
|
||||
|
||||
// allocate string variable to operate on
|
||||
let mut redacted = input.to_owned();
|
||||
|
||||
// Finally we go through the input string and replace each occurance of an
|
||||
// address we want to redact with the corresponding placeholder
|
||||
for (address, placeholder) in replacements.iter() {
|
||||
redacted = redacted.replace(address, placeholder);
|
||||
}
|
||||
|
||||
redacted
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_compares_versions() {
|
||||
assert!(is_latest_version("0.10.2", "0.10.2"));
|
||||
assert!(!is_latest_version("0.10.2", "0.10.3"));
|
||||
assert!(!is_latest_version("0.10.2", "0.11.0"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore = "For local testing, makes http requests to github."]
|
||||
async fn it_compares_with_github() {
|
||||
let result = check_latest_version("0.11.0").await.unwrap();
|
||||
assert_eq!(result, Version::Available);
|
||||
|
||||
let result = check_latest_version("0.11.1").await.unwrap();
|
||||
assert_eq!(result, Version::Current);
|
||||
}
|
||||
}
|
64
swap/src/common/tracing_util.rs
Normal file
64
swap/src/common/tracing_util.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use tracing_subscriber::filter::{Directive, LevelFilter};
|
||||
use tracing_subscriber::fmt::time::UtcTime;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
use tracing_subscriber::{fmt, EnvFilter, Layer};
|
||||
|
||||
/// Output formats for logging messages.
|
||||
pub enum Format {
|
||||
/// Standard, human readable format.
|
||||
Raw,
|
||||
/// JSON, machine readable format.
|
||||
Json,
|
||||
}
|
||||
|
||||
/// Initialize tracing and enable logging messages according to these options.
|
||||
/// Besides printing to `stdout`, this will append to a log file.
|
||||
/// Said file will contain JSON-formatted logs of all levels,
|
||||
/// disregarding the arguments to this function.
|
||||
pub fn init(level_filter: LevelFilter, format: Format, dir: impl AsRef<Path>) -> Result<()> {
|
||||
let env_filter = EnvFilter::from_default_env()
|
||||
.add_directive(Directive::from_str(&format!("asb={}", &level_filter))?)
|
||||
.add_directive(Directive::from_str(&format!("swap={}", &level_filter))?);
|
||||
|
||||
// file logger will always write in JSON format and with timestamps
|
||||
let file_appender = tracing_appender::rolling::never(&dir, "swap-all.log");
|
||||
|
||||
let file_layer = fmt::layer()
|
||||
.with_writer(file_appender)
|
||||
.with_ansi(false)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.with_target(false)
|
||||
.json()
|
||||
.with_filter(env_filter);
|
||||
|
||||
// terminal logger
|
||||
let is_terminal = atty::is(atty::Stream::Stderr);
|
||||
let terminal_layer = fmt::layer()
|
||||
.with_writer(std::io::stdout)
|
||||
.with_ansi(is_terminal)
|
||||
.with_timer(UtcTime::rfc_3339())
|
||||
.with_target(false);
|
||||
|
||||
// combine the layers and start logging, format with json if specified
|
||||
if let Format::Json = format {
|
||||
tracing_subscriber::registry()
|
||||
.with(file_layer)
|
||||
.with(terminal_layer.json().with_filter(level_filter))
|
||||
.init();
|
||||
} else {
|
||||
tracing_subscriber::registry()
|
||||
.with(file_layer)
|
||||
.with(terminal_layer.with_filter(level_filter))
|
||||
.init();
|
||||
}
|
||||
|
||||
// now we can use the tracing macros to log messages
|
||||
tracing::info!(%level_filter, logs_dir=%dir.as_ref().display(), "Initialized tracing");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -4,8 +4,8 @@ use crate::protocol::{Database, State};
|
|||
use anyhow::{anyhow, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use libp2p::{Multiaddr, PeerId};
|
||||
use sqlx::sqlite::Sqlite;
|
||||
use sqlx::{Pool, SqlitePool};
|
||||
use sqlx::sqlite::{Sqlite, SqliteConnectOptions};
|
||||
use sqlx::{ConnectOptions, Pool, SqlitePool};
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use time::OffsetDateTime;
|
||||
|
@ -21,9 +21,16 @@ impl SqliteDatabase {
|
|||
Self: std::marker::Sized,
|
||||
{
|
||||
let path_str = format!("sqlite:{}", path.as_ref().display());
|
||||
let pool = SqlitePool::connect(&path_str).await?;
|
||||
|
||||
let mut options = SqliteConnectOptions::from_str(&path_str)?;
|
||||
|
||||
options.disable_statement_logging();
|
||||
|
||||
let pool = SqlitePool::connect_with(options).await?;
|
||||
let mut sqlite = Self { pool };
|
||||
|
||||
sqlite.run_migrations().await?;
|
||||
|
||||
Ok(sqlite)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::bitcoin::bitcoin_address;
|
||||
use crate::cli::api::request::{
|
||||
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, GetCurrentSwapArgs, GetHistoryArgs,
|
||||
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, GetCurrentSwapArgs, GetHistoryArgs, GetLogsArgs,
|
||||
GetSwapInfoArgs, ListSellersArgs, MoneroRecoveryArgs, Request, ResumeSwapArgs,
|
||||
SuspendCurrentSwapArgs, WithdrawBtcArgs,
|
||||
};
|
||||
|
@ -48,6 +48,14 @@ pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
|
|||
.to_jsonrpsee_result()
|
||||
})?;
|
||||
|
||||
module.register_async_method("get_logs", |params_raw, context| async move {
|
||||
let params: GetLogsArgs = params_raw.parse()?;
|
||||
|
||||
let logs = params.request(context).await?;
|
||||
|
||||
Ok(logs)
|
||||
})?;
|
||||
|
||||
module.register_async_method("resume_swap", |params_raw, context| async move {
|
||||
let params: ResumeSwapArgs = params_raw.parse()?;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::fs::ensure_directory_exists;
|
||||
use ::bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
||||
use ::bitcoin::secp256k1::{self, SecretKey};
|
||||
use anyhow::{Context, Result};
|
||||
use bdk::bitcoin::util::bip32::ExtendedPrivKey;
|
||||
use bitcoin::hashes::{sha256, Hash, HashEngine};
|
||||
use bitcoin::secp256k1::constants::SECRET_KEY_SIZE;
|
||||
use bitcoin::secp256k1::{self, SecretKey};
|
||||
use libp2p::identity;
|
||||
use pem::{encode, Pem};
|
||||
use rand::prelude::*;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue