From 08fc521292ac4ab7fd9274e757ae7887780f94e4 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Tue, 28 May 2024 18:44:23 +0200 Subject: [PATCH 01/18] Add SDK global logger --- cli/Cargo.lock | 3 +- cli/Cargo.toml | 1 - cli/README.md | 11 ----- cli/src/main.rs | 23 +--------- lib/Cargo.lock | 33 +++++++++++++- lib/core/Cargo.toml | 2 + lib/core/src/sdk.rs | 105 ++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 140 insertions(+), 38 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index a839eddcc..b3fb6b5c2 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -395,7 +395,6 @@ dependencies = [ "bip39", "breez-liquid-sdk", "clap", - "env_logger 0.11.3", "log", "qrcode-rs", "rustyline", @@ -411,6 +410,8 @@ dependencies = [ "anyhow", "bip39", "boltz-client", + "chrono", + "env_logger 0.11.3", "flutter_rust_bridge", "futures-util", "glob", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a3ae7a017..c0669e330 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -10,7 +10,6 @@ anyhow = "1.0.80" bip39 = "2.0.0" breez-liquid-sdk = { path = "../lib/core" } clap = { version = "4.5.1", features = ["derive"] } -env_logger = "0.11" log = "0.4.20" qrcode-rs = { version = "0.1", default-features = false } rustyline = { version = "13.0.0", features = ["derive"] } diff --git a/cli/README.md b/cli/README.md index ebade1dad..77c59e71a 100644 --- a/cli/README.md +++ b/cli/README.md @@ -24,14 +24,3 @@ To specify a custom data directory, use ```bash cargo run -- --data-dir temp-dir ``` - -To set a custom log level, use - -```bash -RUST_LOG=info|debug|warn cargo run -``` - -To specify a file to pipe logs to, use -```bash -RUST_LOG=info|debug|warn cargo run -- --log-file /tmp/log -``` diff --git a/cli/src/main.rs b/cli/src/main.rs index 28d6db8aa..b97b83ce8 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,10 +1,7 @@ mod commands; mod persist; -use std::{ - fs::{self, OpenOptions}, - path::PathBuf, -}; +use std::{fs, path::PathBuf}; use anyhow::{anyhow, Result}; use breez_liquid_sdk::{ @@ -61,23 +58,7 @@ async fn main() -> Result<()> { let data_dir = PathBuf::from(&data_dir_str); fs::create_dir_all(&data_dir)?; - let log_path = args.log_file.unwrap_or( - data_dir - .join("cli.log") - .to_str() - .ok_or(anyhow!("Could not create log file"))? - .to_string(), - ); - let log_file = OpenOptions::new() - .create(true) - .append(true) - .open(log_path)?; - - env_logger::builder() - .target(env_logger::Target::Pipe(Box::new(log_file))) - .filter(None, log::LevelFilter::Debug) - .filter(Some("rustyline"), log::LevelFilter::Warn) - .init(); + LiquidSdk::init_logging(&data_dir_str, None)?; let persistence = CliPersistence { data_dir }; let history_file = &persistence.history_file(); diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 78de771eb..36f204340 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -514,6 +514,8 @@ dependencies = [ "anyhow", "bip39", "boltz-client", + "chrono", + "env_logger 0.11.3", "flutter_rust_bridge", "futures-util", "glob", @@ -894,6 +896,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + [[package]] name = "env_logger" version = "0.7.1" @@ -901,7 +913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" dependencies = [ "atty", - "humantime", + "humantime 1.3.0", "log", "regex", "termcolor", @@ -917,6 +929,19 @@ dependencies = [ "regex", ] +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime 2.1.0", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1344,6 +1369,12 @@ dependencies = [ "quick-error", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "1.3.1" diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 2609a959a..d12938b87 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -16,6 +16,8 @@ anyhow = { workspace = true } bip39 = { version = "2.0.0", features = ["serde"] } #boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "a05731cc33030ada9ae14afcafe0cded22842ba6" } boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-breez-latest-05-27" } +chrono = "0.4" +env_logger = "0.11" flutter_rust_bridge = { version = "=2.0.0-dev.36", features = ["chrono"], optional = true } log = "0.4.20" lwk_common = "0.5.1" diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 97c1a94ff..a69c9731c 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1,3 +1,8 @@ +use std::fs::OpenOptions; +use std::io::Write; +use std::time::Instant; +use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; + use anyhow::{anyhow, Result}; use boltz_client::network::Chain; use boltz_client::ToHex; @@ -11,8 +16,9 @@ use boltz_client::{ util::secrets::{LiquidSwapKey, Preimage, SwapKey}, Amount, Bolt11Invoice, ElementsAddress, Keypair, LBtcSwapScriptV2, SwapType, }; +use chrono::Local; use futures_util::SinkExt; -use log::{debug, error, info, warn}; +use log::{debug, error, info, warn, LevelFilter, Metadata, Record}; use lwk_common::{singlesig_desc, Signer, Singlesig}; use lwk_signer::{AnySigner, SwSigner}; use lwk_wollet::bitcoin::Witness; @@ -23,8 +29,6 @@ use lwk_wollet::{ BlockchainBackend, ElectrumClient, ElectrumUrl, ElementsNetwork, FsPersister, Wollet as LwkWollet, WolletDescriptor, }; -use std::time::Instant; -use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; use tokio::{net::TcpStream, sync::Mutex}; use tokio_tungstenite::{connect_async, tungstenite, MaybeTlsStream, WebSocketStream}; use url::Url; @@ -1336,6 +1340,101 @@ impl LiquidSdk { err: format!("Could not create LiquidSwapKey: {e:?}"), }) } + + /// Configures a global SDK logger that will log to file and will forward log events to + /// an optional application-specific logger. + /// + /// If called, it should be called before any SDK methods (for example, before `connect`). + /// + /// It must be called only once in the application lifecycle. Alternatively, If the application + /// already uses a globally-registered logger, this method shouldn't be called at all. + /// + /// ### Arguments + /// + /// - `log_dir`: Location where the the SDK log file will be created. The directory must already exist. + /// + /// - `app_logger`: Optional application logger. + /// + /// If the application is to use it's own logger, but would also like the SDK to log SDK-specific + /// log output to a file in the configured `log_dir`, then do not register the + /// app-specific logger as a global logger and instead call this method with the app logger as an arg. + /// + /// ### Errors + /// + /// An error is thrown if the log file cannot be created in the working directory. + /// + /// An error is thrown if a global logger is already configured. + pub fn init_logging(log_dir: &str, app_logger: Option>) -> Result<()> { + let target_log_file = Box::new( + OpenOptions::new() + .create(true) + .append(true) + .open(format!("{log_dir}/sdk.log")) + .map_err(|e| anyhow!("Can't create log file: {e}"))?, + ); + let logger = env_logger::Builder::new() + .target(env_logger::Target::Pipe(target_log_file)) + .parse_filters( + r#" + debug, + breez_liquid_sdk=debug, + electrum_client::raw_client=warn, + lwk_wollet=info, + rustls=warn, + rustyline=warn, + tungstenite=warn + "#, + ) + .format(|buf, record| { + writeln!( + buf, + "[{} {} {}:{}] {}", + Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), + record.level(), + record.module_path().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.args() + ) + }) + .build(); + + let global_logger = GlobalSdkLogger { + logger, + log_listener: app_logger, + }; + + log::set_boxed_logger(Box::new(global_logger)) + .map_err(|e| anyhow!("Failed to set global logger: {e}"))?; + log::set_max_level(LevelFilter::Trace); + + Ok(()) + } +} + +struct GlobalSdkLogger { + /// SDK internal logger, which logs to file + logger: env_logger::Logger, + /// Optional external log listener, that can receive a stream of log statements + log_listener: Option>, +} +impl log::Log for GlobalSdkLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::Level::Trace + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + self.logger.log(record); + + if let Some(s) = &self.log_listener.as_ref() { + if s.enabled(record.metadata()) { + s.log(record); + } + } + } + } + + fn flush(&self) {} } #[cfg(test)] From 776416e621dfb1236eb36db2adbf0366e1fdf372 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Tue, 28 May 2024 19:23:57 +0200 Subject: [PATCH 02/18] Add bindings --- lib/Cargo.lock | 1 + lib/Cargo.toml | 2 + lib/bindings/Cargo.toml | 3 +- lib/bindings/langs/react-native/Cargo.toml | 4 +- lib/bindings/src/breez_liquid_sdk.udl | 12 ++ lib/bindings/src/lib.rs | 44 +++++- lib/core/Cargo.toml | 4 +- lib/core/src/model.rs | 7 + lib/core/src/sdk.rs | 4 + .../breezliquidsdk/BreezLiquidSDKMapper.kt | 37 +++++ .../breezliquidsdk/BreezLiquidSDKModule.kt | 127 +++++++----------- .../ios/BreezLiquidSDKMapper.swift | 38 ++++++ packages/react-native/ios/RNBreezLiquidSDK.m | 6 + .../react-native/ios/RNBreezLiquidSDK.swift | 10 ++ packages/react-native/src/index.ts | 9 ++ 15 files changed, 221 insertions(+), 87 deletions(-) diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 36f204340..f7a842344 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -547,6 +547,7 @@ dependencies = [ "breez-liquid-sdk", "camino", "glob", + "log", "once_cell", "thiserror", "tokio", diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 6b7addef3..e608cf53e 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -32,6 +32,8 @@ version = "0.0.1" [workspace.dependencies] anyhow = "1.0" +log = "0.4.20" +once_cell = "1.19" thiserror = "1.0" uniffi = "0.27.1" uniffi_macros = "0.27.1" diff --git a/lib/bindings/Cargo.toml b/lib/bindings/Cargo.toml index 2ccd8636e..8656ac753 100644 --- a/lib/bindings/Cargo.toml +++ b/lib/bindings/Cargo.toml @@ -14,6 +14,7 @@ crate-type = ["staticlib", "cdylib", "lib"] [dependencies] anyhow = { workspace = true } breez-liquid-sdk = { path = "../core" } +log = { workspace = true } uniffi = { workspace = true, features = [ "bindgen-tests", "cli" ] } # Bindgen used by KMP, version has to match the one supported by KMP uniffi_bindgen = "0.25.2" @@ -21,7 +22,7 @@ uniffi-kotlin-multiplatform = { git = "https://gitlab.com/trixnity/uniffi-kotlin camino = "1.1.1" thiserror = { workspace = true } tokio = { version = "1", features = ["rt"] } -once_cell = "*" +once_cell = { workspace = true } [build-dependencies] uniffi = { workspace = true, features = [ "build" ] } diff --git a/lib/bindings/langs/react-native/Cargo.toml b/lib/bindings/langs/react-native/Cargo.toml index 5160c2432..40f2cf70a 100644 --- a/lib/bindings/langs/react-native/Cargo.toml +++ b/lib/bindings/langs/react-native/Cargo.toml @@ -13,14 +13,14 @@ uniffi = { version = "0.23.0", features = ["bindgen-tests", "cli"] } uniffi_bindgen = "0.23.0" uniffi_macros = "0.23.0" camino = "1.1.1" -log = "*" +log = { workspace = true } serde = "*" askama = { version = "0.11.1", default-features = false, features = ["config"] } toml = "0.5" clap = { version = "3.2.22", features = ["derive"] } heck = "0.4" paste = "1.0" -once_cell = "1.12" +once_cell = { workspace = true } [build-dependencies] uniffi_build = { version = "0.23.0" } diff --git a/lib/bindings/src/breez_liquid_sdk.udl b/lib/bindings/src/breez_liquid_sdk.udl index fc9b5693b..ffb2a65cd 100644 --- a/lib/bindings/src/breez_liquid_sdk.udl +++ b/lib/bindings/src/breez_liquid_sdk.udl @@ -117,9 +117,21 @@ callback interface EventListener { void on_event(LiquidSdkEvent e); }; +callback interface LogStream { + void log(LogEntry l); +}; + +dictionary LogEntry { + string line; + string level; +}; + namespace breez_liquid_sdk { [Throws=LiquidSdkError] BindingLiquidSdk connect(ConnectRequest req); + + [Throws=LiquidSdkError] + void set_log_stream(LogStream log_stream); }; interface BindingLiquidSdk { diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index 3926d6db0..e321f2b11 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -1,9 +1,12 @@ use std::sync::Arc; use anyhow::Result; +use log::{Metadata, Record}; use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; -use once_cell::sync::Lazy; +use breez_liquid_sdk::sdk::LogStream; +use once_cell::sync::{Lazy, OnceCell}; use tokio::runtime::Runtime; +use uniffi::deps::log::{Level, LevelFilter}; static RT: Lazy = Lazy::new(|| Runtime::new().unwrap()); @@ -11,6 +14,45 @@ fn rt() -> &'static Runtime { &RT } +static LOG_INIT: OnceCell = OnceCell::new(); + +struct BindingLogger { + log_stream: Box, +} + +impl BindingLogger { + fn init(log_stream: Box) { + let binding_logger = BindingLogger { log_stream }; + log::set_boxed_logger(Box::new(binding_logger)).unwrap(); + log::set_max_level(LevelFilter::Trace); + } +} + +impl log::Log for BindingLogger { + fn enabled(&self, m: &Metadata) -> bool { + // ignore the internal uniffi log to prevent infinite loop. + return m.level() <= Level::Trace && *m.target() != *"breez_liquid_sdk::breez_liquid_sdk_bindings"; + } + + fn log(&self, record: &Record) { + self.log_stream.log(LogEntry { + line: record.args().to_string(), + level: record.level().as_str().to_string(), + }); + } + fn flush(&self) {} +} + + +/// If used, this must be called before `connect` +pub fn set_log_stream(log_stream: Box) -> Result<(), LiquidSdkError> { + LOG_INIT.set(true).map_err(|_| LiquidSdkError::Generic { + err: "Log stream already created".into(), + })?; + BindingLogger::init(log_stream); + Ok(()) +} + pub fn connect(req: ConnectRequest) -> Result, LiquidSdkError> { rt().block_on(async { let sdk = LiquidSdk::connect(req).await?; diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index d12938b87..8b4579649 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -19,7 +19,7 @@ boltz-client = { git = "https://github.com/ok300/boltz-rust", branch = "ok300-br chrono = "0.4" env_logger = "0.11" flutter_rust_bridge = { version = "=2.0.0-dev.36", features = ["chrono"], optional = true } -log = "0.4.20" +log = { workspace = true } lwk_common = "0.5.1" lwk_signer = "0.5.1" # Switch back to published version once this PR is merged: https://github.com/Blockstream/lwk/pull/34 @@ -31,7 +31,7 @@ serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.116" thiserror = { workspace = true } tokio-tungstenite = { version = "0.21.0", features = ["native-tls-vendored"] } -once_cell = "1" +once_cell = { workspace = true } openssl = { version = "0.10", features = ["vendored"] } tokio = { version = "1", features = ["rt"] } url = "2.5.0" diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 590d37116..3124a9233 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -546,6 +546,13 @@ impl Payment { } } +/// Internal SDK log entry +#[derive(Clone, Debug)] +pub struct LogEntry { + pub line: String, + pub level: String, +} + #[derive(Clone, Debug, Serialize, Deserialize)] struct InternalLeaf { pub output: String, diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index a69c9731c..d94c28dc2 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1437,6 +1437,10 @@ impl log::Log for GlobalSdkLogger { fn flush(&self) {} } +pub trait LogStream: Send + Sync { + fn log(&self, l: LogEntry); +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt index 44243f8c3..3fc6ff7ef 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt @@ -152,6 +152,43 @@ fun asGetInfoResponseList(arr: ReadableArray): List { return list } +fun asLogEntry(logEntry: ReadableMap): LogEntry? { + if (!validateMandatoryFields( + logEntry, + arrayOf( + "line", + "level", + ), + ) + ) { + return null + } + val line = logEntry.getString("line")!! + val level = logEntry.getString("level")!! + return LogEntry( + line, + level, + ) +} + +fun readableMapOf(logEntry: LogEntry): ReadableMap { + return readableMapOf( + "line" to logEntry.line, + "level" to logEntry.level, + ) +} + +fun asLogEntryList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toArrayList()) { + when (value) { + is ReadableMap -> list.add(asLogEntry(value)!!) + else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}")) + } + } + return list +} + fun asPayment(payment: ReadableMap): Payment? { if (!validateMandatoryFields( payment, diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt index 78f30e11c..d6c38f6ad 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt @@ -3,10 +3,12 @@ package com.breezliquidsdk import breez_liquid_sdk.* import com.facebook.react.bridge.* import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter +import java.io.File import java.util.* import java.util.concurrent.ExecutorService import java.util.concurrent.Executors + class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { private lateinit var executor: ExecutorService private var bindingLiquidSdk: BindingLiquidSdk? = null @@ -40,11 +42,22 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext @ReactMethod fun removeListeners(count: Int) {} + + @ReactMethod + fun setLogStream(logStream: , promise: Promise) { + executor.execute { + try { + setLogStream(logStream) + promise.resolve(readableMapOf("status" to "ok")) + } catch (e: Exception) { + promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) + } + } + } + + @ReactMethod - fun connect( - req: ReadableMap, - promise: Promise, - ) { + fun connect(req: ReadableMap, promise: Promise) { if (bindingLiquidSdk != null) { promise.reject("Generic", "Already initialized") return @@ -52,13 +65,8 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext executor.execute { try { - var connectRequest = - asConnectRequest( - req, - ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) } - connectRequest.dataDir = connectRequest.dataDir?.takeUnless { - it.isEmpty() - } ?: run { reactApplicationContext.filesDir.toString() + "/breezLiquidSdk" } + var connectRequest = asConnectRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) } + connectRequest.dataDir = connectRequest.dataDir?.takeUnless { it.isEmpty() } ?: run { reactApplicationContext.filesDir.toString() + "/breezLiquidSdk" } bindingLiquidSdk = connect(connectRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { @@ -83,11 +91,9 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } + @ReactMethod - fun removeEventListener( - id: String, - promise: Promise, - ) { + fun removeEventListener(id: String, promise: Promise) { executor.execute { try { getBindingLiquidSdk().removeEventListener(id) @@ -97,18 +103,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun getInfo( - req: ReadableMap, - promise: Promise, - ) { + fun getInfo(req: ReadableMap, promise: Promise) { executor.execute { try { - val getInfoRequest = - asGetInfoRequest( - req, - ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "GetInfoRequest")) } + val getInfoRequest = asGetInfoRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "GetInfoRequest")) } val res = getBindingLiquidSdk().getInfo(getInfoRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -116,18 +116,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun prepareSendPayment( - req: ReadableMap, - promise: Promise, - ) { + fun prepareSendPayment(req: ReadableMap, promise: Promise) { executor.execute { try { - val prepareSendRequest = - asPrepareSendRequest(req) ?: run { - throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendRequest")) - } + val prepareSendRequest = asPrepareSendRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendRequest")) } val res = getBindingLiquidSdk().prepareSendPayment(prepareSendRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -135,18 +129,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun sendPayment( - req: ReadableMap, - promise: Promise, - ) { + fun sendPayment(req: ReadableMap, promise: Promise) { executor.execute { try { - val prepareSendResponse = - asPrepareSendResponse(req) ?: run { - throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendResponse")) - } + val prepareSendResponse = asPrepareSendResponse(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendResponse")) } val res = getBindingLiquidSdk().sendPayment(prepareSendResponse) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -154,18 +142,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun prepareReceivePayment( - req: ReadableMap, - promise: Promise, - ) { + fun prepareReceivePayment(req: ReadableMap, promise: Promise) { executor.execute { try { - val prepareReceiveRequest = - asPrepareReceiveRequest(req) ?: run { - throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) - } + val prepareReceiveRequest = asPrepareReceiveRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) } val res = getBindingLiquidSdk().prepareReceivePayment(prepareReceiveRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -173,18 +155,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun receivePayment( - req: ReadableMap, - promise: Promise, - ) { + fun receivePayment(req: ReadableMap, promise: Promise) { executor.execute { try { - val prepareReceiveResponse = - asPrepareReceiveResponse(req) ?: run { - throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveResponse")) - } + val prepareReceiveResponse = asPrepareReceiveResponse(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveResponse")) } val res = getBindingLiquidSdk().receivePayment(prepareReceiveResponse) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -192,7 +168,7 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod fun listPayments(promise: Promise) { executor.execute { @@ -204,7 +180,7 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod fun sync(promise: Promise) { executor.execute { @@ -216,18 +192,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun backup( - req: ReadableMap, - promise: Promise, - ) { + fun backup(req: ReadableMap, promise: Promise) { executor.execute { try { - val backupRequest = - asBackupRequest( - req, - ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "BackupRequest")) } + val backupRequest = asBackupRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "BackupRequest")) } getBindingLiquidSdk().backup(backupRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { @@ -235,18 +205,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun restore( - req: ReadableMap, - promise: Promise, - ) { + fun restore(req: ReadableMap, promise: Promise) { executor.execute { try { - val restoreRequest = - asRestoreRequest( - req, - ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "RestoreRequest")) } + val restoreRequest = asRestoreRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "RestoreRequest")) } getBindingLiquidSdk().restore(restoreRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { @@ -254,4 +218,5 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } -} + +} \ No newline at end of file diff --git a/packages/react-native/ios/BreezLiquidSDKMapper.swift b/packages/react-native/ios/BreezLiquidSDKMapper.swift index 640ab3893..e38324010 100644 --- a/packages/react-native/ios/BreezLiquidSDKMapper.swift +++ b/packages/react-native/ios/BreezLiquidSDKMapper.swift @@ -167,6 +167,44 @@ enum BreezLiquidSDKMapper { return getInfoResponseList.map { v -> [String: Any?] in dictionaryOf(getInfoResponse: v) } } + static func asLogEntry(logEntry: [String: Any?]) throws -> LogEntry { + guard let line = logEntry["line"] as? String else { + throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "line", typeName: "LogEntry")) + } + guard let level = logEntry["level"] as? String else { + throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "level", typeName: "LogEntry")) + } + + return LogEntry( + line: line, + level: level + ) + } + + static func dictionaryOf(logEntry: LogEntry) -> [String: Any?] { + return [ + "line": logEntry.line, + "level": logEntry.level, + ] + } + + static func asLogEntryList(arr: [Any]) throws -> [LogEntry] { + var list = [LogEntry]() + for value in arr { + if let val = value as? [String: Any?] { + var logEntry = try asLogEntry(logEntry: val) + list.append(logEntry) + } else { + throw LiquidSdkError.Generic(message: errUnexpectedType(typeName: "LogEntry")) + } + } + return list + } + + static func arrayOf(logEntryList: [LogEntry]) -> [Any] { + return logEntryList.map { v -> [String: Any?] in dictionaryOf(logEntry: v) } + } + static func asPayment(payment: [String: Any?]) throws -> Payment { guard let txId = payment["txId"] as? String else { throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "txId", typeName: "Payment")) diff --git a/packages/react-native/ios/RNBreezLiquidSDK.m b/packages/react-native/ios/RNBreezLiquidSDK.m index a3294a1c1..8245f4e2d 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.m +++ b/packages/react-native/ios/RNBreezLiquidSDK.m @@ -2,6 +2,12 @@ #import @interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter) + +RCT_EXTERN_METHOD( + setLogStream: ()logStream + resolve: (RCTPromiseResolveBlock)resolve + reject: (RCTPromiseRejectBlock)reject +) RCT_EXTERN_METHOD( connect: (NSDictionary*)req diff --git a/packages/react-native/ios/RNBreezLiquidSDK.swift b/packages/react-native/ios/RNBreezLiquidSDK.swift index dff37dd38..bcb87bba8 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.swift +++ b/packages/react-native/ios/RNBreezLiquidSDK.swift @@ -56,6 +56,16 @@ class RNBreezLiquidSDK: RCTEventEmitter { throw LiquidSdkError.Generic(message: "Not initialized") } + @objc(setLogStream:resolve:reject:) + func setLogStream(_ logStream: LogStream, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + do { + try BreezLiquidSDK.setLogStream(logStream: logStream) + resolve(["status": "ok"]) + } catch let err { + rejectErr(err: err, reject: reject) + } + } + @objc(connect:resolve:reject:) func connect(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { if bindingLiquidSdk != nil { diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index f45248a10..1833355b6 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -40,6 +40,11 @@ export interface GetInfoResponse { pubkey: string } +export interface LogEntry { + line: string + level: string +} + export interface Payment { txId: string swapId?: string @@ -146,6 +151,10 @@ export const addEventListener = async (listener: EventListener): Promise return response } +export const setLogStream = async (logStream: LogStream): Promise => { + await BreezLiquidSDK.setLogStream(logStream) +} + export const removeEventListener = async (id: string): Promise => { await BreezLiquidSDK.removeEventListener(id) From e62fc0b1535815ae814a611b27e0b025965ed0c0 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 29 May 2024 18:01:21 +0200 Subject: [PATCH 03/18] Fix path to internal uniffi log --- lib/bindings/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index e321f2b11..04e2ae87f 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -1,9 +1,9 @@ use std::sync::Arc; use anyhow::Result; -use log::{Metadata, Record}; -use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; use breez_liquid_sdk::sdk::LogStream; +use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; +use log::{Metadata, Record}; use once_cell::sync::{Lazy, OnceCell}; use tokio::runtime::Runtime; use uniffi::deps::log::{Level, LevelFilter}; @@ -31,7 +31,7 @@ impl BindingLogger { impl log::Log for BindingLogger { fn enabled(&self, m: &Metadata) -> bool { // ignore the internal uniffi log to prevent infinite loop. - return m.level() <= Level::Trace && *m.target() != *"breez_liquid_sdk::breez_liquid_sdk_bindings"; + return m.level() <= Level::Trace && *m.target() != *"breez_liquid_sdk_bindings"; } fn log(&self, record: &Record) { @@ -43,7 +43,6 @@ impl log::Log for BindingLogger { fn flush(&self) {} } - /// If used, this must be called before `connect` pub fn set_log_stream(log_stream: Box) -> Result<(), LiquidSdkError> { LOG_INIT.set(true).map_err(|_| LiquidSdkError::Generic { From fe51e8297ed0a47011427518e086e2cefbb0bf2b Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Wed, 29 May 2024 18:08:29 +0200 Subject: [PATCH 04/18] Exclude "set_log_stream" from generated RN methods --- .../langs/react-native/src/gen_kotlin/mod.rs | 2 +- .../langs/react-native/src/gen_swift/mod.rs | 2 +- .../react-native/src/gen_typescript/mod.rs | 2 +- .../breezliquidsdk/BreezLiquidSDKModule.kt | 127 +++++++++++------- packages/react-native/ios/RNBreezLiquidSDK.m | 6 - .../react-native/ios/RNBreezLiquidSDK.swift | 10 -- packages/react-native/src/index.ts | 4 - 7 files changed, 84 insertions(+), 69 deletions(-) diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs b/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs index d7c88477f..383779c56 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs @@ -10,7 +10,7 @@ pub use uniffi_bindgen::bindings::kotlin::gen_kotlin::*; use crate::generator::RNConfig; static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { - let list: Vec<&str> = vec!["connect", "add_event_listener"]; + let list: Vec<&str> = vec!["connect", "add_event_listener", "set_log_stream"]; HashSet::from_iter(list.into_iter().map(|s| s.to_string())) }); diff --git a/lib/bindings/langs/react-native/src/gen_swift/mod.rs b/lib/bindings/langs/react-native/src/gen_swift/mod.rs index 1a06e2825..835557ef3 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_swift/mod.rs @@ -9,7 +9,7 @@ use crate::generator::RNConfig; pub use uniffi_bindgen::bindings::swift::gen_swift::*; static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { - let list: Vec<&str> = vec!["connect", "add_event_listener"]; + let list: Vec<&str> = vec!["connect", "add_event_listener", "set_log_stream"]; HashSet::from_iter(list.into_iter().map(|s| s.to_string())) }); diff --git a/lib/bindings/langs/react-native/src/gen_typescript/mod.rs b/lib/bindings/langs/react-native/src/gen_typescript/mod.rs index 851ecb794..7bc31773d 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/mod.rs @@ -26,7 +26,7 @@ static KEYWORDS: Lazy> = Lazy::new(|| { }); static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { - let list: Vec<&str> = vec!["connect", "add_event_listener"]; + let list: Vec<&str> = vec!["connect", "add_event_listener", "set_log_stream"]; HashSet::from_iter(list.into_iter().map(|s| s.to_string())) }); diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt index d6c38f6ad..78f30e11c 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt @@ -3,12 +3,10 @@ package com.breezliquidsdk import breez_liquid_sdk.* import com.facebook.react.bridge.* import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter -import java.io.File import java.util.* import java.util.concurrent.ExecutorService import java.util.concurrent.Executors - class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { private lateinit var executor: ExecutorService private var bindingLiquidSdk: BindingLiquidSdk? = null @@ -42,22 +40,11 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext @ReactMethod fun removeListeners(count: Int) {} - - @ReactMethod - fun setLogStream(logStream: , promise: Promise) { - executor.execute { - try { - setLogStream(logStream) - promise.resolve(readableMapOf("status" to "ok")) - } catch (e: Exception) { - promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) - } - } - } - - @ReactMethod - fun connect(req: ReadableMap, promise: Promise) { + fun connect( + req: ReadableMap, + promise: Promise, + ) { if (bindingLiquidSdk != null) { promise.reject("Generic", "Already initialized") return @@ -65,8 +52,13 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext executor.execute { try { - var connectRequest = asConnectRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) } - connectRequest.dataDir = connectRequest.dataDir?.takeUnless { it.isEmpty() } ?: run { reactApplicationContext.filesDir.toString() + "/breezLiquidSdk" } + var connectRequest = + asConnectRequest( + req, + ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "ConnectRequest")) } + connectRequest.dataDir = connectRequest.dataDir?.takeUnless { + it.isEmpty() + } ?: run { reactApplicationContext.filesDir.toString() + "/breezLiquidSdk" } bindingLiquidSdk = connect(connectRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { @@ -91,9 +83,11 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } - @ReactMethod - fun removeEventListener(id: String, promise: Promise) { + fun removeEventListener( + id: String, + promise: Promise, + ) { executor.execute { try { getBindingLiquidSdk().removeEventListener(id) @@ -103,12 +97,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun getInfo(req: ReadableMap, promise: Promise) { + fun getInfo( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val getInfoRequest = asGetInfoRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "GetInfoRequest")) } + val getInfoRequest = + asGetInfoRequest( + req, + ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "GetInfoRequest")) } val res = getBindingLiquidSdk().getInfo(getInfoRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -116,12 +116,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun prepareSendPayment(req: ReadableMap, promise: Promise) { + fun prepareSendPayment( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val prepareSendRequest = asPrepareSendRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendRequest")) } + val prepareSendRequest = + asPrepareSendRequest(req) ?: run { + throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendRequest")) + } val res = getBindingLiquidSdk().prepareSendPayment(prepareSendRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -129,12 +135,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun sendPayment(req: ReadableMap, promise: Promise) { + fun sendPayment( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val prepareSendResponse = asPrepareSendResponse(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendResponse")) } + val prepareSendResponse = + asPrepareSendResponse(req) ?: run { + throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareSendResponse")) + } val res = getBindingLiquidSdk().sendPayment(prepareSendResponse) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -142,12 +154,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun prepareReceivePayment(req: ReadableMap, promise: Promise) { + fun prepareReceivePayment( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val prepareReceiveRequest = asPrepareReceiveRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) } + val prepareReceiveRequest = + asPrepareReceiveRequest(req) ?: run { + throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveRequest")) + } val res = getBindingLiquidSdk().prepareReceivePayment(prepareReceiveRequest) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -155,12 +173,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun receivePayment(req: ReadableMap, promise: Promise) { + fun receivePayment( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val prepareReceiveResponse = asPrepareReceiveResponse(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveResponse")) } + val prepareReceiveResponse = + asPrepareReceiveResponse(req) ?: run { + throw LiquidSdkException.Generic(errMissingMandatoryField("req", "PrepareReceiveResponse")) + } val res = getBindingLiquidSdk().receivePayment(prepareReceiveResponse) promise.resolve(readableMapOf(res)) } catch (e: Exception) { @@ -168,7 +192,7 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod fun listPayments(promise: Promise) { executor.execute { @@ -180,7 +204,7 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod fun sync(promise: Promise) { executor.execute { @@ -192,12 +216,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun backup(req: ReadableMap, promise: Promise) { + fun backup( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val backupRequest = asBackupRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "BackupRequest")) } + val backupRequest = + asBackupRequest( + req, + ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "BackupRequest")) } getBindingLiquidSdk().backup(backupRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { @@ -205,12 +235,18 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - + @ReactMethod - fun restore(req: ReadableMap, promise: Promise) { + fun restore( + req: ReadableMap, + promise: Promise, + ) { executor.execute { try { - val restoreRequest = asRestoreRequest(req) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "RestoreRequest")) } + val restoreRequest = + asRestoreRequest( + req, + ) ?: run { throw LiquidSdkException.Generic(errMissingMandatoryField("req", "RestoreRequest")) } getBindingLiquidSdk().restore(restoreRequest) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { @@ -218,5 +254,4 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } } - -} \ No newline at end of file +} diff --git a/packages/react-native/ios/RNBreezLiquidSDK.m b/packages/react-native/ios/RNBreezLiquidSDK.m index 8245f4e2d..a3294a1c1 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.m +++ b/packages/react-native/ios/RNBreezLiquidSDK.m @@ -2,12 +2,6 @@ #import @interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter) - -RCT_EXTERN_METHOD( - setLogStream: ()logStream - resolve: (RCTPromiseResolveBlock)resolve - reject: (RCTPromiseRejectBlock)reject -) RCT_EXTERN_METHOD( connect: (NSDictionary*)req diff --git a/packages/react-native/ios/RNBreezLiquidSDK.swift b/packages/react-native/ios/RNBreezLiquidSDK.swift index bcb87bba8..dff37dd38 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.swift +++ b/packages/react-native/ios/RNBreezLiquidSDK.swift @@ -56,16 +56,6 @@ class RNBreezLiquidSDK: RCTEventEmitter { throw LiquidSdkError.Generic(message: "Not initialized") } - @objc(setLogStream:resolve:reject:) - func setLogStream(_ logStream: LogStream, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { - do { - try BreezLiquidSDK.setLogStream(logStream: logStream) - resolve(["status": "ok"]) - } catch let err { - rejectErr(err: err, reject: reject) - } - } - @objc(connect:resolve:reject:) func connect(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { if bindingLiquidSdk != nil { diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 1833355b6..db49c339b 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -151,10 +151,6 @@ export const addEventListener = async (listener: EventListener): Promise return response } -export const setLogStream = async (logStream: LogStream): Promise => { - await BreezLiquidSDK.setLogStream(logStream) -} - export const removeEventListener = async (id: string): Promise => { await BreezLiquidSDK.removeEventListener(id) From 4569a56d54250cb76fb6240819c918c1f9c3c99e Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 30 May 2024 14:18:16 +0200 Subject: [PATCH 05/18] Move logger-specific structs to a separate module --- lib/bindings/src/lib.rs | 2 +- lib/core/src/lib.rs | 1 + lib/core/src/logger.rs | 32 ++++++++++++++++++++++++++++++++ lib/core/src/sdk.rs | 33 ++------------------------------- 4 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 lib/core/src/logger.rs diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index 6a5491c16..bb23d9b17 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use anyhow::Result; -use breez_liquid_sdk::sdk::LogStream; +use breez_liquid_sdk::logger::LogStream; use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; use log::{Metadata, Record}; use once_cell::sync::{Lazy, OnceCell}; diff --git a/lib/core/src/lib.rs b/lib/core/src/lib.rs index 113df0ffa..645ed379d 100644 --- a/lib/core/src/lib.rs +++ b/lib/core/src/lib.rs @@ -5,6 +5,7 @@ pub mod error; pub(crate) mod event; #[cfg(feature = "frb")] pub mod frb; +pub mod logger; pub mod model; pub mod persist; pub mod sdk; diff --git a/lib/core/src/logger.rs b/lib/core/src/logger.rs new file mode 100644 index 000000000..a09bac869 --- /dev/null +++ b/lib/core/src/logger.rs @@ -0,0 +1,32 @@ +use crate::model::LogEntry; +use log::{Metadata, Record}; + +pub(crate) struct GlobalSdkLogger { + /// SDK internal logger, which logs to file + pub(crate) logger: env_logger::Logger, + /// Optional external log listener, that can receive a stream of log statements + pub(crate) log_listener: Option>, +} +impl log::Log for GlobalSdkLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= log::Level::Trace + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + self.logger.log(record); + + if let Some(s) = &self.log_listener.as_ref() { + if s.enabled(record.metadata()) { + s.log(record); + } + } + } + } + + fn flush(&self) {} +} + +pub trait LogStream: Send + Sync { + fn log(&self, l: LogEntry); +} diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index f5053f5e2..449dc193f 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -18,7 +18,7 @@ use boltz_client::{ }; use chrono::Local; use futures_util::SinkExt; -use log::{debug, error, info, warn, LevelFilter, Metadata, Record}; +use log::{debug, error, info, warn, LevelFilter}; use lwk_common::{singlesig_desc, Signer, Singlesig}; use lwk_signer::{AnySigner, SwSigner}; use lwk_wollet::bitcoin::Witness; @@ -35,6 +35,7 @@ use tokio_tungstenite::{connect_async, tungstenite, MaybeTlsStream, WebSocketStr use url::Url; use crate::error::LiquidSdkError; +use crate::logger::GlobalSdkLogger; use crate::model::PaymentState::*; use crate::{ boltz_status_stream::BoltzStatusStream, @@ -1478,36 +1479,6 @@ impl LiquidSdk { } } -struct GlobalSdkLogger { - /// SDK internal logger, which logs to file - logger: env_logger::Logger, - /// Optional external log listener, that can receive a stream of log statements - log_listener: Option>, -} -impl log::Log for GlobalSdkLogger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= log::Level::Trace - } - - fn log(&self, record: &Record) { - if self.enabled(record.metadata()) { - self.logger.log(record); - - if let Some(s) = &self.log_listener.as_ref() { - if s.enabled(record.metadata()) { - s.log(record); - } - } - } - } - - fn flush(&self) {} -} - -pub trait LogStream: Send + Sync { - fn log(&self, l: LogEntry); -} - #[cfg(test)] mod tests { use anyhow::Result; From 0554ded76bd2d237d0fc4d66e303bd27320e604b Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 30 May 2024 14:44:49 +0200 Subject: [PATCH 06/18] Delegate init_logging to method in logger.rs --- lib/core/src/logger.rs | 54 +++++++++++++++++++++++++++++++++++++++++- lib/core/src/sdk.rs | 50 ++------------------------------------ 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/lib/core/src/logger.rs b/lib/core/src/logger.rs index a09bac869..6c766e151 100644 --- a/lib/core/src/logger.rs +++ b/lib/core/src/logger.rs @@ -1,5 +1,11 @@ +use std::fs::OpenOptions; +use std::io::Write; + +use anyhow::{anyhow, Result}; +use chrono::Local; +use log::{LevelFilter, Metadata, Record}; + use crate::model::LogEntry; -use log::{Metadata, Record}; pub(crate) struct GlobalSdkLogger { /// SDK internal logger, which logs to file @@ -27,6 +33,52 @@ impl log::Log for GlobalSdkLogger { fn flush(&self) {} } +pub(super) fn init_logging(log_dir: &str, app_logger: Option>) -> Result<()> { + let target_log_file = Box::new( + OpenOptions::new() + .create(true) + .append(true) + .open(format!("{log_dir}/sdk.log")) + .map_err(|e| anyhow!("Can't create log file: {e}"))?, + ); + let logger = env_logger::Builder::new() + .target(env_logger::Target::Pipe(target_log_file)) + .parse_filters( + r#" + debug, + breez_liquid_sdk=debug, + electrum_client::raw_client=warn, + lwk_wollet=info, + rustls=warn, + rustyline=warn, + tungstenite=warn + "#, + ) + .format(|buf, record| { + writeln!( + buf, + "[{} {} {}:{}] {}", + Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), + record.level(), + record.module_path().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.args() + ) + }) + .build(); + + let global_logger = GlobalSdkLogger { + logger, + log_listener: app_logger, + }; + + log::set_boxed_logger(Box::new(global_logger)) + .map_err(|e| anyhow!("Failed to set global logger: {e}"))?; + log::set_max_level(LevelFilter::Trace); + + Ok(()) +} + pub trait LogStream: Send + Sync { fn log(&self, l: LogEntry); } diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 449dc193f..b0533f4bf 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1,5 +1,3 @@ -use std::fs::OpenOptions; -use std::io::Write; use std::time::Instant; use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; @@ -16,9 +14,8 @@ use boltz_client::{ util::secrets::{LiquidSwapKey, Preimage, SwapKey}, Amount, Bolt11Invoice, ElementsAddress, Keypair, LBtcSwapScriptV2, SwapType, }; -use chrono::Local; use futures_util::SinkExt; -use log::{debug, error, info, warn, LevelFilter}; +use log::{debug, error, info, warn}; use lwk_common::{singlesig_desc, Signer, Singlesig}; use lwk_signer::{AnySigner, SwSigner}; use lwk_wollet::bitcoin::Witness; @@ -35,7 +32,6 @@ use tokio_tungstenite::{connect_async, tungstenite, MaybeTlsStream, WebSocketStr use url::Url; use crate::error::LiquidSdkError; -use crate::logger::GlobalSdkLogger; use crate::model::PaymentState::*; use crate::{ boltz_status_stream::BoltzStatusStream, @@ -1433,49 +1429,7 @@ impl LiquidSdk { /// /// An error is thrown if a global logger is already configured. pub fn init_logging(log_dir: &str, app_logger: Option>) -> Result<()> { - let target_log_file = Box::new( - OpenOptions::new() - .create(true) - .append(true) - .open(format!("{log_dir}/sdk.log")) - .map_err(|e| anyhow!("Can't create log file: {e}"))?, - ); - let logger = env_logger::Builder::new() - .target(env_logger::Target::Pipe(target_log_file)) - .parse_filters( - r#" - debug, - breez_liquid_sdk=debug, - electrum_client::raw_client=warn, - lwk_wollet=info, - rustls=warn, - rustyline=warn, - tungstenite=warn - "#, - ) - .format(|buf, record| { - writeln!( - buf, - "[{} {} {}:{}] {}", - Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), - record.level(), - record.module_path().unwrap_or("unknown"), - record.line().unwrap_or(0), - record.args() - ) - }) - .build(); - - let global_logger = GlobalSdkLogger { - logger, - log_listener: app_logger, - }; - - log::set_boxed_logger(Box::new(global_logger)) - .map_err(|e| anyhow!("Failed to set global logger: {e}"))?; - log::set_max_level(LevelFilter::Trace); - - Ok(()) + crate::logger::init_logging(log_dir, app_logger) } } From 5467c6c5b345817d2182ff82b0fb6a94c0564e6c Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 30 May 2024 14:55:54 +0200 Subject: [PATCH 07/18] Rename uniffi BindingLogger to UniffiBindingLogger --- lib/bindings/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index bb23d9b17..5af318763 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -16,19 +16,19 @@ fn rt() -> &'static Runtime { static LOG_INIT: OnceCell = OnceCell::new(); -struct BindingLogger { +struct UniffiBindingLogger { log_stream: Box, } -impl BindingLogger { +impl UniffiBindingLogger { fn init(log_stream: Box) { - let binding_logger = BindingLogger { log_stream }; + let binding_logger = UniffiBindingLogger { log_stream }; log::set_boxed_logger(Box::new(binding_logger)).unwrap(); log::set_max_level(LevelFilter::Trace); } } -impl log::Log for BindingLogger { +impl log::Log for UniffiBindingLogger { fn enabled(&self, m: &Metadata) -> bool { // ignore the internal uniffi log to prevent infinite loop. return m.level() <= Level::Trace && *m.target() != *"breez_liquid_sdk_bindings"; @@ -48,7 +48,7 @@ pub fn set_log_stream(log_stream: Box) -> Result<(), LiquidSdkErr LOG_INIT.set(true).map_err(|_| LiquidSdkError::Generic { err: "Log stream already created".into(), })?; - BindingLogger::init(log_stream); + UniffiBindingLogger::init(log_stream); Ok(()) } From 3b57b5ca2c1c4629bd24bc6033363e125ca94e07 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Thu, 30 May 2024 15:07:30 +0200 Subject: [PATCH 08/18] Add set_log_stream for dart bindings --- lib/core/src/bindings.rs | 43 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index ebc105d6d..8cb94c6aa 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -1,10 +1,12 @@ use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; -use anyhow::Result; -use once_cell::sync::Lazy; +use anyhow::{anyhow, Result}; +use once_cell::sync::{Lazy, OnceCell}; use std::sync::Arc; +use log::{Level, LevelFilter, Metadata, Record}; use tokio::runtime::Runtime; static RT: Lazy = Lazy::new(|| Runtime::new().unwrap()); +static LOG_INIT: OnceCell = OnceCell::new(); pub(crate) fn rt() -> &'static Runtime { &RT @@ -20,6 +22,34 @@ impl EventListener for BindingEventListener { } } +struct BindingLogger { + log_stream: StreamSink, +} + +impl BindingLogger { + fn init(log_stream: StreamSink) { + let binding_logger = BindingLogger { log_stream }; + log::set_boxed_logger(Box::new(binding_logger)).unwrap(); + log::set_max_level(LevelFilter::Trace); + } +} + +impl log::Log for BindingLogger { + fn enabled(&self, m: &Metadata) -> bool { + m.level() <= Level::Trace + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + self.log_stream.add(LogEntry { + line: record.args().to_string(), + level: record.level().as_str().to_string(), + }); + } + } + fn flush(&self) {} +} + pub fn connect(req: ConnectRequest) -> Result { rt().block_on(async { let ln_sdk = LiquidSdk::connect(req).await?; @@ -27,6 +57,15 @@ pub fn connect(req: ConnectRequest) -> Result }) } +/// If used, this must be called before `connect`. It can only be called once. +pub fn set_log_stream(s: StreamSink) -> Result<()> { + LOG_INIT + .set(true) + .map_err(|_| anyhow!("Log stream already created"))?; + BindingLogger::init(s); + Ok(()) +} + pub struct BindingLiquidSdk { sdk: Arc, } From 0dc882bad7c5eb990d49784d44f01f271bdd8a96 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 10:02:39 +0200 Subject: [PATCH 09/18] Add SDK logger to Dart bindings --- .../include/breez_liquid_sdk.h | 9 ++ lib/core/src/bindings.rs | 43 ++++++++- lib/core/src/frb/bridge.io.rs | 55 +++++++++++ lib/core/src/frb/bridge.rs | 82 +++++++++++++++- lib/core/src/sdk.rs | 3 - packages/dart/lib/src/bindings.dart | 5 + packages/dart/lib/src/frb_generated.dart | 95 ++++++++++++++++++- packages/dart/lib/src/frb_generated.io.dart | 69 ++++++++++++++ packages/dart/lib/src/model.dart | 19 ++++ ...utter_breez_liquid_bindings_generated.dart | 23 +++++ 10 files changed, 397 insertions(+), 6 deletions(-) diff --git a/lib/bindings/langs/flutter/breez_liquid_sdk/include/breez_liquid_sdk.h b/lib/bindings/langs/flutter/breez_liquid_sdk/include/breez_liquid_sdk.h index 6fc4f6403..cd2ef3ccb 100644 --- a/lib/bindings/langs/flutter/breez_liquid_sdk/include/breez_liquid_sdk.h +++ b/lib/bindings/langs/flutter/breez_liquid_sdk/include/breez_liquid_sdk.h @@ -176,6 +176,11 @@ typedef struct wire_cst_ln_invoice { uint64_t min_final_cltv_expiry_delta; } wire_cst_ln_invoice; +typedef struct wire_cst_log_entry { + struct wire_cst_list_prim_u_8_strict *line; + struct wire_cst_list_prim_u_8_strict *level; +} wire_cst_log_entry; + typedef struct wire_cst_PaymentError_Generic { struct wire_cst_list_prim_u_8_strict *err; } wire_cst_PaymentError_Generic; @@ -260,6 +265,9 @@ void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment(in void frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync(int64_t port_, uintptr_t that); +void frbgen_breez_liquid_wire__crate__bindings__breez_log_stream(int64_t port_, + struct wire_cst_list_prim_u_8_strict *s); + void frbgen_breez_liquid_wire__crate__bindings__connect(int64_t port_, struct wire_cst_connect_request *req); @@ -327,6 +335,7 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_restore); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_send_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sync); + dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__breez_log_stream); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__connect); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire__crate__bindings__parse_invoice); dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index e22c7d6ac..591db50c3 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -1,8 +1,12 @@ use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; -use anyhow::Result; +use anyhow::{anyhow, Result}; use flutter_rust_bridge::frb; +use log::{Level, LevelFilter, Metadata, Record}; +use once_cell::sync::OnceCell; use std::sync::Arc; +static LOG_INIT: OnceCell = OnceCell::new(); + struct BindingEventListener { stream: StreamSink, } @@ -13,11 +17,48 @@ impl EventListener for BindingEventListener { } } +struct BindingLogger { + log_stream: StreamSink, +} + +impl BindingLogger { + fn init(log_stream: StreamSink) { + let binding_logger = BindingLogger { log_stream }; + log::set_boxed_logger(Box::new(binding_logger)).unwrap(); + log::set_max_level(LevelFilter::Trace); + } +} + +impl log::Log for BindingLogger { + fn enabled(&self, m: &Metadata) -> bool { + m.level() <= Level::Trace + } + + fn log(&self, record: &Record) { + if self.enabled(record.metadata()) { + let _ = self.log_stream.add(LogEntry { + line: record.args().to_string(), + level: record.level().as_str().to_string(), + }); + } + } + fn flush(&self) {} +} + pub async fn connect(req: ConnectRequest) -> Result { let ln_sdk = LiquidSdk::connect(req).await?; Ok(BindingLiquidSdk { sdk: ln_sdk }) } +/// If used, this must be called before `connect`. It can only be called once. +pub fn breez_log_stream(s: StreamSink) -> Result<()> { + LOG_INIT + .set(true) + .map_err(|_| anyhow!("Log stream already created"))?; + BindingLogger::init(s); + Ok(()) +} + pub fn parse_invoice(input: String) -> Result { LiquidSdk::parse_invoice(&input) } diff --git a/lib/core/src/frb/bridge.io.rs b/lib/core/src/frb/bridge.io.rs index 0e50a3530..f7f7b8ee1 100644 --- a/lib/core/src/frb/bridge.io.rs +++ b/lib/core/src/frb/bridge.io.rs @@ -15,6 +15,14 @@ flutter_rust_bridge::frb_generated_boilerplate_io!(); // Section: dart2rust +impl CstDecode + for *mut wire_cst_list_prim_u_8_strict +{ + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> flutter_rust_bridge::for_generated::anyhow::Error { + unimplemented!() + } +} impl CstDecode for usize { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> BindingLiquidSdk { @@ -54,6 +62,17 @@ impl StreamSink::deserialize(raw) } } +impl CstDecode> + for *mut wire_cst_list_prim_u_8_strict +{ + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode( + self, + ) -> StreamSink { + let raw: String = self.cst_decode(); + StreamSink::deserialize(raw) + } +} impl CstDecode for *mut wire_cst_list_prim_u_8_strict { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> String { @@ -286,6 +305,15 @@ impl CstDecode for wire_cst_ln_invoice { } } } +impl CstDecode for wire_cst_log_entry { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::model::LogEntry { + crate::model::LogEntry { + line: self.line.cst_decode(), + level: self.level.cst_decode(), + } + } +} impl CstDecode for wire_cst_payment { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::Payment { @@ -533,6 +561,19 @@ impl Default for wire_cst_ln_invoice { Self::new_with_null_ptr() } } +impl NewWithNullPtr for wire_cst_log_entry { + fn new_with_null_ptr() -> Self { + Self { + line: core::ptr::null_mut(), + level: core::ptr::null_mut(), + } + } +} +impl Default for wire_cst_log_entry { + fn default() -> Self { + Self::new_with_null_ptr() + } +} impl NewWithNullPtr for wire_cst_payment { fn new_with_null_ptr() -> Self { Self { @@ -786,6 +827,14 @@ pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_sy wire__crate__bindings__BindingLiquidSdk_sync_impl(port_, that) } +#[no_mangle] +pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__breez_log_stream( + port_: i64, + s: *mut wire_cst_list_prim_u_8_strict, +) { + wire__crate__bindings__breez_log_stream_impl(port_, s) +} + #[no_mangle] pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__connect( port_: i64, @@ -1076,6 +1125,12 @@ pub struct wire_cst_ln_invoice { } #[repr(C)] #[derive(Clone, Copy)] +pub struct wire_cst_log_entry { + line: *mut wire_cst_list_prim_u_8_strict, + level: *mut wire_cst_list_prim_u_8_strict, +} +#[repr(C)] +#[derive(Clone, Copy)] pub struct wire_cst_payment { tx_id: *mut wire_cst_list_prim_u_8_strict, swap_id: *mut wire_cst_list_prim_u_8_strict, diff --git a/lib/core/src/frb/bridge.rs b/lib/core/src/frb/bridge.rs index e68b20c70..456f9a43c 100644 --- a/lib/core/src/frb/bridge.rs +++ b/lib/core/src/frb/bridge.rs @@ -34,7 +34,7 @@ flutter_rust_bridge::frb_generated_boilerplate!( default_rust_auto_opaque = RustAutoOpaqueNom, ); pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.0.0-dev.36"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 84136112; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -532134055; // Section: executor @@ -360,6 +360,16 @@ let decode_indices_ = flutter_rust_bridge::for_generated::rust_auto_opaque_decod })().await) } }) } +fn wire__crate__bindings__breez_log_stream_impl( + port_: flutter_rust_bridge::for_generated::MessagePort, + s: impl CstDecode>, +) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::(flutter_rust_bridge::for_generated::TaskInfo{ debug_name: "breez_log_stream", port: Some(port_), mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal }, move || { let api_s = s.cst_decode(); move |context| { + transform_result_dco((move || { + crate::bindings::breez_log_stream(api_s) + })()) + } }) +} fn wire__crate__bindings__connect_impl( port_: flutter_rust_bridge::for_generated::MessagePort, req: impl CstDecode, @@ -469,6 +479,14 @@ impl CstDecode for usize { self } } +impl SseDecode for flutter_rust_bridge::for_generated::anyhow::Error { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return flutter_rust_bridge::for_generated::anyhow::anyhow!("{}", inner); + } +} + impl SseDecode for BindingLiquidSdk { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -499,6 +517,16 @@ impl SseDecode } } +impl SseDecode + for StreamSink +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut inner = ::sse_decode(deserializer); + return StreamSink::deserialize(inner); + } +} + impl SseDecode for String { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -724,6 +752,18 @@ impl SseDecode for crate::model::LNInvoice { } } +impl SseDecode for crate::model::LogEntry { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_line = ::sse_decode(deserializer); + let mut var_level = ::sse_decode(deserializer); + return crate::model::LogEntry { + line: var_line, + level: var_level, + }; + } +} + impl SseDecode for crate::model::Network { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { @@ -1203,6 +1243,22 @@ impl flutter_rust_bridge::IntoIntoDart for crate::model } } // Codec=Dco (DartCObject based), see doc to use other codecs +impl flutter_rust_bridge::IntoDart for crate::model::LogEntry { + fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { + [ + self.line.into_into_dart().into_dart(), + self.level.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::LogEntry {} +impl flutter_rust_bridge::IntoIntoDart for crate::model::LogEntry { + fn into_into_dart(self) -> crate::model::LogEntry { + self + } +} +// Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::model::Network { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { match self { @@ -1476,6 +1532,13 @@ impl flutter_rust_bridge::IntoIntoDart } } +impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(format!("{:?}", self), serializer); + } +} + impl SseEncode for BindingLiquidSdk { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1503,6 +1566,15 @@ impl SseEncode } } +impl SseEncode + for StreamSink +{ + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + unimplemented!("") + } +} + impl SseEncode for String { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { @@ -1668,6 +1740,14 @@ impl SseEncode for crate::model::LNInvoice { } } +impl SseEncode for crate::model::LogEntry { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.line, serializer); + ::sse_encode(self.level, serializer); + } +} + impl SseEncode for crate::model::Network { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 2faa3c467..c9f8b05bb 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -33,9 +33,6 @@ use lwk_wollet::{ BlockchainBackend, ElectrumClient, ElectrumUrl, ElementsNetwork, FsPersister, Wollet as LwkWollet, WolletDescriptor, }; -use std::time::Instant; -use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; -use tokio::net::TcpStream; use tokio::sync::{watch, Mutex, RwLock}; use crate::error::LiquidSdkError; diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index dcd32fc64..67db1b59b 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -9,10 +9,15 @@ import 'model.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // The type `BindingEventListener` is not used by any `pub` functions, thus it is ignored. +// The type `BindingLogger` is not used by any `pub` functions, thus it is ignored. Future connect({required ConnectRequest req, dynamic hint}) => RustLib.instance.api.crateBindingsConnect(req: req, hint: hint); +/// If used, this must be called before `connect`. It can only be called once. +Stream breezLogStream({dynamic hint}) => + RustLib.instance.api.crateBindingsBreezLogStream(hint: hint); + Future parseInvoice({required String input, dynamic hint}) => RustLib.instance.api.crateBindingsParseInvoice(input: input, hint: hint); diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 76329f2f8..59eb232b6 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -53,7 +53,7 @@ class RustLib extends BaseEntrypoint { String get codegenVersion => '2.0.0-dev.36'; @override - int get rustContentHash => 84136112; + int get rustContentHash => -532134055; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( stem: 'breez_liquid_sdk', @@ -96,6 +96,8 @@ abstract class RustLibApi extends BaseApi { Future crateBindingsBindingLiquidSdkSync({required BindingLiquidSdk that, dynamic hint}); + Stream crateBindingsBreezLogStream({dynamic hint}); + Future crateBindingsConnect({required ConnectRequest req, dynamic hint}); Future crateBindingsParseInvoice({required String input, dynamic hint}); @@ -434,6 +436,31 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["that"], ); + @override + Stream crateBindingsBreezLogStream({dynamic hint}) { + final s = RustStreamSink(); + unawaited(handler.executeNormal(NormalTask( + callFfi: (port_) { + var arg0 = cst_encode_StreamSink_log_entry_Dco(s); + return wire.wire__crate__bindings__breez_log_stream(port_, arg0); + }, + codec: DcoCodec( + decodeSuccessData: dco_decode_unit, + decodeErrorData: dco_decode_AnyhowException, + ), + constMeta: kCrateBindingsBreezLogStreamConstMeta, + argValues: [s], + apiImpl: this, + hint: hint, + ))); + return s.stream; + } + + TaskConstMeta get kCrateBindingsBreezLogStreamConstMeta => const TaskConstMeta( + debugName: "breez_log_stream", + argNames: ["s"], + ); + @override Future crateBindingsConnect({required ConnectRequest req, dynamic hint}) { return handler.executeNormal(NormalTask( @@ -487,6 +514,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_BindingLiquidSdk => wire .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk; + @protected + AnyhowException dco_decode_AnyhowException(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + return AnyhowException(raw as String); + } + @protected BindingLiquidSdk dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( @@ -516,6 +549,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { throw UnimplementedError(); } + @protected + RustStreamSink dco_decode_StreamSink_log_entry_Dco(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + throw UnimplementedError(); + } + @protected String dco_decode_String(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -736,6 +775,17 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { ); } + @protected + LogEntry dco_decode_log_entry(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); + return LogEntry( + line: dco_decode_String(arr[0]), + level: dco_decode_String(arr[1]), + ); + } + @protected Network dco_decode_network(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -960,6 +1010,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dcoDecodeU64(raw); } + @protected + AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var inner = sse_decode_String(deserializer); + return AnyhowException(inner); + } + @protected BindingLiquidSdk sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( @@ -989,6 +1046,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { throw UnimplementedError('Unreachable ()'); } + @protected + RustStreamSink sse_decode_StreamSink_log_entry_Dco(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + throw UnimplementedError('Unreachable ()'); + } + @protected String sse_decode_String(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1227,6 +1290,14 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { minFinalCltvExpiryDelta: var_minFinalCltvExpiryDelta); } + @protected + LogEntry sse_decode_log_entry(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_line = sse_decode_String(deserializer); + var var_level = sse_decode_String(deserializer); + return LogEntry(line: var_line, level: var_level); + } + @protected Network sse_decode_network(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1519,6 +1590,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw; } + @protected + void sse_encode_AnyhowException(AnyhowException self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.message, serializer); + } + @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( BindingLiquidSdk self, SseSerializer serializer) { @@ -1550,6 +1627,15 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { serializer); } + @protected + void sse_encode_StreamSink_log_entry_Dco(RustStreamSink self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String( + self.setupAndSerialize( + codec: DcoCodec(decodeSuccessData: dco_decode_log_entry, decodeErrorData: null)), + serializer); + } + @protected void sse_encode_String(String self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1750,6 +1836,13 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { sse_encode_u_64(self.minFinalCltvExpiryDelta, serializer); } + @protected + void sse_encode_log_entry(LogEntry self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_String(self.line, serializer); + sse_encode_String(self.level, serializer); + } + @protected void sse_encode_network(Network self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index dccc45dd3..85dbab940 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -23,6 +23,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_BindingLiquidSdkPtr => wire ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdkPtr; + @protected + AnyhowException dco_decode_AnyhowException(dynamic raw); + @protected BindingLiquidSdk dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( @@ -40,6 +43,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustStreamSink dco_decode_StreamSink_liquid_sdk_event_Dco(dynamic raw); + @protected + RustStreamSink dco_decode_StreamSink_log_entry_Dco(dynamic raw); + @protected String dco_decode_String(dynamic raw); @@ -112,6 +118,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice dco_decode_ln_invoice(dynamic raw); + @protected + LogEntry dco_decode_log_entry(dynamic raw); + @protected Network dco_decode_network(dynamic raw); @@ -175,6 +184,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); + @protected + AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); + @protected BindingLiquidSdk sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( @@ -192,6 +204,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected RustStreamSink sse_decode_StreamSink_liquid_sdk_event_Dco(SseDeserializer deserializer); + @protected + RustStreamSink sse_decode_StreamSink_log_entry_Dco(SseDeserializer deserializer); + @protected String sse_decode_String(SseDeserializer deserializer); @@ -264,6 +279,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected LNInvoice sse_decode_ln_invoice(SseDeserializer deserializer); + @protected + LogEntry sse_decode_log_entry(SseDeserializer deserializer); + @protected Network sse_decode_network(SseDeserializer deserializer); @@ -327,6 +345,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt sse_decode_usize(SseDeserializer deserializer); + @protected + ffi.Pointer cst_encode_AnyhowException(AnyhowException raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + throw UnimplementedError(); + } + @protected ffi.Pointer cst_encode_StreamSink_liquid_sdk_event_Dco( RustStreamSink raw) { @@ -335,6 +359,14 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { codec: DcoCodec(decodeSuccessData: dco_decode_liquid_sdk_event, decodeErrorData: null))); } + @protected + ffi.Pointer cst_encode_StreamSink_log_entry_Dco( + RustStreamSink raw) { + // Codec=Cst (C-struct based), see doc to use other codecs + return cst_encode_String(raw.setupAndSerialize( + codec: DcoCodec(decodeSuccessData: dco_decode_log_entry, decodeErrorData: null))); + } + @protected ffi.Pointer cst_encode_String(String raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -641,6 +673,12 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.min_final_cltv_expiry_delta = cst_encode_u_64(apiObj.minFinalCltvExpiryDelta); } + @protected + void cst_api_fill_to_wire_log_entry(LogEntry apiObj, wire_cst_log_entry wireObj) { + wireObj.line = cst_encode_String(apiObj.line); + wireObj.level = cst_encode_String(apiObj.level); + } + @protected void cst_api_fill_to_wire_payment(Payment apiObj, wire_cst_payment wireObj) { wireObj.tx_id = cst_encode_String(apiObj.txId); @@ -819,6 +857,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_encode_unit(void raw); + @protected + void sse_encode_AnyhowException(AnyhowException self, SseSerializer serializer); + @protected void sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( BindingLiquidSdk self, SseSerializer serializer); @@ -835,6 +876,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_StreamSink_liquid_sdk_event_Dco( RustStreamSink self, SseSerializer serializer); + @protected + void sse_encode_StreamSink_log_entry_Dco(RustStreamSink self, SseSerializer serializer); + @protected void sse_encode_String(String self, SseSerializer serializer); @@ -907,6 +951,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_ln_invoice(LNInvoice self, SseSerializer serializer); + @protected + void sse_encode_log_entry(LogEntry self, SseSerializer serializer); + @protected void sse_encode_network(Network self, SseSerializer serializer); @@ -1225,6 +1272,22 @@ class RustLibWire implements BaseWire { late final _wire__crate__bindings__BindingLiquidSdk_sync = _wire__crate__bindings__BindingLiquidSdk_syncPtr.asFunction(); + void wire__crate__bindings__breez_log_stream( + int port_, + ffi.Pointer s, + ) { + return _wire__crate__bindings__breez_log_stream( + port_, + s, + ); + } + + late final _wire__crate__bindings__breez_log_streamPtr = + _lookup)>>( + 'frbgen_breez_liquid_wire__crate__bindings__breez_log_stream'); + late final _wire__crate__bindings__breez_log_stream = _wire__crate__bindings__breez_log_streamPtr + .asFunction)>(); + void wire__crate__bindings__connect( int port_, ffi.Pointer req, @@ -1693,6 +1756,12 @@ final class wire_cst_ln_invoice extends ffi.Struct { external int min_final_cltv_expiry_delta; } +final class wire_cst_log_entry extends ffi.Struct { + external ffi.Pointer line; + + external ffi.Pointer level; +} + final class wire_cst_PaymentError_Generic extends ffi.Struct { external ffi.Pointer err; } diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index f45af88d4..2875ab77f 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -190,6 +190,25 @@ class LNInvoice { minFinalCltvExpiryDelta == other.minFinalCltvExpiryDelta; } +/// Internal SDK log entry +class LogEntry { + final String line; + final String level; + + const LogEntry({ + required this.line, + required this.level, + }); + + @override + int get hashCode => line.hashCode ^ level.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is LogEntry && runtimeType == other.runtimeType && line == other.line && level == other.level; +} + enum Network { /// Mainnet Bitcoin and Liquid chains mainnet, diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index d06830124..309cfb56e 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -260,6 +260,23 @@ class FlutterBreezLiquidBindings { _frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_syncPtr .asFunction(); + void frbgen_breez_liquid_wire__crate__bindings__breez_log_stream( + int port_, + ffi.Pointer s, + ) { + return _frbgen_breez_liquid_wire__crate__bindings__breez_log_stream( + port_, + s, + ); + } + + late final _frbgen_breez_liquid_wire__crate__bindings__breez_log_streamPtr = + _lookup)>>( + 'frbgen_breez_liquid_wire__crate__bindings__breez_log_stream'); + late final _frbgen_breez_liquid_wire__crate__bindings__breez_log_stream = + _frbgen_breez_liquid_wire__crate__bindings__breez_log_streamPtr + .asFunction)>(); + void frbgen_breez_liquid_wire__crate__bindings__connect( int port_, ffi.Pointer req, @@ -755,6 +772,12 @@ final class wire_cst_ln_invoice extends ffi.Struct { external int min_final_cltv_expiry_delta; } +final class wire_cst_log_entry extends ffi.Struct { + external ffi.Pointer line; + + external ffi.Pointer level; +} + final class wire_cst_PaymentError_Generic extends ffi.Struct { external ffi.Pointer err; } From d2c61b036ef1cc5b4c23664e45b4e2b17ca26010 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 10:05:51 +0200 Subject: [PATCH 10/18] Rename dart binding logger to DartBindingLogger --- lib/core/src/bindings.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 591db50c3..42d76d886 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -17,19 +17,19 @@ impl EventListener for BindingEventListener { } } -struct BindingLogger { +struct DartBindingLogger { log_stream: StreamSink, } -impl BindingLogger { +impl DartBindingLogger { fn init(log_stream: StreamSink) { - let binding_logger = BindingLogger { log_stream }; + let binding_logger = DartBindingLogger { log_stream }; log::set_boxed_logger(Box::new(binding_logger)).unwrap(); log::set_max_level(LevelFilter::Trace); } } -impl log::Log for BindingLogger { +impl log::Log for DartBindingLogger { fn enabled(&self, m: &Metadata) -> bool { m.level() <= Level::Trace } @@ -55,7 +55,7 @@ pub fn breez_log_stream(s: StreamSink) -> Result<()> { LOG_INIT .set(true) .map_err(|_| anyhow!("Log stream already created"))?; - BindingLogger::init(s); + DartBindingLogger::init(s); Ok(()) } From 7e87cbd83bb28e76eb70a250171a7ec6a3601f1f Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 10:13:06 +0200 Subject: [PATCH 11/18] Add rustdocs --- lib/bindings/src/lib.rs | 2 ++ lib/core/src/bindings.rs | 8 ++++++-- lib/core/src/model.rs | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index c7356c481..839d352f1 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -1,3 +1,5 @@ +//! Uniffi bindings + use std::sync::Arc; use anyhow::Result; diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 42d76d886..3ecd177e9 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -1,9 +1,13 @@ -use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; +//! Dart / flutter bindings + +use std::sync::Arc; + use anyhow::{anyhow, Result}; use flutter_rust_bridge::frb; use log::{Level, LevelFilter, Metadata, Record}; use once_cell::sync::OnceCell; -use std::sync::Arc; + +use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; static LOG_INIT: OnceCell = OnceCell::new(); diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 7fd353fa7..ce8d49c2f 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -567,7 +567,7 @@ impl Payment { } } -/// Internal SDK log entry +/// Internal SDK log entry used in the Uniffi and Dart bindings #[derive(Clone, Debug)] pub struct LogEntry { pub line: String, From 80b3b77c614676028c3019ee38bd1c1f25ae2050 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 11:18:33 +0200 Subject: [PATCH 12/18] RN bindings: Add manual handling for setLogStream() --- .../src/gen_kotlin/templates/module.kt | 16 +++++++++++++++- .../src/gen_swift/templates/extern.m | 5 +++++ .../src/gen_swift/templates/module.swift | 12 +++++++++++- .../src/gen_typescript/templates/Helpers.ts | 12 ++++++++++++ .../breezliquidsdk/BreezLiquidSDKLogStream.kt | 15 +++++++++++++++ .../ios/BreezLiquidSDKLogStream.swift | 13 +++++++++++++ 6 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt create mode 100644 packages/react-native/ios/BreezLiquidSDKLogStream.swift diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt index e83d9c420..d77ffbcab 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt @@ -47,7 +47,21 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext {%- if func.name()|ignored_function == false -%} {% include "TopLevelFunctionTemplate.kt" %} {% endif -%} - {%- endfor %} + {%- endfor %} + @ReactMethod + fun setLogStream(promise: Promise) { + executor.execute { + try { + val emitter = reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java) + + setLogStream(BreezLiquidSDKLogStream(emitter)) + promise.resolve(readableMapOf("status" to "ok")) + } catch (e: Exception) { + e.printStackTrace() + promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) + } + } + } @ReactMethod fun connect(req: ReadableMap, promise: Promise) { diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m b/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m index 1d2483ea9..eb210b2af 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m @@ -7,6 +7,11 @@ @interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter) {% include "ExternFunctionTemplate.m" %} {% endif %} {%- endfor %} +RCT_EXTERN_METHOD( + setLogStream: (RCTPromiseResolveBlock)resolve + reject: (RCTPromiseRejectBlock)reject +) + RCT_EXTERN_METHOD( connect: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift index 8a68d1d26..3daee2b4f 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift @@ -7,7 +7,7 @@ class RNBreezLiquidSDK: RCTEventEmitter { public static var emitter: RCTEventEmitter! public static var hasListeners: Bool = false - public static var supportedEvents: [String] = [] + public static var supportedEvents: [String] = ["breezLiquidSdkLog"] private var bindingLiquidSdk: BindingLiquidSdk! @@ -62,6 +62,16 @@ class RNBreezLiquidSDK: RCTEventEmitter { {% include "TopLevelFunctionTemplate.swift" %} {% endif -%} {%- endfor %} + @objc(setLogStream:reject:) + func setLogStream(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + do { + try BreezLiquidSDK.setLogStream(logStream: BreezLiquidSDKLogStream()) + resolve(["status": "ok"]) + } catch let err { + rejectErr(err: err, reject: reject) + } + } + @objc(connect:resolve:reject:) func connect(_ req:[String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { if bindingLiquidSdk != nil { diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts index 381ec21ba..bc7b6cee4 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts @@ -1,6 +1,8 @@ export type EventListener = (e: LiquidSdkEvent) => void +export type LogStream = (logEntry: LogEntry) => void + export const connect = async (req: ConnectRequest): Promise => { const response = await BreezLiquidSDK.connect(req) return response @@ -12,3 +14,13 @@ export const addEventListener = async (listener: EventListener): Promise return response } + +export const setLogStream = async (logStream: LogStream): Promise => { + const subscription = BreezLiquidSDKEmitter.addListener("breezLiquidSdkLog", logStream) + + try { + await BreezLiquidSDK.setLogStream() + } catch {} + + return subscription +} \ No newline at end of file diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt new file mode 100644 index 000000000..47dd36eed --- /dev/null +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt @@ -0,0 +1,15 @@ +package com.breezliquidsdk + +import breez_liquid_sdk.LogEntry +import breez_liquid_sdk.LogStream +import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter + +class BreezLiquidSDKLogStream(private val emitter: RCTDeviceEventEmitter) : LogStream { + companion object { + var emitterName = "breezLiquidSdkLog" + } + + override fun log(l: LogEntry) { + emitter.emit(emitterName, readableMapOf(l)) + } +} \ No newline at end of file diff --git a/packages/react-native/ios/BreezLiquidSDKLogStream.swift b/packages/react-native/ios/BreezLiquidSDKLogStream.swift new file mode 100644 index 000000000..241e75feb --- /dev/null +++ b/packages/react-native/ios/BreezLiquidSDKLogStream.swift @@ -0,0 +1,13 @@ +import Foundation +import BreezLiquidSDK + +class BreezLiquidSDKLogStream: LogStream { + static let emitterName: String = "breezLiquidSdkLog" + + func log(l: LogEntry) { + if RNBreezLiquidSDK.hasListeners { + RNBreezLiquidSDK.emitter.sendEvent(withName: BreezLiquidSDKLogStream.emitterName, + body: BreezLiquidSDKMapper.dictionaryOf(logEntry: l)) + } + } +} \ No newline at end of file From 6b35a0ec680fe671fc1f4944a17370a37d02c209 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 12:03:55 +0200 Subject: [PATCH 13/18] Re-generate dart bindings --- packages/dart/lib/src/bindings.dart | 2 +- packages/dart/lib/src/model.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index 67db1b59b..132fd90ae 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -9,7 +9,7 @@ import 'model.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart'; // The type `BindingEventListener` is not used by any `pub` functions, thus it is ignored. -// The type `BindingLogger` is not used by any `pub` functions, thus it is ignored. +// The type `DartBindingLogger` is not used by any `pub` functions, thus it is ignored. Future connect({required ConnectRequest req, dynamic hint}) => RustLib.instance.api.crateBindingsConnect(req: req, hint: hint); diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index e939cc242..ae193aa0c 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -190,7 +190,7 @@ class LNInvoice { minFinalCltvExpiryDelta == other.minFinalCltvExpiryDelta; } -/// Internal SDK log entry +/// Internal SDK log entry used in the Uniffi and Dart bindings class LogEntry { final String line; final String level; From 1f5298a951ec4fb024c50bae1c553276d9905899 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 12:04:24 +0200 Subject: [PATCH 14/18] Re-generate RN bindings --- .../breezliquidsdk/BreezLiquidSDKMapper.kt | 37 ++++++++++++++++++ .../breezliquidsdk/BreezLiquidSDKModule.kt | 15 ++++++++ .../ios/BreezLiquidSDKMapper.swift | 38 +++++++++++++++++++ packages/react-native/ios/RNBreezLiquidSDK.m | 5 +++ .../react-native/ios/RNBreezLiquidSDK.swift | 12 +++++- packages/react-native/src/index.ts | 17 +++++++++ 6 files changed, 123 insertions(+), 1 deletion(-) diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt index 3305a98de..7a9a8705d 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKMapper.kt @@ -226,6 +226,43 @@ fun asLnInvoiceList(arr: ReadableArray): List { return list } +fun asLogEntry(logEntry: ReadableMap): LogEntry? { + if (!validateMandatoryFields( + logEntry, + arrayOf( + "line", + "level", + ), + ) + ) { + return null + } + val line = logEntry.getString("line")!! + val level = logEntry.getString("level")!! + return LogEntry( + line, + level, + ) +} + +fun readableMapOf(logEntry: LogEntry): ReadableMap { + return readableMapOf( + "line" to logEntry.line, + "level" to logEntry.level, + ) +} + +fun asLogEntryList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toArrayList()) { + when (value) { + is ReadableMap -> list.add(asLogEntry(value)!!) + else -> throw LiquidSdkException.Generic(errUnexpectedType("${value::class.java.name}")) + } + } + return list +} + fun asPayment(payment: ReadableMap): Payment? { if (!validateMandatoryFields( payment, diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt index 4f0a3783f..889a90c2f 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt @@ -55,6 +55,21 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } } + @ReactMethod + fun setLogStream(promise: Promise) { + executor.execute { + try { + val emitter = reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java) + + setLogStream(BreezLiquidSDKLogStream(emitter)) + promise.resolve(readableMapOf("status" to "ok")) + } catch (e: Exception) { + e.printStackTrace() + promise.reject(e.javaClass.simpleName.replace("Exception", "Error"), e.message, e) + } + } + } + @ReactMethod fun connect( req: ReadableMap, diff --git a/packages/react-native/ios/BreezLiquidSDKMapper.swift b/packages/react-native/ios/BreezLiquidSDKMapper.swift index 11b3f3670..203bd63c5 100644 --- a/packages/react-native/ios/BreezLiquidSDKMapper.swift +++ b/packages/react-native/ios/BreezLiquidSDKMapper.swift @@ -271,6 +271,44 @@ enum BreezLiquidSDKMapper { return lnInvoiceList.map { v -> [String: Any?] in dictionaryOf(lnInvoice: v) } } + static func asLogEntry(logEntry: [String: Any?]) throws -> LogEntry { + guard let line = logEntry["line"] as? String else { + throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "line", typeName: "LogEntry")) + } + guard let level = logEntry["level"] as? String else { + throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "level", typeName: "LogEntry")) + } + + return LogEntry( + line: line, + level: level + ) + } + + static func dictionaryOf(logEntry: LogEntry) -> [String: Any?] { + return [ + "line": logEntry.line, + "level": logEntry.level, + ] + } + + static func asLogEntryList(arr: [Any]) throws -> [LogEntry] { + var list = [LogEntry]() + for value in arr { + if let val = value as? [String: Any?] { + var logEntry = try asLogEntry(logEntry: val) + list.append(logEntry) + } else { + throw LiquidSdkError.Generic(message: errUnexpectedType(typeName: "LogEntry")) + } + } + return list + } + + static func arrayOf(logEntryList: [LogEntry]) -> [Any] { + return logEntryList.map { v -> [String: Any?] in dictionaryOf(logEntry: v) } + } + static func asPayment(payment: [String: Any?]) throws -> Payment { guard let txId = payment["txId"] as? String else { throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "txId", typeName: "Payment")) diff --git a/packages/react-native/ios/RNBreezLiquidSDK.m b/packages/react-native/ios/RNBreezLiquidSDK.m index cf9d68582..c3f0205fd 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.m +++ b/packages/react-native/ios/RNBreezLiquidSDK.m @@ -9,6 +9,11 @@ @interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter) reject: (RCTPromiseRejectBlock)reject ) +RCT_EXTERN_METHOD( + setLogStream: (RCTPromiseResolveBlock)resolve + reject: (RCTPromiseRejectBlock)reject +) + RCT_EXTERN_METHOD( connect: (NSDictionary*)req resolve: (RCTPromiseResolveBlock)resolve diff --git a/packages/react-native/ios/RNBreezLiquidSDK.swift b/packages/react-native/ios/RNBreezLiquidSDK.swift index 166f20136..6bde421b1 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.swift +++ b/packages/react-native/ios/RNBreezLiquidSDK.swift @@ -7,7 +7,7 @@ class RNBreezLiquidSDK: RCTEventEmitter { public static var emitter: RCTEventEmitter! public static var hasListeners: Bool = false - public static var supportedEvents: [String] = [] + public static var supportedEvents: [String] = ["breezLiquidSdkLog"] private var bindingLiquidSdk: BindingLiquidSdk! @@ -66,6 +66,16 @@ class RNBreezLiquidSDK: RCTEventEmitter { } } + @objc(setLogStream:reject:) + func setLogStream(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + do { + try BreezLiquidSDK.setLogStream(logStream: BreezLiquidSDKLogStream()) + resolve(["status": "ok"]) + } catch let err { + rejectErr(err: err, reject: reject) + } + } + @objc(connect:resolve:reject:) func connect(_ req: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { if bindingLiquidSdk != nil { diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index df9acb294..5307ab147 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -55,6 +55,11 @@ export interface LnInvoice { minFinalCltvExpiryDelta: number } +export interface LogEntry { + line: string + level: string +} + export interface Payment { txId: string swapId?: string @@ -165,6 +170,8 @@ export enum PaymentType { export type EventListener = (e: LiquidSdkEvent) => void +export type LogStream = (logEntry: LogEntry) => void + export const connect = async (req: ConnectRequest): Promise => { const response = await BreezLiquidSDK.connect(req) return response @@ -176,6 +183,16 @@ export const addEventListener = async (listener: EventListener): Promise return response } + +export const setLogStream = async (logStream: LogStream): Promise => { + const subscription = BreezLiquidSDKEmitter.addListener("breezLiquidSdkLog", logStream) + + try { + await BreezLiquidSDK.setLogStream() + } catch {} + + return subscription +} export const parseInvoice = async (invoice: string): Promise => { const response = await BreezLiquidSDK.parseInvoice(invoice) return response From d6cea32e03b7e80e499154e63c338edba9a0b2f2 Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Fri, 31 May 2024 12:16:14 +0300 Subject: [PATCH 15/18] Remove LOG_INIT cell --- cli/Cargo.lock | 1 - lib/Cargo.lock | 1 - lib/bindings/src/lib.rs | 13 ++++++------- lib/core/Cargo.toml | 1 - lib/core/src/bindings.rs | 13 ++++--------- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 492499e1a..4b4c095bd 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -419,7 +419,6 @@ dependencies = [ "lwk_common", "lwk_signer", "lwk_wollet", - "once_cell", "openssl", "rusqlite", "rusqlite_migration", diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 06cfaab3a..41d617b4f 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -523,7 +523,6 @@ dependencies = [ "lwk_common", "lwk_signer", "lwk_wollet", - "once_cell", "openssl", "rusqlite", "rusqlite_migration", diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index 839d352f1..0b607724c 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -6,7 +6,7 @@ use anyhow::Result; use breez_liquid_sdk::logger::LogStream; use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; use log::{Metadata, Record}; -use once_cell::sync::{Lazy, OnceCell}; +use once_cell::sync::Lazy; use tokio::runtime::Runtime; use uniffi::deps::log::{Level, LevelFilter}; @@ -16,8 +16,6 @@ fn rt() -> &'static Runtime { &RT } -static LOG_INIT: OnceCell = OnceCell::new(); - struct UniffiBindingLogger { log_stream: Box, } @@ -25,7 +23,11 @@ struct UniffiBindingLogger { impl UniffiBindingLogger { fn init(log_stream: Box) { let binding_logger = UniffiBindingLogger { log_stream }; - log::set_boxed_logger(Box::new(binding_logger)).unwrap(); + log::set_boxed_logger(Box::new(binding_logger)) + .map_err(|_| LiquidSdkError::Generic { + err: "Log stream already created".into(), + }) + .unwrap(); log::set_max_level(LevelFilter::Trace); } } @@ -47,9 +49,6 @@ impl log::Log for UniffiBindingLogger { /// If used, this must be called before `connect` pub fn set_log_stream(log_stream: Box) -> Result<(), LiquidSdkError> { - LOG_INIT.set(true).map_err(|_| LiquidSdkError::Generic { - err: "Log stream already created".into(), - })?; UniffiBindingLogger::init(log_stream); Ok(()) } diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index e162f976f..54347b5b2 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -31,7 +31,6 @@ serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.116" thiserror = { workspace = true } tokio-tungstenite = { version = "0.21.0", features = ["native-tls-vendored"] } -once_cell = { workspace = true } openssl = { version = "0.10", features = ["vendored"] } tokio = { version = "1", features = ["rt", "macros"] } url = "2.5.0" diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 3ecd177e9..3b4c84b39 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -1,15 +1,11 @@ //! Dart / flutter bindings -use std::sync::Arc; - use anyhow::{anyhow, Result}; use flutter_rust_bridge::frb; use log::{Level, LevelFilter, Metadata, Record}; -use once_cell::sync::OnceCell; use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; - -static LOG_INIT: OnceCell = OnceCell::new(); +use std::sync::Arc; struct BindingEventListener { stream: StreamSink, @@ -28,7 +24,9 @@ struct DartBindingLogger { impl DartBindingLogger { fn init(log_stream: StreamSink) { let binding_logger = DartBindingLogger { log_stream }; - log::set_boxed_logger(Box::new(binding_logger)).unwrap(); + log::set_boxed_logger(Box::new(binding_logger)) + .map_err(|_| anyhow!("Log stream already created")) + .unwrap(); log::set_max_level(LevelFilter::Trace); } } @@ -56,9 +54,6 @@ pub async fn connect(req: ConnectRequest) -> Result) -> Result<()> { - LOG_INIT - .set(true) - .map_err(|_| anyhow!("Log stream already created"))?; DartBindingLogger::init(s); Ok(()) } From 080c4b4f73ec102ade0367e20c8dcac1e585089f Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Fri, 31 May 2024 12:36:00 +0300 Subject: [PATCH 16/18] Set global maximum log level once on initialization Return a LiquidSdkError::Generic instead of Anyhow error when initializing log stream on Dart bindings --- lib/bindings/src/lib.rs | 2 +- lib/core/src/bindings.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index 0b607724c..177b7ec54 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -24,11 +24,11 @@ impl UniffiBindingLogger { fn init(log_stream: Box) { let binding_logger = UniffiBindingLogger { log_stream }; log::set_boxed_logger(Box::new(binding_logger)) + .map(|_| log::set_max_level(LevelFilter::Trace)) .map_err(|_| LiquidSdkError::Generic { err: "Log stream already created".into(), }) .unwrap(); - log::set_max_level(LevelFilter::Trace); } } diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 3b4c84b39..86fdb431f 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -1,11 +1,12 @@ //! Dart / flutter bindings -use anyhow::{anyhow, Result}; +use std::sync::Arc; + +use anyhow::Result; use flutter_rust_bridge::frb; use log::{Level, LevelFilter, Metadata, Record}; use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; -use std::sync::Arc; struct BindingEventListener { stream: StreamSink, @@ -25,9 +26,11 @@ impl DartBindingLogger { fn init(log_stream: StreamSink) { let binding_logger = DartBindingLogger { log_stream }; log::set_boxed_logger(Box::new(binding_logger)) - .map_err(|_| anyhow!("Log stream already created")) + .map(|_| log::set_max_level(LevelFilter::Trace)) + .map_err(|_| LiquidSdkError::Generic { + err: "Log stream already created".into(), + }) .unwrap(); - log::set_max_level(LevelFilter::Trace); } } From 1d08ab22bfc8a66e2a1e94de6fbe43a5436e4d52 Mon Sep 17 00:00:00 2001 From: Erdem Yerebasmaz Date: Fri, 31 May 2024 13:04:24 +0300 Subject: [PATCH 17/18] Do not panic when initializing binding loggers --- lib/bindings/src/lib.rs | 14 ++++++-------- lib/core/src/bindings.rs | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index 177b7ec54..b1080ae68 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use anyhow::Result; use breez_liquid_sdk::logger::LogStream; use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; -use log::{Metadata, Record}; +use log::{Metadata, Record, SetLoggerError}; use once_cell::sync::Lazy; use tokio::runtime::Runtime; use uniffi::deps::log::{Level, LevelFilter}; @@ -21,14 +21,10 @@ struct UniffiBindingLogger { } impl UniffiBindingLogger { - fn init(log_stream: Box) { - let binding_logger = UniffiBindingLogger { log_stream }; + fn init(log_stream: Box) -> Result<(), SetLoggerError> { + let binding_logger: UniffiBindingLogger = UniffiBindingLogger { log_stream }; log::set_boxed_logger(Box::new(binding_logger)) .map(|_| log::set_max_level(LevelFilter::Trace)) - .map_err(|_| LiquidSdkError::Generic { - err: "Log stream already created".into(), - }) - .unwrap(); } } @@ -49,7 +45,9 @@ impl log::Log for UniffiBindingLogger { /// If used, this must be called before `connect` pub fn set_log_stream(log_stream: Box) -> Result<(), LiquidSdkError> { - UniffiBindingLogger::init(log_stream); + UniffiBindingLogger::init(log_stream).map_err(|_| LiquidSdkError::Generic { + err: "Log stream already created".into(), + })?; Ok(()) } diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index 86fdb431f..d2cac8686 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use anyhow::Result; use flutter_rust_bridge::frb; -use log::{Level, LevelFilter, Metadata, Record}; +use log::{Level, LevelFilter, Metadata, Record, SetLoggerError}; use crate::{error::*, frb::bridge::StreamSink, model::*, sdk::LiquidSdk}; @@ -23,14 +23,10 @@ struct DartBindingLogger { } impl DartBindingLogger { - fn init(log_stream: StreamSink) { - let binding_logger = DartBindingLogger { log_stream }; + fn init(log_stream: StreamSink) -> Result<(), SetLoggerError> { + let binding_logger: DartBindingLogger = DartBindingLogger { log_stream }; log::set_boxed_logger(Box::new(binding_logger)) .map(|_| log::set_max_level(LevelFilter::Trace)) - .map_err(|_| LiquidSdkError::Generic { - err: "Log stream already created".into(), - }) - .unwrap(); } } @@ -57,7 +53,9 @@ pub async fn connect(req: ConnectRequest) -> Result) -> Result<()> { - DartBindingLogger::init(s); + DartBindingLogger::init(s).map_err(|_| LiquidSdkError::Generic { + err: "Log stream already created".into(), + })?; Ok(()) } From 8d7f817090dc26dc4ea4c4cd3874be1aa29f2838 Mon Sep 17 00:00:00 2001 From: ok300 <106775972+ok300@users.noreply.github.com> Date: Fri, 31 May 2024 15:47:48 +0200 Subject: [PATCH 18/18] Rename LogStream to Logger --- .../langs/react-native/src/gen_kotlin/mod.rs | 2 +- .../src/gen_kotlin/templates/module.kt | 4 ++-- .../langs/react-native/src/gen_swift/mod.rs | 2 +- .../src/gen_swift/templates/extern.m | 2 +- .../src/gen_swift/templates/module.swift | 6 +++--- .../langs/react-native/src/gen_typescript/mod.rs | 2 +- .../src/gen_typescript/templates/Helpers.ts | 8 ++++---- lib/bindings/src/breez_liquid_sdk.udl | 4 ++-- lib/bindings/src/lib.rs | 16 ++++++++-------- lib/core/src/logger.rs | 2 +- .../breezliquidsdk/BreezLiquidSDKLogStream.kt | 4 ++-- .../com/breezliquidsdk/BreezLiquidSDKModule.kt | 4 ++-- .../ios/BreezLiquidSDKLogStream.swift | 4 ++-- packages/react-native/ios/RNBreezLiquidSDK.m | 2 +- packages/react-native/ios/RNBreezLiquidSDK.swift | 6 +++--- packages/react-native/src/index.ts | 8 ++++---- 16 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs b/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs index 383779c56..0eaf099c2 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_kotlin/mod.rs @@ -10,7 +10,7 @@ pub use uniffi_bindgen::bindings::kotlin::gen_kotlin::*; use crate::generator::RNConfig; static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { - let list: Vec<&str> = vec!["connect", "add_event_listener", "set_log_stream"]; + let list: Vec<&str> = vec!["connect", "add_event_listener", "set_logger"]; HashSet::from_iter(list.into_iter().map(|s| s.to_string())) }); diff --git a/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt b/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt index d77ffbcab..6bfb42ccc 100644 --- a/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt +++ b/lib/bindings/langs/react-native/src/gen_kotlin/templates/module.kt @@ -49,12 +49,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext {% endif -%} {%- endfor %} @ReactMethod - fun setLogStream(promise: Promise) { + fun setLogger(promise: Promise) { executor.execute { try { val emitter = reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java) - setLogStream(BreezLiquidSDKLogStream(emitter)) + setLogger(BreezLiquidSDKLogger(emitter)) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { e.printStackTrace() diff --git a/lib/bindings/langs/react-native/src/gen_swift/mod.rs b/lib/bindings/langs/react-native/src/gen_swift/mod.rs index 835557ef3..1ee706373 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_swift/mod.rs @@ -9,7 +9,7 @@ use crate::generator::RNConfig; pub use uniffi_bindgen::bindings::swift::gen_swift::*; static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { - let list: Vec<&str> = vec!["connect", "add_event_listener", "set_log_stream"]; + let list: Vec<&str> = vec!["connect", "add_event_listener", "set_logger"]; HashSet::from_iter(list.into_iter().map(|s| s.to_string())) }); diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m b/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m index eb210b2af..9441f0bf2 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/extern.m @@ -8,7 +8,7 @@ @interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter) {% endif %} {%- endfor %} RCT_EXTERN_METHOD( - setLogStream: (RCTPromiseResolveBlock)resolve + setLogger: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) diff --git a/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift b/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift index 3daee2b4f..87768279a 100644 --- a/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift +++ b/lib/bindings/langs/react-native/src/gen_swift/templates/module.swift @@ -62,10 +62,10 @@ class RNBreezLiquidSDK: RCTEventEmitter { {% include "TopLevelFunctionTemplate.swift" %} {% endif -%} {%- endfor %} - @objc(setLogStream:reject:) - func setLogStream(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { + @objc(setLogger:reject:) + func setLogger(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void { do { - try BreezLiquidSDK.setLogStream(logStream: BreezLiquidSDKLogStream()) + try BreezLiquidSDK.setLogger(Logger: BreezLiquidSDKLogger()) resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) diff --git a/lib/bindings/langs/react-native/src/gen_typescript/mod.rs b/lib/bindings/langs/react-native/src/gen_typescript/mod.rs index 7bc31773d..a8bb6912c 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/mod.rs +++ b/lib/bindings/langs/react-native/src/gen_typescript/mod.rs @@ -26,7 +26,7 @@ static KEYWORDS: Lazy> = Lazy::new(|| { }); static IGNORED_FUNCTIONS: Lazy> = Lazy::new(|| { - let list: Vec<&str> = vec!["connect", "add_event_listener", "set_log_stream"]; + let list: Vec<&str> = vec!["connect", "add_event_listener", "set_logger"]; HashSet::from_iter(list.into_iter().map(|s| s.to_string())) }); diff --git a/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts b/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts index bc7b6cee4..97a2443d1 100644 --- a/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts +++ b/lib/bindings/langs/react-native/src/gen_typescript/templates/Helpers.ts @@ -1,7 +1,7 @@ export type EventListener = (e: LiquidSdkEvent) => void -export type LogStream = (logEntry: LogEntry) => void +export type Logger = (logEntry: LogEntry) => void export const connect = async (req: ConnectRequest): Promise => { const response = await BreezLiquidSDK.connect(req) @@ -15,11 +15,11 @@ export const addEventListener = async (listener: EventListener): Promise return response } -export const setLogStream = async (logStream: LogStream): Promise => { - const subscription = BreezLiquidSDKEmitter.addListener("breezLiquidSdkLog", logStream) +export const setLogger = async (logger: Logger): Promise => { + const subscription = BreezLiquidSDKEmitter.addListener("breezLiquidSdkLog", logger) try { - await BreezLiquidSDK.setLogStream() + await BreezLiquidSDK.setLogger() } catch {} return subscription diff --git a/lib/bindings/src/breez_liquid_sdk.udl b/lib/bindings/src/breez_liquid_sdk.udl index 7b2f2ede0..e8d78ed03 100644 --- a/lib/bindings/src/breez_liquid_sdk.udl +++ b/lib/bindings/src/breez_liquid_sdk.udl @@ -150,7 +150,7 @@ callback interface EventListener { void on_event(LiquidSdkEvent e); }; -callback interface LogStream { +callback interface Logger { void log(LogEntry l); }; @@ -164,7 +164,7 @@ namespace breez_liquid_sdk { BindingLiquidSdk connect(ConnectRequest req); [Throws=LiquidSdkError] - void set_log_stream(LogStream log_stream); + void set_logger(Logger logger); [Throws=PaymentError] LNInvoice parse_invoice(string invoice); diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index b1080ae68..5933835a8 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use anyhow::Result; -use breez_liquid_sdk::logger::LogStream; +use breez_liquid_sdk::logger::Logger; use breez_liquid_sdk::{error::*, model::*, sdk::LiquidSdk}; use log::{Metadata, Record, SetLoggerError}; use once_cell::sync::Lazy; @@ -17,12 +17,12 @@ fn rt() -> &'static Runtime { } struct UniffiBindingLogger { - log_stream: Box, + logger: Box, } impl UniffiBindingLogger { - fn init(log_stream: Box) -> Result<(), SetLoggerError> { - let binding_logger: UniffiBindingLogger = UniffiBindingLogger { log_stream }; + fn init(logger: Box) -> Result<(), SetLoggerError> { + let binding_logger: UniffiBindingLogger = UniffiBindingLogger { logger }; log::set_boxed_logger(Box::new(binding_logger)) .map(|_| log::set_max_level(LevelFilter::Trace)) } @@ -35,7 +35,7 @@ impl log::Log for UniffiBindingLogger { } fn log(&self, record: &Record) { - self.log_stream.log(LogEntry { + self.logger.log(LogEntry { line: record.args().to_string(), level: record.level().as_str().to_string(), }); @@ -44,9 +44,9 @@ impl log::Log for UniffiBindingLogger { } /// If used, this must be called before `connect` -pub fn set_log_stream(log_stream: Box) -> Result<(), LiquidSdkError> { - UniffiBindingLogger::init(log_stream).map_err(|_| LiquidSdkError::Generic { - err: "Log stream already created".into(), +pub fn set_logger(logger: Box) -> Result<(), LiquidSdkError> { + UniffiBindingLogger::init(logger).map_err(|_| LiquidSdkError::Generic { + err: "Logger already created".into(), })?; Ok(()) } diff --git a/lib/core/src/logger.rs b/lib/core/src/logger.rs index 6c766e151..6ebf6a6df 100644 --- a/lib/core/src/logger.rs +++ b/lib/core/src/logger.rs @@ -79,6 +79,6 @@ pub(super) fn init_logging(log_dir: &str, app_logger: Option>) Ok(()) } -pub trait LogStream: Send + Sync { +pub trait Logger: Send + Sync { fn log(&self, l: LogEntry); } diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt index 47dd36eed..efabc8cdf 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKLogStream.kt @@ -1,10 +1,10 @@ package com.breezliquidsdk import breez_liquid_sdk.LogEntry -import breez_liquid_sdk.LogStream +import breez_liquid_sdk.Logger import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter -class BreezLiquidSDKLogStream(private val emitter: RCTDeviceEventEmitter) : LogStream { +class BreezLiquidSDKLogger(private val emitter: RCTDeviceEventEmitter) : Logger { companion object { var emitterName = "breezLiquidSdkLog" } diff --git a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt index 889a90c2f..c647bd1da 100644 --- a/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt +++ b/packages/react-native/android/src/main/java/com/breezliquidsdk/BreezLiquidSDKModule.kt @@ -56,12 +56,12 @@ class BreezLiquidSDKModule(reactContext: ReactApplicationContext) : ReactContext } @ReactMethod - fun setLogStream(promise: Promise) { + fun setLogger(promise: Promise) { executor.execute { try { val emitter = reactApplicationContext.getJSModule(RCTDeviceEventEmitter::class.java) - setLogStream(BreezLiquidSDKLogStream(emitter)) + setLogger(BreezLiquidSDKLogger(emitter)) promise.resolve(readableMapOf("status" to "ok")) } catch (e: Exception) { e.printStackTrace() diff --git a/packages/react-native/ios/BreezLiquidSDKLogStream.swift b/packages/react-native/ios/BreezLiquidSDKLogStream.swift index 241e75feb..a7f0749b1 100644 --- a/packages/react-native/ios/BreezLiquidSDKLogStream.swift +++ b/packages/react-native/ios/BreezLiquidSDKLogStream.swift @@ -1,12 +1,12 @@ import Foundation import BreezLiquidSDK -class BreezLiquidSDKLogStream: LogStream { +class BreezLiquidSDKLogger: Logger { static let emitterName: String = "breezLiquidSdkLog" func log(l: LogEntry) { if RNBreezLiquidSDK.hasListeners { - RNBreezLiquidSDK.emitter.sendEvent(withName: BreezLiquidSDKLogStream.emitterName, + RNBreezLiquidSDK.emitter.sendEvent(withName: BreezLiquidSDKLogger.emitterName, body: BreezLiquidSDKMapper.dictionaryOf(logEntry: l)) } } diff --git a/packages/react-native/ios/RNBreezLiquidSDK.m b/packages/react-native/ios/RNBreezLiquidSDK.m index c3f0205fd..0f3dded57 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.m +++ b/packages/react-native/ios/RNBreezLiquidSDK.m @@ -10,7 +10,7 @@ @interface RCT_EXTERN_MODULE(RNBreezLiquidSDK, RCTEventEmitter) ) RCT_EXTERN_METHOD( - setLogStream: (RCTPromiseResolveBlock)resolve + setLogger: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject ) diff --git a/packages/react-native/ios/RNBreezLiquidSDK.swift b/packages/react-native/ios/RNBreezLiquidSDK.swift index 6bde421b1..1a018a2f2 100644 --- a/packages/react-native/ios/RNBreezLiquidSDK.swift +++ b/packages/react-native/ios/RNBreezLiquidSDK.swift @@ -66,10 +66,10 @@ class RNBreezLiquidSDK: RCTEventEmitter { } } - @objc(setLogStream:reject:) - func setLogStream(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + @objc(setLogger:reject:) + func setLogger(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { do { - try BreezLiquidSDK.setLogStream(logStream: BreezLiquidSDKLogStream()) + try BreezLiquidSDK.setLogger(Logger: BreezLiquidSDKLogger()) resolve(["status": "ok"]) } catch let err { rejectErr(err: err, reject: reject) diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index 5307ab147..a08a6de95 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -170,7 +170,7 @@ export enum PaymentType { export type EventListener = (e: LiquidSdkEvent) => void -export type LogStream = (logEntry: LogEntry) => void +export type Logger = (logEntry: LogEntry) => void export const connect = async (req: ConnectRequest): Promise => { const response = await BreezLiquidSDK.connect(req) @@ -184,11 +184,11 @@ export const addEventListener = async (listener: EventListener): Promise return response } -export const setLogStream = async (logStream: LogStream): Promise => { - const subscription = BreezLiquidSDKEmitter.addListener("breezLiquidSdkLog", logStream) +export const setLogger = async (logger: Logger): Promise => { + const subscription = BreezLiquidSDKEmitter.addListener("breezLiquidSdkLog", logger) try { - await BreezLiquidSDK.setLogStream() + await BreezLiquidSDK.setLogger() } catch {} return subscription