diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 1cbb980..8b7d50e 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "accessory" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb3791c4beae5b827e93558ac83a88e63a841aad61759a05d9b577ef16030470" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "addr2line" version = "0.24.2" @@ -756,7 +768,7 @@ dependencies = [ "electrum-client", "elements", "env_logger 0.7.1", - "getrandom 0.2.14", + "getrandom 0.2.15", "hex", "js-sys", "lightning-invoice 0.32.0", @@ -786,12 +798,12 @@ dependencies = [ "derivative", "ecies", "electrum-client", - "env_logger 0.11.7", + "env_logger 0.11.8", "esplora-client", "flutter_rust_bridge", "futures", "futures-util", - "getrandom 0.2.14", + "getrandom 0.2.15", "glob", "gloo-timers", "hex", @@ -863,13 +875,21 @@ dependencies = [ "anyhow", "breez-sdk-liquid", "console_log", + "getrandom 0.2.15", + "indexed_db_futures", "js-sys", "log", + "rand 0.8.5", + "sdk-common", "sdk-macros", "serde", + "tokio", "tsify-next", + "uuid", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-bindgen-test", + "web-time", ] [[package]] @@ -1038,9 +1058,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive 4.5.32", @@ -1048,9 +1068,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -1281,6 +1301,20 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "delegate-display" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9926686c832494164c33a36bf65118f4bd6e704000b58c94681bf62e9ad67a74" +dependencies = [ + "impartial-ord", + "itoa", + "macroific", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "der-parser" version = "9.0.0" @@ -1315,6 +1349,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", + "unicode-xid", +] + [[package]] name = "digest" version = "0.9.0" @@ -1358,9 +1413,9 @@ dependencies = [ [[package]] name = "dnssec-prover" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96487aad690d45a83f2b9876828ba856c5430bbb143cb5730d8a5d04a4805179" +checksum = "48f9e1163868b86c37d43c586af9d917e699c87f1266ebfdf356ad1003458118" [[package]] name = "downcast" @@ -1370,12 +1425,12 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "ecies" -version = "0.2.7" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0206e602d2645ec8b24ed8307fadbc6c3110e2b11ab2f806fc02fee49327079" +checksum = "011318cc6f4f1906c1dae015013fd381e92deac290a29ddcd9f2e0dd14786037" dependencies = [ "aes-gcm", - "getrandom 0.2.14", + "getrandom 0.2.15", "hkdf", "libsecp256k1", "once_cell", @@ -1490,9 +1545,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -1543,6 +1598,18 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy_constructor" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac0fd7f4636276b4bd7b3148d0ba2c1c3fbede2b5214e47e7fedb70b02cde44" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1701,9 +1768,9 @@ dependencies = [ [[package]] name = "fragile" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" [[package]] name = "fs-err" @@ -1830,9 +1897,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2332,9 +2399,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -2342,6 +2409,7 @@ dependencies = [ "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "libc", "pin-project-lite", "socket2", "tokio", @@ -2418,9 +2486,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.62" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2428,7 +2496,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core", + "windows-core 0.61.0", ] [[package]] @@ -2481,9 +2549,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -2505,9 +2573,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -2526,9 +2594,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -2579,6 +2647,17 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impartial-ord" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab604ee7085efba6efc65e4ebca0e9533e3aff6cb501d7d77b211e3a781c6d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "include_dir" version = "0.7.4" @@ -2598,6 +2677,40 @@ dependencies = [ "quote", ] +[[package]] +name = "indexed_db_futures" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94eebf0199c01a1560770d964efb1fb09183a3afc0b1c1397fac1bfdbfa7d6cf" +dependencies = [ + "accessory", + "cfg-if 1.0.0", + "delegate-display", + "derive_more", + "fancy_constructor", + "indexed_db_futures_macros_internal", + "js-sys", + "sealed", + "smallvec", + "thiserror 2.0.12", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "indexed_db_futures_macros_internal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caeba94923b68f254abef921cea7e7698bf4675fdd89d7c58bf1ed885b49a27d" +dependencies = [ + "macroific", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2968,7 +3081,7 @@ dependencies = [ "base64 0.21.7", "elements", "elements-miniscript", - "getrandom 0.2.14", + "getrandom 0.2.15", "qr_code", "rand 0.8.5", "thiserror 1.0.69", @@ -3018,6 +3131,54 @@ dependencies = [ "web-sys", ] +[[package]] +name = "macroific" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89f276537b4b8f981bf1c13d79470980f71134b7bdcc5e6e911e910e556b0285" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "macroific_macro", +] + +[[package]] +name = "macroific_attr_parse" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad4023761b45fcd36abed8fb7ae6a80456b0a38102d55e89a57d9a594a236be9" +dependencies = [ + "proc-macro2", + "quote", + "sealed", + "syn 2.0.100", +] + +[[package]] +name = "macroific_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a7594d3c14916fa55bef7e9d18c5daa9ed410dd37504251e4b75bbdeec33e3" +dependencies = [ + "proc-macro2", + "quote", + "sealed", + "syn 2.0.100", +] + +[[package]] +name = "macroific_macro" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da6f2ed796261b0a74e2b52b42c693bb6dee1effba3a482c49592659f824b3b" +dependencies = [ + "macroific_attr_parse", + "macroific_core", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "macros" version = "0.0.0" @@ -3093,9 +3254,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniscript" -version = "12.3.0" +version = "12.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd3c9608217b0d6fa9c9c8ddd875b85ab72bd4311cfc8db35e1b5a08fc11f4d" +checksum = "82911d2fb527bb9aacd2446d2f517aff3f8e3846ace1b3c24258b61ea3cce2bc" dependencies = [ "bech32 0.11.0", "bitcoin 0.32.5", @@ -3262,9 +3423,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.1" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" dependencies = [ "critical-section", "portable-atomic", @@ -3536,7 +3697,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b122a615d72104fb3d8b26523fdf9232cd8ee06949fb37e4ce3ff964d15dffd" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -3824,9 +3985,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46f3055866785f6b92bc6164b76be02ca8f2eb4b002c0354b28cf4c119e5944" +checksum = "541d0f57c6ec747a90738a52741d3221f7960e8ac2f0ff4b1a63680e033b4ab5" dependencies = [ "cfg_aliases", "libc", @@ -3927,7 +4088,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.14", + "getrandom 0.2.15", ] [[package]] @@ -4177,7 +4338,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if 1.0.0", - "getrandom 0.2.14", + "getrandom 0.2.15", "libc", "untrusted 0.9.0", "windows-sys 0.52.0", @@ -4291,9 +4452,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", @@ -4336,7 +4497,7 @@ dependencies = [ "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.0", + "rustls-webpki 0.103.1", "subtle", "zeroize", ] @@ -4392,9 +4553,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.0" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa4eeac2588ffff23e9d7a7e9b3f971c5fb5b7ebc9452745e0c232c64f83b2f" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring 0.17.14", "rustls-pki-types", @@ -4536,7 +4697,7 @@ dependencies = [ "cbc", "dns-parser", "elements", - "getrandom 0.2.14", + "getrandom 0.2.15", "hex", "hickory-resolver", "lazy_static", @@ -4577,6 +4738,17 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "sealed" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "secp256k1" version = "0.24.3" @@ -4840,9 +5012,9 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -5030,7 +5202,7 @@ dependencies = [ "fastrand", "getrandom 0.3.2", "once_cell", - "rustix 1.0.3", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -5648,6 +5820,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "uniffi" version = "0.23.0" @@ -5670,7 +5848,7 @@ checksum = "21345172d31092fd48c47fd56c53d4ae9e41c4b1f559fb8c38c1ab1685fd919f" dependencies = [ "anyhow", "camino", - "clap 4.5.32", + "clap 4.5.35", "uniffi_bindgen 0.25.3", "uniffi_build 0.25.3", "uniffi_core 0.25.3", @@ -5686,7 +5864,7 @@ dependencies = [ "anyhow", "camino", "cargo_metadata", - "clap 4.5.32", + "clap 4.5.35", "uniffi_bindgen 0.28.3", "uniffi_build 0.28.3", "uniffi_core 0.28.3", @@ -5727,7 +5905,7 @@ dependencies = [ "askama 0.12.1", "camino", "cargo_metadata", - "clap 4.5.32", + "clap 4.5.35", "fs-err", "glob", "goblin 0.6.1", @@ -5773,7 +5951,7 @@ dependencies = [ "anyhow", "askama 0.12.1", "camino", - "clap 4.5.32", + "clap 4.5.35", "heck 0.4.1", "include_dir", "paste", @@ -6403,7 +6581,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", "windows-targets 0.52.6", ] @@ -6416,6 +6594,41 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings 0.4.0", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "windows-link" version = "0.1.1" @@ -6429,7 +6642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", - "windows-strings", + "windows-strings 0.3.1", "windows-targets 0.53.0", ] @@ -6451,6 +6664,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-strings" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 5f35d22..6c4481c 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -14,6 +14,7 @@ frb = ["dep:flutter_rust_bridge"] uniffi-25 = [] uniffi-28 = [] regtest = [] # Enable regtest tests +test-utils = ["sdk-common/test-utils"] [lints] workspace = true @@ -31,10 +32,6 @@ log = { workspace = true } lwk_common = "0.9.0" lwk_signer = { version = "0.9.0", default-features = false } mockall = "0.13.1" -rusqlite = { git = "https://github.com/Spxg/rusqlite", rev = "e36644127f31fa6e7ea0999b59432deb4a07f220", features = [ - "backup", - "bundled", -] } tokio = { version = "1", default-features = false, features = ["rt", "macros"] } tokio_with_wasm = { version = "0.8.2", features = [ "macros", @@ -80,6 +77,10 @@ prost = "^0.11" tonic = { version = "^0.8", features = ["tls", "tls-webpki-roots"] } uuid = { version = "1.8.0", features = ["v4"] } boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "f78e159fe72e1c357e7830bc08d2b9e42a65362c", features = ["electrum"] } +rusqlite = { git = "https://github.com/Spxg/rusqlite", rev = "e36644127f31fa6e7ea0999b59432deb4a07f220", features = [ + "backup", + "bundled", +] } # Wasm dependencies [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dependencies] @@ -95,6 +96,11 @@ tonic = { version = "0.12", default-features = false, features = [ "prost", ] } boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "f78e159fe72e1c357e7830bc08d2b9e42a65362c" } +rusqlite = { git = "https://github.com/Spxg/rusqlite", rev = "e36644127f31fa6e7ea0999b59432deb4a07f220", features = [ + "backup", + "bundled", + "serialize" +] } [dev-dependencies] sdk-common = { workspace = true, features = ["test-utils"] } diff --git a/lib/core/src/lib.rs b/lib/core/src/lib.rs index 97d165f..a547b3e 100644 --- a/lib/core/src/lib.rs +++ b/lib/core/src/lib.rs @@ -186,6 +186,10 @@ pub(crate) mod send_swap; pub(crate) mod signer; pub(crate) mod swapper; pub(crate) mod sync; +#[cfg(feature = "test-utils")] +pub mod test_utils; +#[cfg(test)] +#[cfg(not(feature = "test-utils"))] pub(crate) mod test_utils; #[allow(hidden_glob_reexports)] pub(crate) mod utils; diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 6202c5b..01e29ee 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -248,11 +248,7 @@ impl Config { } } - pub(crate) fn get_wallet_dir( - &self, - base_dir: &str, - fingerprint_hex: &str, - ) -> anyhow::Result { + pub fn get_wallet_dir(&self, base_dir: &str, fingerprint_hex: &str) -> anyhow::Result { Ok(PathBuf::from(base_dir) .join(match self.network { LiquidNetwork::Mainnet => "mainnet", diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index 121e311..edb354a 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -19,10 +19,11 @@ use crate::sync::model::RecordType; use crate::{get_invoice_description, utils}; use anyhow::{anyhow, Result}; use boltz_client::boltz::{ChainPair, ReversePair, SubmarinePair}; -use log::warn; +use log::{error, warn}; use lwk_wollet::WalletTx; use migrations::current_migrations; use model::PaymentTxDetails; +use rusqlite::backup::Backup; use rusqlite::{ params, params_from_iter, Connection, OptionalExtension, Row, ToSql, TransactionBehavior, }; @@ -72,7 +73,7 @@ impl Persister { if !main_db_dir.exists() { std::fs::create_dir_all(&main_db_dir)?; } - Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata) + Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata, None) } /// Creates a new Persister that only keeps data in memory. @@ -85,9 +86,29 @@ impl Persister { network: LiquidNetwork, sync_enabled: bool, asset_metadata: Option>, + backup_bytes: Option>, ) -> Result { let main_db_dir = PathBuf::from_str(database_id)?; - Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata) + let backup_con = backup_bytes + .map(|data| { + let size = data.len(); + let cursor = std::io::Cursor::new(data); + let mut conn = Connection::open_in_memory()?; + conn.deserialize_read_exact(rusqlite::DatabaseName::Main, cursor, size, false)?; + Ok::(conn) + }) + .transpose() + .unwrap_or_else(|e| { + error!("Failed to deserialize backup data: {e} - proceeding without it"); + None + }); + Self::new_inner( + main_db_dir, + network, + sync_enabled, + asset_metadata, + backup_con, + ) } fn new_inner( @@ -95,6 +116,7 @@ impl Persister { network: LiquidNetwork, sync_enabled: bool, asset_metadata: Option>, + backup_con: Option, ) -> Result { let mut sync_trigger = None; if sync_enabled { @@ -108,6 +130,17 @@ impl Persister { sync_trigger, }; + if let Some(backup_con) = backup_con { + if let Err(e) = (|| { + let mut dst_con = persister.get_connection()?; + let backup = Backup::new(&backup_con, &mut dst_con)?; + backup.step(-1)?; + Ok::<(), anyhow::Error>(()) + })() { + error!("Failed to restore from backup: {e} - proceeding without it"); + } + } + persister.init()?; persister.replace_asset_metadata(asset_metadata)?; @@ -125,7 +158,14 @@ impl Persister { Ok(()) } - #[cfg(test)] + #[cfg(all(target_family = "wasm", target_os = "unknown"))] + pub fn serialize(&self) -> Result> { + let con = self.get_connection()?; + let db_bytes = con.serialize(rusqlite::DatabaseName::Main)?; + Ok(db_bytes.to_vec()) + } + + #[cfg(any(test, feature = "test-utils"))] pub(crate) fn get_database_dir(&self) -> &PathBuf { &self.main_db_dir } @@ -1133,3 +1173,22 @@ mod tests { Ok(()) } } + +#[cfg(feature = "test-utils")] +pub mod test_helpers { + use super::*; + + impl Persister { + pub fn test_insert_or_update_send_swap(&self, swap: &SendSwap) -> Result<()> { + self.insert_or_update_send_swap(swap) + } + + pub fn test_insert_or_update_receive_swap(&self, swap: &ReceiveSwap) -> Result<()> { + self.insert_or_update_receive_swap(swap) + } + + pub fn test_list_ongoing_swaps(&self) -> Result> { + self.list_ongoing_swaps() + } + } +} diff --git a/lib/core/src/test_utils/chain.rs b/lib/core/src/test_utils/chain.rs index 822a26e..38d6da4 100644 --- a/lib/core/src/test_utils/chain.rs +++ b/lib/core/src/test_utils/chain.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use std::sync::Mutex; use crate::{ diff --git a/lib/core/src/test_utils/chain_swap.rs b/lib/core/src/test_utils/chain_swap.rs index 6541129..9b9a5e1 100644 --- a/lib/core/src/test_utils/chain_swap.rs +++ b/lib/core/src/test_utils/chain_swap.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use anyhow::Result; use hex::FromHex; use lazy_static::lazy_static; diff --git a/lib/core/src/test_utils/mod.rs b/lib/core/src/test_utils/mod.rs index 6f1a075..8681295 100644 --- a/lib/core/src/test_utils/mod.rs +++ b/lib/core/src/test_utils/mod.rs @@ -1,10 +1,10 @@ -#![cfg(test)] +#![cfg_attr(feature = "test-utils", allow(dead_code))] use bip39::rand::{self, distributions::Alphanumeric, Rng}; pub(crate) mod chain; pub(crate) mod chain_swap; -pub(crate) mod persist; +pub mod persist; pub(crate) mod receive_swap; pub(crate) mod recover; pub(crate) mod sdk; diff --git a/lib/core/src/test_utils/persist.rs b/lib/core/src/test_utils/persist.rs index bde4967..292cefe 100644 --- a/lib/core/src/test_utils/persist.rs +++ b/lib/core/src/test_utils/persist.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use bip39::rand::{self, RngCore}; use sdk_common::{ bitcoin::{ @@ -25,7 +23,7 @@ fn new_secret_key() -> SecretKey { SecretKey::from_slice(&buf).expect("Expected valid secret key") } -pub(crate) fn new_send_swap( +pub fn new_send_swap( payment_state: Option, receiver_amount_sat: Option, ) -> SendSwap { @@ -95,7 +93,7 @@ pub(crate) fn new_send_swap( } } -pub(crate) fn new_receive_swap( +pub fn new_receive_swap( payment_state: Option, receiver_amount_sat: Option, ) -> ReceiveSwap { @@ -124,6 +122,7 @@ pub(crate) fn new_receive_swap( } } +#[macro_export] macro_rules! create_persister { ($name:ident) => { #[cfg(all(target_family = "wasm", target_os = "unknown"))] @@ -137,11 +136,12 @@ macro_rules! create_persister { .collect(); res }; - sdk_common::utils::Arc::new(crate::persist::Persister::new_in_memory( + sdk_common::utils::Arc::new($crate::persist::Persister::new_in_memory( &db_id, - crate::model::LiquidNetwork::Testnet, + $crate::model::LiquidNetwork::Testnet, true, None, + None, )?) }; #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] @@ -151,16 +151,16 @@ macro_rules! create_persister { .to_str() .ok_or(anyhow::anyhow!("Could not create temporary directory"))? .to_string(); - sdk_common::utils::Arc::new(crate::persist::Persister::new_using_fs( + sdk_common::utils::Arc::new($crate::persist::Persister::new_using_fs( &temp_dir_path, - crate::model::LiquidNetwork::Testnet, + $crate::model::LiquidNetwork::Testnet, true, None, )?) }; }; } -pub(crate) use create_persister; +pub use create_persister; pub(crate) fn new_payment_tx_data( network: LiquidNetwork, diff --git a/lib/core/src/test_utils/receive_swap.rs b/lib/core/src/test_utils/receive_swap.rs index b84dda9..b82e689 100644 --- a/lib/core/src/test_utils/receive_swap.rs +++ b/lib/core/src/test_utils/receive_swap.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use anyhow::Result; use sdk_common::utils::Arc; diff --git a/lib/core/src/test_utils/sdk.rs b/lib/core/src/test_utils/sdk.rs index dfa148e..2f1fcae 100644 --- a/lib/core/src/test_utils/sdk.rs +++ b/lib/core/src/test_utils/sdk.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use anyhow::{anyhow, Result}; use sdk_common::prelude::{MockRestClient, RestClient, STAGING_BREEZSERVER_URL}; use sdk_common::utils::Arc; diff --git a/lib/core/src/test_utils/send_swap.rs b/lib/core/src/test_utils/send_swap.rs index 0f8d354..b4a442e 100644 --- a/lib/core/src/test_utils/send_swap.rs +++ b/lib/core/src/test_utils/send_swap.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use crate::{ model::{Config, Signer}, persist::Persister, diff --git a/lib/core/src/test_utils/status_stream.rs b/lib/core/src/test_utils/status_stream.rs index 987ecce..c898d91 100644 --- a/lib/core/src/test_utils/status_stream.rs +++ b/lib/core/src/test_utils/status_stream.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use anyhow::Result; use boltz_client::boltz; use sdk_common::utils::Arc; diff --git a/lib/core/src/test_utils/swapper.rs b/lib/core/src/test_utils/swapper.rs index 82fc990..c8cc271 100644 --- a/lib/core/src/test_utils/swapper.rs +++ b/lib/core/src/test_utils/swapper.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use anyhow::Result; use boltz_client::{ boltz::{ diff --git a/lib/core/src/test_utils/sync.rs b/lib/core/src/test_utils/sync.rs index 3877449..518adde 100644 --- a/lib/core/src/test_utils/sync.rs +++ b/lib/core/src/test_utils/sync.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use std::collections::HashMap; use crate::{ diff --git a/lib/core/src/test_utils/wallet.rs b/lib/core/src/test_utils/wallet.rs index a950b27..16c70f7 100644 --- a/lib/core/src/test_utils/wallet.rs +++ b/lib/core/src/test_utils/wallet.rs @@ -1,5 +1,3 @@ -#![cfg(test)] - use std::{collections::HashMap, str::FromStr, sync::Mutex}; use crate::{ diff --git a/lib/wasm/Cargo.toml b/lib/wasm/Cargo.toml index c23626d..8c5b482 100644 --- a/lib/wasm/Cargo.toml +++ b/lib/wasm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "breez-sdk-liquid-wasm" edition = "2021" -version.workspace = true +version = { workspace = true } [lib] name = "breez_sdk_liquid_wasm" @@ -18,6 +18,21 @@ js-sys = "0.3.77" log = { workspace = true } sdk-macros = { workspace = true } serde = { workspace = true } +tokio = { version = "1", default-features = false } tsify-next = "0.5.5" wasm-bindgen = "0.2.100" wasm-bindgen-futures = "0.4.50" +web-time = "1.1.0" +indexed_db_futures = "0.6.1" + +[dev-dependencies] +breez-sdk-liquid = { path = "../core", features = ["test-utils"] } +rand = "0.8" +getrandom = { version = "0.2", features = ["js"] } +sdk-common = { workspace = true, features = ["test-utils"] } +wasm-bindgen-test = "0.3.33" +uuid = "1.16.0" + +[features] +node-js = [] +browser-tests = [] diff --git a/lib/wasm/Makefile b/lib/wasm/Makefile index b584906..1e2d40f 100644 --- a/lib/wasm/Makefile +++ b/lib/wasm/Makefile @@ -8,9 +8,14 @@ init: cargo install wasm-pack rustup target add wasm32-unknown-unknown -clippy: +clippy: clippy-base clippy-node + +clippy-base: $(CLANG_PREFIX) cargo clippy --all-targets --target=wasm32-unknown-unknown -- -D warnings +clippy-node: + $(CLANG_PREFIX) cargo clippy --all-targets --target=wasm32-unknown-unknown --features node-js -- -D warnings + build: build-bundle build-deno build-node build-web build-bundle: @@ -20,16 +25,21 @@ build-deno: $(CLANG_PREFIX) wasm-pack build --target deno --release --out-dir pkg/deno build-node: - $(CLANG_PREFIX) wasm-pack build --target nodejs --release --out-dir pkg/node + $(CLANG_PREFIX) wasm-pack build --target nodejs --release --out-dir pkg/node --features node-js build-web: $(CLANG_PREFIX) wasm-pack build --target web --release --out-dir pkg/web -test: - $(CLANG_PREFIX) wasm-pack test --headless --firefox +test: test-firefox test-node + +test-node: + $(CLANG_PREFIX) wasm-pack test --node --features node-js + +test-firefox: + $(CLANG_PREFIX) wasm-pack test --headless --firefox --features browser-tests test-chrome: - $(CLANG_PREFIX) wasm-pack test --headless --chrome + $(CLANG_PREFIX) wasm-pack test --headless --chrome --features browser-tests test-safari: - $(CLANG_PREFIX) wasm-pack test --headless --safari + $(CLANG_PREFIX) wasm-pack test --headless --safari --features browser-tests diff --git a/lib/wasm/src/backup/indexed_db.rs b/lib/wasm/src/backup/indexed_db.rs new file mode 100644 index 0000000..608daec --- /dev/null +++ b/lib/wasm/src/backup/indexed_db.rs @@ -0,0 +1,71 @@ +use anyhow::Result; +use indexed_db_futures::{ + database::Database, query_source::QuerySource, transaction::TransactionMode, Build, +}; +use js_sys::{global, Reflect}; + +const IDB_STORE_NAME: &str = "BREEZ_SDK_LIQUID_DB_BACKUP_STORE"; + +pub(crate) fn is_indexed_db_supported() -> bool { + let global = global(); + Reflect::get(&global, &"indexedDB".into()).is_ok_and(|v| !v.is_undefined()) +} + +pub(crate) async fn backup_to_indexed_db(db_bytes: Vec, db_name: &str) -> Result<()> { + let idb = open_indexed_db(db_name).await?; + let tx = idb + .transaction([IDB_STORE_NAME]) + .with_mode(TransactionMode::Readwrite) + .build() + .map_err(|e| anyhow::anyhow!("Failed to build transaction: {}", e))?; + + let store = tx + .object_store(IDB_STORE_NAME) + .map_err(|e| anyhow::anyhow!("Failed to open object store: {}", e))?; + + store + .put(db_bytes) + .with_key(1) + .await + .map_err(|e| anyhow::anyhow!("Failed to put key in db: {}", e))?; + + tx.commit() + .await + .map_err(|e| anyhow::anyhow!("Failed to commit transaction: {}", e))?; + + Ok(()) +} + +pub(crate) async fn load_indexed_db_backup(db_name: &str) -> Result>> { + let idb = open_indexed_db(db_name).await?; + + let tx = idb + .transaction([IDB_STORE_NAME]) + .with_mode(TransactionMode::Readonly) + .build() + .map_err(|e| anyhow::anyhow!("Failed to build transaction: {}", e))?; + + let store = tx + .object_store(IDB_STORE_NAME) + .map_err(|e| anyhow::anyhow!("Failed to open object store: {}", e))?; + + store + .get(1) + .await + .map_err(|e| anyhow::anyhow!("Failed to get data: {}", e)) +} + +pub(crate) async fn open_indexed_db(name: &str) -> Result { + let db = Database::open(name) + .with_version(1u32) + .with_on_upgrade_needed(|event, db| { + if let (0.0, Some(1.0)) = (event.old_version(), event.new_version()) { + db.create_object_store(IDB_STORE_NAME).build()?; + } + + Ok(()) + }) + .await + .map_err(|e| anyhow::anyhow!("Failed to open IndexedDB: {}", e))?; + Ok(db) +} diff --git a/lib/wasm/src/backup/mod.rs b/lib/wasm/src/backup/mod.rs new file mode 100644 index 0000000..d7638ad --- /dev/null +++ b/lib/wasm/src/backup/mod.rs @@ -0,0 +1,136 @@ +mod indexed_db; +mod node_fs; + +use crate::utils::PathExt; +use anyhow::Result; +use breez_sdk_liquid::model::{EventListener, SdkEvent}; +use breez_sdk_liquid::persist::Persister; +use indexed_db::{backup_to_indexed_db, is_indexed_db_supported, load_indexed_db_backup}; +use std::path::{Path, PathBuf}; +use std::rc::Rc; +use tokio::sync::mpsc::{Receiver, Sender}; + +pub(crate) struct ForwardingEventListener { + sender: Sender, +} + +impl ForwardingEventListener { + pub fn new(sender: Sender) -> Self { + Self { sender } + } +} + +impl EventListener for ForwardingEventListener { + fn on_event(&self, e: SdkEvent) { + if let Err(e) = self.sender.try_send(e) { + log::error!("Failed to forward event: {:?}", e); + } + } +} + +pub(crate) fn start_backup_task( + persister: Rc, + mut receiver: Receiver, + backup_dir_path: PathBuf, +) { + wasm_bindgen_futures::spawn_local(async move { + while let Some(e) = receiver.recv().await { + let res = match e { + SdkEvent::Synced => backup(&persister, &backup_dir_path).await, + SdkEvent::DataSynced { + did_pull_new_records, + } if did_pull_new_records => backup(&persister, &backup_dir_path).await, + _ => continue, + }; + if let Err(e) = res { + log::error!("Failed to backup to IndexedDB: {:?}", e); + }; + } + }); +} + +async fn backup(persister: &Rc, backup_dir_path: &Path) -> Result<()> { + let start = web_time::Instant::now(); + + let db_bytes = persister.serialize()?; + + if is_indexed_db_supported() { + backup_to_indexed_db(db_bytes, backup_dir_path.to_str_safe()?).await?; + } else { + #[cfg(not(feature = "node-js"))] + return Err(anyhow::anyhow!("No backup mechanism available")); + #[cfg(feature = "node-js")] + node_fs::backup_to_file_system(db_bytes, backup_dir_path)?; + } + + let backup_duration_ms = start.elapsed().as_millis(); + log::info!("Backup completed successfully ({backup_duration_ms} ms)"); + Ok(()) +} + +pub(crate) async fn load_backup(backup_dir_path: &Path) -> Result>> { + let maybe_data = if is_indexed_db_supported() { + load_indexed_db_backup(backup_dir_path.to_str_safe()?).await? + } else { + #[cfg(not(feature = "node-js"))] + return Err(anyhow::anyhow!("No backup restore mechanism available")); + #[cfg(feature = "node-js")] + node_fs::load_file_system_backup(backup_dir_path)? + }; + Ok(maybe_data) +} + +#[cfg(test)] +mod tests { + use crate::backup::backup; + use crate::backup::load_backup; + use std::path::PathBuf; + use std::str::FromStr; + + use breez_sdk_liquid::model::PaymentState; + use breez_sdk_liquid::persist::Persister; + use breez_sdk_liquid::prelude::LiquidNetwork; + use breez_sdk_liquid::test_utils::persist::{ + create_persister, new_receive_swap, new_send_swap, + }; + + #[cfg(feature = "browser-tests")] + wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + + #[sdk_macros::async_test_wasm] + async fn test_backup_and_restore() -> anyhow::Result<()> { + create_persister!(local); + + local.test_insert_or_update_send_swap(&new_send_swap(Some(PaymentState::Pending), None))?; + local.test_insert_or_update_receive_swap(&new_receive_swap( + Some(PaymentState::Pending), + None, + ))?; + assert_eq!(local.test_list_ongoing_swaps()?.len(), 2); + + let backup_dir_path = PathBuf::from_str(&format!("/tmp/{}", uuid::Uuid::new_v4()))?; + backup(&local, &backup_dir_path).await?; + + let backup_bytes = load_backup(&backup_dir_path).await?; + let remote = + Persister::new_in_memory("remote", LiquidNetwork::Testnet, false, None, backup_bytes)?; + assert_eq!(remote.test_list_ongoing_swaps()?.len(), 2); + + // Try again to verify that a new backup overwrites an old one + local.test_insert_or_update_send_swap(&new_send_swap(Some(PaymentState::Pending), None))?; + local.test_insert_or_update_receive_swap(&new_receive_swap( + Some(PaymentState::Pending), + None, + ))?; + assert_eq!(local.test_list_ongoing_swaps()?.len(), 4); + + backup(&local, &backup_dir_path).await?; + + let backup_bytes = load_backup(&backup_dir_path).await?; + let remote = + Persister::new_in_memory("remote", LiquidNetwork::Testnet, false, None, backup_bytes)?; + assert_eq!(remote.test_list_ongoing_swaps()?.len(), 4); + + Ok(()) + } +} diff --git a/lib/wasm/src/backup/node_fs.rs b/lib/wasm/src/backup/node_fs.rs new file mode 100644 index 0000000..7a47b28 --- /dev/null +++ b/lib/wasm/src/backup/node_fs.rs @@ -0,0 +1,77 @@ +#![cfg(feature = "node-js")] + +use crate::utils::PathExt; +use anyhow::Result; +use js_sys::Reflect; +use std::path::{Path, PathBuf}; +use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; + +#[wasm_bindgen(module = "fs")] +extern "C" { + #[wasm_bindgen(js_name = writeFileSync, catch)] + fn write_file_sync(path: &str, data: &js_sys::Uint8Array) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = readFileSync, catch)] + fn read_file_sync(path: &str) -> Result; + + #[wasm_bindgen(js_name = existsSync)] + fn exists_sync(path: &str) -> bool; + + #[wasm_bindgen(js_name = mkdirSync, catch)] + fn mkdir_sync(path: &str, options: &JsValue) -> Result<(), JsValue>; +} + +fn get_backup_file_path(backup_dir_path: &Path) -> PathBuf { + backup_dir_path.join("backup.sql") +} + +pub fn ensure_dir_exists(path: &str) -> Result<(), JsValue> { + if !exists_sync(path) { + let options = js_sys::Object::new(); + Reflect::set(&options, &"recursive".into(), &true.into())?; + mkdir_sync(path, &options)?; + } + Ok(()) +} + +pub(crate) fn backup_to_file_system(db_bytes: Vec, backup_dir_path: &Path) -> Result<()> { + let uint8_array = js_sys::Uint8Array::from(db_bytes.as_slice()); + + ensure_dir_exists(backup_dir_path.to_str_safe()?) + .map_err(|e| anyhow::anyhow!("Failed to create backup directory: {:?}", e))?; + + let backup_file_path = get_backup_file_path(backup_dir_path); + write_file_sync(backup_file_path.to_str_safe()?, &uint8_array).map_err(|e| { + anyhow::anyhow!( + "Failed to write backup to file system using fs.writeFileSync: {:?}", + e + ) + })?; + + Ok(()) +} + +pub(crate) fn load_file_system_backup(backup_dir_path: &Path) -> Result>> { + let backup_file_path = get_backup_file_path(backup_dir_path); + let backup_file_path_str = backup_file_path.to_str_safe()?; + if !exists_sync(backup_file_path_str) { + log::debug!("Backup file '{backup_file_path:?}' not found."); + return Ok(None); + } + log::debug!("Backup file '{backup_file_path:?}' found, attempting to read.",); + + let buffer = read_file_sync(backup_file_path_str).map_err(|e| { + anyhow::anyhow!("Failed to read backup file using fs.readFileSync: {:?}", e) + })?; + + if !buffer.is_undefined() && !buffer.is_null() { + let uint8_array = js_sys::Uint8Array::new(&buffer); + let mut data = vec![0; uint8_array.length() as usize]; + uint8_array.copy_to(&mut data); + Ok(Some(data)) + } else { + Err(anyhow::anyhow!( + "readFileSync returned null or undefined for '{backup_file_path:?}'" + )) + } +} diff --git a/lib/wasm/src/lib.rs b/lib/wasm/src/lib.rs index e3b0fd0..ba8a7ed 100644 --- a/lib/wasm/src/lib.rs +++ b/lib/wasm/src/lib.rs @@ -1,14 +1,20 @@ +mod backup; mod error; mod event; mod logger; pub mod model; mod signer; +mod utils; +use std::path::PathBuf; use std::rc::Rc; +use std::str::FromStr; use crate::event::{EventListener, WasmEventListener}; use crate::model::*; use anyhow::anyhow; +use breez_sdk_liquid::bitcoin::bip32::{Fingerprint, Xpub}; +use breez_sdk_liquid::elements::hex::ToHex; use breez_sdk_liquid::persist::Persister; use breez_sdk_liquid::sdk::{LiquidSdk, LiquidSdkBuilder}; use breez_sdk_liquid::wallet::LiquidOnchainWallet; @@ -51,25 +57,50 @@ async fn connect_inner( Rc::clone(&signer), )?; + let fingerprint: Fingerprint = Xpub::decode( + &signer + .xpub() + .map_err(|e| anyhow!("Failed to get xpub: {e}"))?, + ) + .map_err(|e| anyhow!(e.to_string()))? + .identifier()[0..4] + .try_into() + .map_err(|e| anyhow!("Failed to get fingerprint: {e}"))?; + let fingerprint = fingerprint.to_hex(); + + let wallet_dir = PathBuf::from_str(&config.get_wallet_dir(&config.working_dir, &fingerprint)?) + .map_err(|e| anyhow!(e.to_string()))?; + let maybe_backup_bytes = backup::load_backup(&wallet_dir).await.unwrap_or_else(|e| { + log::error!("Failed to load backup: {:?}", e); + None + }); + let persister = Rc::new(Persister::new_in_memory( &config.working_dir, config.network, config.sync_enabled(), config.asset_metadata.clone(), + maybe_backup_bytes, )?); let onchain_wallet = Rc::new(LiquidOnchainWallet::new_in_memory( - config, + config.clone(), Rc::clone(&persister), signer, )?); - sdk_builder.persister(persister); + sdk_builder.persister(persister.clone()); sdk_builder.onchain_wallet(onchain_wallet); let sdk = sdk_builder.build()?; sdk.start().await?; + let (sender, receiver) = tokio::sync::mpsc::channel(20); + let listener = backup::ForwardingEventListener::new(sender); + sdk.add_event_listener(Box::new(listener)).await?; + + backup::start_backup_task(persister, receiver, wallet_dir); + Ok(BindingLiquidSdk { sdk }) } diff --git a/lib/wasm/src/utils.rs b/lib/wasm/src/utils.rs new file mode 100644 index 0000000..757a682 --- /dev/null +++ b/lib/wasm/src/utils.rs @@ -0,0 +1,13 @@ +use anyhow::anyhow; +use std::path::Path; + +pub trait PathExt { + fn to_str_safe(&self) -> anyhow::Result<&str>; +} + +impl PathExt for Path { + fn to_str_safe(&self) -> anyhow::Result<&str> { + self.to_str() + .ok_or_else(|| anyhow!("Invalid UTF-8 sequence in path: {:?}", self)) + } +} diff --git a/packages/wasm/README.md b/packages/wasm/README.md index e695fb0..e305729 100644 --- a/packages/wasm/README.md +++ b/packages/wasm/README.md @@ -85,7 +85,8 @@ const initSdk = async () => { const mnemonic = process.env.MNEMONIC // Connect using the config - const config = await defaultConfig('mainnet', breezApiKey) + let config = defaultConfig('mainnet', breezApiKey) + config.workingDir = "./.data" console.log(`defaultConfig: ${JSON.stringify(config)}`) const sdk = await connect({ config, mnemonic }) diff --git a/packages/wasm/examples/node/.gitignore b/packages/wasm/examples/node/.gitignore index 6eab97b..b7e2577 100644 --- a/packages/wasm/examples/node/.gitignore +++ b/packages/wasm/examples/node/.gitignore @@ -14,6 +14,8 @@ dist dist-ssr *.local +.data + # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/packages/wasm/examples/node/src/cli.js b/packages/wasm/examples/node/src/cli.js index ca2324a..4929afd 100644 --- a/packages/wasm/examples/node/src/cli.js +++ b/packages/wasm/examples/node/src/cli.js @@ -145,7 +145,8 @@ const initSdk = async () => { const mnemonic = process.env.MNEMONIC // Connect using the config - const config = defaultConfig('mainnet', breezApiKey) + let config = defaultConfig('mainnet', breezApiKey) + config.workingDir = "./.data" sdk = await connect({ config, mnemonic })