diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 5f1cbfc..ba0b938 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -127,47 +127,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -175,9 +176,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "atomic" @@ -198,9 +199,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -231,9 +232,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bech32" @@ -262,13 +263,15 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.29.2" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" +checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" dependencies = [ "bech32 0.9.1", - "bitcoin_hashes 0.11.0", - "secp256k1 0.24.3", + "bitcoin-private", + "bitcoin_hashes 0.12.0", + "hex_lit", + "secp256k1 0.27.0", ] [[package]] @@ -308,6 +311,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +[[package]] +name = "bitcoin_hashes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +dependencies = [ + "bitcoin-private", +] + [[package]] name = "bitcoin_hashes" version = "0.13.0" @@ -343,7 +355,7 @@ dependencies = [ [[package]] name = "boltz-client" version = "0.1.3" -source = "git+https://github.com/SatoshiPortal/boltz-rust?rev=6f45fff8b87c7530c847eb05f018906c48785a6c#6f45fff8b87c7530c847eb05f018906c48785a6c" +source = "git+https://github.com/hydra-yse/boltz-rust?rev=be8395900495e415699a54e15f4bd0bc6d774237#be8395900495e415699a54e15f4bd0bc6d774237" dependencies = [ "bip39", "bitcoin 0.31.2", @@ -383,6 +395,7 @@ dependencies = [ "anyhow", "bip39", "boltz-client", + "elements", "flutter_rust_bridge", "log", "lwk_common", @@ -392,7 +405,9 @@ dependencies = [ "rusqlite", "rusqlite_migration", "serde", + "serde_json", "thiserror", + "tungstenite", ] [[package]] @@ -427,9 +442,9 @@ checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg-if" @@ -511,9 +526,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "console_error_panic_hook" @@ -599,9 +614,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "delegate-attr" @@ -762,9 +777,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.0.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" [[package]] name = "fd-lock" @@ -779,9 +794,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.29" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4556222738635b7a3417ae6130d8f52201e45a0c4d1a907f0826383adb5f85e7" +checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" dependencies = [ "crc32fast", "miniz_oxide", @@ -957,9 +972,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", "js-sys", @@ -1001,9 +1016,9 @@ checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -1231,6 +1246,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itoa" version = "1.0.11" @@ -1254,9 +1275,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libsqlite3-sys" @@ -1271,25 +1292,25 @@ dependencies = [ [[package]] name = "lightning" -version = "0.0.118" +version = "0.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cec5fa9382154fe9671e8df93095b800c7d77abc66e2a5ef839d672521c5e" +checksum = "0d9b36ae12b379905bfc429ce5d4e8ca4a55c8dd3de73074309bd0bcc053bcac" dependencies = [ - "bitcoin 0.29.2", + "bitcoin 0.30.2", + "hex-conservative", ] [[package]] name = "lightning-invoice" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb24878b0f4ef75f020976c886d9ad1503867802329cc963e0ab4623ea3b25c" +checksum = "106fdb897e69df697480f45bf0a564b425af488fb0f7407e770a770c39b19a21" dependencies = [ "bech32 0.9.1", - "bitcoin 0.29.2", - "bitcoin_hashes 0.11.0", + "bitcoin 0.30.2", "lightning", "num-traits", - "secp256k1 0.24.3", + "secp256k1 0.27.0", ] [[package]] @@ -1485,9 +1506,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1657,9 +1678,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -1850,9 +1871,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" @@ -1904,9 +1925,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "51f344d206c5e1b010eec27349b815a4805f70a778895959d70b74b9b529b30a" [[package]] name = "rustls-webpki" @@ -1965,9 +1986,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" @@ -1996,12 +2017,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "bitcoin_hashes 0.11.0", - "secp256k1-sys 0.6.1", + "bitcoin_hashes 0.12.0", + "secp256k1-sys 0.8.1", ] [[package]] @@ -2018,9 +2039,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ "cc", ] @@ -2057,11 +2078,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -2070,9 +2091,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -2080,9 +2101,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] @@ -2108,9 +2129,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", @@ -2168,9 +2189,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2196,9 +2217,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.60" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -2255,18 +2276,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", @@ -2325,16 +2346,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -2461,7 +2481,7 @@ version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "flate2", "log", "native-tls", @@ -2822,18 +2842,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/cli/src/commands.rs b/cli/src/commands.rs index 570150e..e827442 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -117,12 +117,12 @@ pub(crate) fn handle_command( } Command::SendPayment { bolt11, delay } => { let prepare_response = - sdk.prepare_send_payment(PrepareSendRequest { invoice: bolt11 })?; + sdk.prepare_send_payment(&PrepareSendRequest { invoice: bolt11 })?; wait_confirmation!( format!( "Fees: {} sat. Are the fees acceptable? (y/N) ", - prepare_response.total_fees + prepare_response.fees_sat ), "Payment send halted" ); diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 8fcd36e..c4ff871 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -127,47 +127,48 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", "windows-sys 0.52.0", @@ -175,9 +176,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" dependencies = [ "backtrace", ] @@ -227,7 +228,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -281,9 +282,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" @@ -314,9 +315,9 @@ checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.22.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "basic-toml" @@ -385,13 +386,15 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.29.2" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0694ea59225b0c5f3cb405ff3f670e4828358ed26aec49dc352f730f0cb1a8a3" +checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" dependencies = [ "bech32 0.9.1", - "bitcoin_hashes 0.11.0", - "secp256k1 0.24.3", + "bitcoin-private", + "bitcoin_hashes 0.12.0", + "hex_lit", + "secp256k1 0.27.0", ] [[package]] @@ -431,6 +434,15 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4" +[[package]] +name = "bitcoin_hashes" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +dependencies = [ + "bitcoin-private", +] + [[package]] name = "bitcoin_hashes" version = "0.13.0" @@ -466,7 +478,7 @@ dependencies = [ [[package]] name = "boltz-client" version = "0.1.3" -source = "git+https://github.com/SatoshiPortal/boltz-rust?rev=6f45fff8b87c7530c847eb05f018906c48785a6c#6f45fff8b87c7530c847eb05f018906c48785a6c" +source = "git+https://github.com/hydra-yse/boltz-rust?rev=be8395900495e415699a54e15f4bd0bc6d774237#be8395900495e415699a54e15f4bd0bc6d774237" dependencies = [ "bip39", "bitcoin 0.31.2", @@ -490,6 +502,7 @@ dependencies = [ "anyhow", "bip39", "boltz-client", + "elements", "flutter_rust_bridge", "log", "lwk_common", @@ -499,8 +512,10 @@ dependencies = [ "rusqlite", "rusqlite_migration", "serde", + "serde_json", "tempdir", "thiserror", + "tungstenite", "uuid", ] @@ -581,9 +596,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" [[package]] name = "cfg-if" @@ -675,7 +690,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -695,9 +710,9 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "console_error_panic_hook" @@ -795,7 +810,7 @@ checksum = "51aac4c99b2e6775164b412ea33ae8441b2fde2dbf05a20bc0052a63d08c475b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -961,7 +976,7 @@ checksum = "d852460bc16316c4491a60e1652612f717764d436f3a97f8f1cc7c3b54d9a0dc" dependencies = [ "hex", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1065,7 +1080,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1110,9 +1125,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", "js-sys", @@ -1438,6 +1453,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itoa" version = "1.0.11" @@ -1461,9 +1482,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "libsqlite3-sys" @@ -1478,25 +1499,25 @@ dependencies = [ [[package]] name = "lightning" -version = "0.0.118" +version = "0.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cec5fa9382154fe9671e8df93095b800c7d77abc66e2a5ef839d672521c5e" +checksum = "0d9b36ae12b379905bfc429ce5d4e8ca4a55c8dd3de73074309bd0bcc053bcac" dependencies = [ - "bitcoin 0.29.2", + "bitcoin 0.30.2", + "hex-conservative", ] [[package]] name = "lightning-invoice" -version = "0.26.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb24878b0f4ef75f020976c886d9ad1503867802329cc963e0ab4623ea3b25c" +checksum = "106fdb897e69df697480f45bf0a564b425af488fb0f7407e770a770c39b19a21" dependencies = [ "bech32 0.9.1", - "bitcoin 0.29.2", - "bitcoin_hashes 0.11.0", + "bitcoin 0.30.2", "lightning", "num-traits", - "secp256k1 0.24.3", + "secp256k1 0.27.0", ] [[package]] @@ -1698,9 +1719,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -1765,7 +1786,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -1863,9 +1884,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" @@ -1941,9 +1962,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -2173,9 +2194,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" @@ -2227,9 +2248,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "51f344d206c5e1b010eec27349b815a4805f70a778895959d70b74b9b529b30a" [[package]] name = "rustls-webpki" @@ -2254,9 +2275,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" @@ -2299,7 +2320,7 @@ checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -2310,7 +2331,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -2325,12 +2346,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.24.3" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62" +checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" dependencies = [ - "bitcoin_hashes 0.11.0", - "secp256k1-sys 0.6.1", + "bitcoin_hashes 0.12.0", + "secp256k1-sys 0.8.1", ] [[package]] @@ -2347,9 +2368,9 @@ dependencies = [ [[package]] name = "secp256k1-sys" -version = "0.6.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83080e2c2fc1006e625be82e5d1eb6a43b7fd9578b617fcc55814daf286bba4b" +checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" dependencies = [ "cc", ] @@ -2386,11 +2407,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", "core-foundation", "core-foundation-sys", "libc", @@ -2399,9 +2420,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" dependencies = [ "core-foundation-sys", "libc", @@ -2409,18 +2430,18 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" dependencies = [ "serde_derive", ] @@ -2446,13 +2467,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.199" +version = "1.0.200" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -2578,9 +2599,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -2658,22 +2679,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -2727,7 +2748,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -2742,16 +2763,15 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] @@ -2788,7 +2808,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -3031,7 +3051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55137c122f712d9330fd985d66fa61bdc381752e89c35708c13ce63049a3002c" dependencies = [ "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -3041,7 +3061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae7e5a6c33b1dec3f255f57ec0b6af0f0b2bb3021868be1d5eec7a38e2905ebc" dependencies = [ "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] @@ -3108,7 +3128,7 @@ dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.60", + "syn 2.0.61", "toml", "uniffi_meta 0.27.1", ] @@ -3236,7 +3256,7 @@ version = "2.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" dependencies = [ - "base64 0.22.0", + "base64 0.22.1", "flate2", "log", "native-tls", @@ -3330,7 +3350,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", "wasm-bindgen-shared", ] @@ -3364,7 +3384,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3624,22 +3644,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.61", ] [[package]] diff --git a/lib/bindings/bindings-android/lib/src/main/kotlin/breez_liquid_sdk.kt b/lib/bindings/bindings-android/lib/src/main/kotlin/breez_liquid_sdk.kt new file mode 100644 index 0000000..7dc3358 --- /dev/null +++ b/lib/bindings/bindings-android/lib/src/main/kotlin/breez_liquid_sdk.kt @@ -0,0 +1,1925 @@ +// This file was autogenerated by some hot garbage in the `uniffi` crate. +// Trust me, you don't want to mess with it! + +@file:Suppress("NAME_SHADOWING") + +package breez_liquid_sdk; + +// Common helper code. +// +// Ideally this would live in a separate .kt file where it can be unittested etc +// in isolation, and perhaps even published as a re-useable package. +// +// However, it's important that the details of how this helper code works (e.g. the +// way that different builtin types are passed across the FFI) exactly match what's +// expected by the Rust code on the other side of the interface. In practice right +// now that means coming from the exact some version of `uniffi` that was used to +// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin +// helpers directly inline like we're doing here. + +import com.sun.jna.Library +import com.sun.jna.IntegerType +import com.sun.jna.Native +import com.sun.jna.Pointer +import com.sun.jna.Structure +import com.sun.jna.Callback +import com.sun.jna.ptr.* +import java.nio.ByteBuffer +import java.nio.ByteOrder +import java.nio.CharBuffer +import java.nio.charset.CodingErrorAction +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicBoolean + +// This is a helper for safely working with byte buffers returned from the Rust code. +// A rust-owned buffer is represented by its capacity, its current length, and a +// pointer to the underlying data. + +@Structure.FieldOrder("capacity", "len", "data") +open class RustBuffer : Structure() { + // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values. + // When dealing with these fields, make sure to call `toULong()`. + @JvmField var capacity: Long = 0 + @JvmField var len: Long = 0 + @JvmField var data: Pointer? = null + + class ByValue: RustBuffer(), Structure.ByValue + class ByReference: RustBuffer(), Structure.ByReference + + internal fun setValue(other: RustBuffer) { + capacity = other.capacity + len = other.len + data = other.data + } + + companion object { + internal fun alloc(size: ULong = 0UL) = uniffiRustCall() { status -> + // Note: need to convert the size to a `Long` value to make this work with JVM. + UniffiLib.INSTANCE.ffi_breez_liquid_sdk_bindings_rustbuffer_alloc(size.toLong(), status) + }.also { + if(it.data == null) { + throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") + } + } + + internal fun create(capacity: ULong, len: ULong, data: Pointer?): RustBuffer.ByValue { + var buf = RustBuffer.ByValue() + buf.capacity = capacity.toLong() + buf.len = len.toLong() + buf.data = data + return buf + } + + internal fun free(buf: RustBuffer.ByValue) = uniffiRustCall() { status -> + UniffiLib.INSTANCE.ffi_breez_liquid_sdk_bindings_rustbuffer_free(buf, status) + } + } + + @Suppress("TooGenericExceptionThrown") + fun asByteBuffer() = + this.data?.getByteBuffer(0, this.len.toLong())?.also { + it.order(ByteOrder.BIG_ENDIAN) + } +} + +/** + * The equivalent of the `*mut RustBuffer` type. + * Required for callbacks taking in an out pointer. + * + * Size is the sum of all values in the struct. + */ +class RustBufferByReference : ByReference(16) { + /** + * Set the pointed-to `RustBuffer` to the given value. + */ + fun setValue(value: RustBuffer.ByValue) { + // NOTE: The offsets are as they are in the C-like struct. + val pointer = getPointer() + pointer.setLong(0, value.capacity) + pointer.setLong(8, value.len) + pointer.setPointer(16, value.data) + } + + /** + * Get a `RustBuffer.ByValue` from this reference. + */ + fun getValue(): RustBuffer.ByValue { + val pointer = getPointer() + val value = RustBuffer.ByValue() + value.writeField("capacity", pointer.getLong(0)) + value.writeField("len", pointer.getLong(8)) + value.writeField("data", pointer.getLong(16)) + + return value + } +} + +// This is a helper for safely passing byte references into the rust code. +// It's not actually used at the moment, because there aren't many things that you +// can take a direct pointer to in the JVM, and if we're going to copy something +// then we might as well copy it into a `RustBuffer`. But it's here for API +// completeness. + +@Structure.FieldOrder("len", "data") +open class ForeignBytes : Structure() { + @JvmField var len: Int = 0 + @JvmField var data: Pointer? = null + + class ByValue : ForeignBytes(), Structure.ByValue +} +// The FfiConverter interface handles converter types to and from the FFI +// +// All implementing objects should be public to support external types. When a +// type is external we need to import it's FfiConverter. +public interface FfiConverter { + // Convert an FFI type to a Kotlin type + fun lift(value: FfiType): KotlinType + + // Convert an Kotlin type to an FFI type + fun lower(value: KotlinType): FfiType + + // Read a Kotlin type from a `ByteBuffer` + fun read(buf: ByteBuffer): KotlinType + + // Calculate bytes to allocate when creating a `RustBuffer` + // + // This must return at least as many bytes as the write() function will + // write. It can return more bytes than needed, for example when writing + // Strings we can't know the exact bytes needed until we the UTF-8 + // encoding, so we pessimistically allocate the largest size possible (3 + // bytes per codepoint). Allocating extra bytes is not really a big deal + // because the `RustBuffer` is short-lived. + fun allocationSize(value: KotlinType): ULong + + // Write a Kotlin type to a `ByteBuffer` + fun write(value: KotlinType, buf: ByteBuffer) + + // Lower a value into a `RustBuffer` + // + // This method lowers a value into a `RustBuffer` rather than the normal + // FfiType. It's used by the callback interface code. Callback interface + // returns are always serialized into a `RustBuffer` regardless of their + // normal FFI type. + fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue { + val rbuf = RustBuffer.alloc(allocationSize(value)) + try { + val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also { + it.order(ByteOrder.BIG_ENDIAN) + } + write(value, bbuf) + rbuf.writeField("len", bbuf.position().toLong()) + return rbuf + } catch (e: Throwable) { + RustBuffer.free(rbuf) + throw e + } + } + + // Lift a value from a `RustBuffer`. + // + // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. + // It's currently only used by the `FfiConverterRustBuffer` class below. + fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType { + val byteBuf = rbuf.asByteBuffer()!! + try { + val item = read(byteBuf) + if (byteBuf.hasRemaining()) { + throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") + } + return item + } finally { + RustBuffer.free(rbuf) + } + } +} + +// FfiConverter that uses `RustBuffer` as the FfiType +public interface FfiConverterRustBuffer: FfiConverter { + override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) + override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) +} +// A handful of classes and functions to support the generated data structures. +// This would be a good candidate for isolating in its own ffi-support lib. + +internal const val UNIFFI_CALL_SUCCESS = 0.toByte() +internal const val UNIFFI_CALL_ERROR = 1.toByte() +internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte() + +@Structure.FieldOrder("code", "error_buf") +internal open class UniffiRustCallStatus : Structure() { + @JvmField var code: Byte = 0 + @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() + + class ByValue: UniffiRustCallStatus(), Structure.ByValue + + fun isSuccess(): Boolean { + return code == UNIFFI_CALL_SUCCESS + } + + fun isError(): Boolean { + return code == UNIFFI_CALL_ERROR + } + + fun isPanic(): Boolean { + return code == UNIFFI_CALL_UNEXPECTED_ERROR + } + + companion object { + fun create(code: Byte, errorBuf: RustBuffer.ByValue): UniffiRustCallStatus.ByValue { + val callStatus = UniffiRustCallStatus.ByValue() + callStatus.code = code + callStatus.error_buf = errorBuf + return callStatus + } + } +} + +class InternalException(message: String) : Exception(message) + +// Each top-level error class has a companion object that can lift the error from the call status's rust buffer +interface UniffiRustCallStatusErrorHandler { + fun lift(error_buf: RustBuffer.ByValue): E; +} + +// Helpers for calling Rust +// In practice we usually need to be synchronized to call this safely, so it doesn't +// synchronize itself + +// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err +private inline fun uniffiRustCallWithError(errorHandler: UniffiRustCallStatusErrorHandler, callback: (UniffiRustCallStatus) -> U): U { + var status = UniffiRustCallStatus(); + val return_value = callback(status) + uniffiCheckCallStatus(errorHandler, status) + return return_value +} + +// Check UniffiRustCallStatus and throw an error if the call wasn't successful +private fun uniffiCheckCallStatus(errorHandler: UniffiRustCallStatusErrorHandler, status: UniffiRustCallStatus) { + if (status.isSuccess()) { + return + } else if (status.isError()) { + throw errorHandler.lift(status.error_buf) + } else if (status.isPanic()) { + // when the rust code sees a panic, it tries to construct a rustbuffer + // with the message. but if that code panics, then it just sends back + // an empty buffer. + if (status.error_buf.len > 0) { + throw InternalException(FfiConverterString.lift(status.error_buf)) + } else { + throw InternalException("Rust panic") + } + } else { + throw InternalException("Unknown rust call status: $status.code") + } +} + +// UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR +object UniffiNullRustCallStatusErrorHandler: UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): InternalException { + RustBuffer.free(error_buf) + return InternalException("Unexpected CALL_ERROR") + } +} + +// Call a rust function that returns a plain value +private inline fun uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U { + return uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback); +} + +internal inline fun uniffiTraitInterfaceCall( + callStatus: UniffiRustCallStatus, + makeCall: () -> T, + writeReturn: (T) -> Unit, +) { + try { + writeReturn(makeCall()) + } catch(e: Exception) { + callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR + callStatus.error_buf = FfiConverterString.lower(e.toString()) + } +} + +internal inline fun uniffiTraitInterfaceCallWithError( + callStatus: UniffiRustCallStatus, + makeCall: () -> T, + writeReturn: (T) -> Unit, + lowerError: (E) -> RustBuffer.ByValue +) { + try { + writeReturn(makeCall()) + } catch(e: Exception) { + if (e is E) { + callStatus.code = UNIFFI_CALL_ERROR + callStatus.error_buf = lowerError(e) + } else { + callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR + callStatus.error_buf = FfiConverterString.lower(e.toString()) + } + } +} +// Map handles to objects +// +// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code. +internal class UniffiHandleMap { + private val map = ConcurrentHashMap() + private val counter = java.util.concurrent.atomic.AtomicLong(0) + + val size: Int + get() = map.size + + // Insert a new object into the handle map and get a handle for it + fun insert(obj: T): Long { + val handle = counter.getAndAdd(1) + map.put(handle, obj) + return handle + } + + // Get an object from the handle map + fun get(handle: Long): T { + return map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle") + } + + // Remove an entry from the handlemap and get the Kotlin object back + fun remove(handle: Long): T { + return map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle") + } +} + +// Contains loading, initialization code, +// and the FFI Function declarations in a com.sun.jna.Library. +@Synchronized +private fun findLibraryName(componentName: String): String { + val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") + if (libOverride != null) { + return libOverride + } + return "breez_liquid_sdk_bindings" +} + +private inline fun loadIndirect( + componentName: String +): Lib { + return Native.load(findLibraryName(componentName), Lib::class.java) +} + +// Define FFI callback types +internal interface UniffiRustFutureContinuationCallback : com.sun.jna.Callback { + fun callback(`data`: Long,`pollResult`: Byte,) +} +internal interface UniffiForeignFutureFree : com.sun.jna.Callback { + fun callback(`handle`: Long,) +} +internal interface UniffiCallbackInterfaceFree : com.sun.jna.Callback { + fun callback(`handle`: Long,) +} +@Structure.FieldOrder("handle", "free") +internal open class UniffiForeignFuture( + @JvmField internal var `handle`: Long = 0.toLong(), + @JvmField internal var `free`: UniffiForeignFutureFree? = null, +) : Structure() { + class UniffiByValue( + `handle`: Long = 0.toLong(), + `free`: UniffiForeignFutureFree? = null, + ): UniffiForeignFuture(`handle`,`free`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFuture) { + `handle` = other.`handle` + `free` = other.`free` + } + +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU8( + @JvmField internal var `returnValue`: Byte = 0.toByte(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Byte = 0.toByte(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU8(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU8) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU8 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU8.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI8( + @JvmField internal var `returnValue`: Byte = 0.toByte(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Byte = 0.toByte(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI8(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI8) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI8 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI8.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU16( + @JvmField internal var `returnValue`: Short = 0.toShort(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Short = 0.toShort(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU16(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU16) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU16 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU16.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI16( + @JvmField internal var `returnValue`: Short = 0.toShort(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Short = 0.toShort(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI16(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI16) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI16 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI16.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU32( + @JvmField internal var `returnValue`: Int = 0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Int = 0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI32( + @JvmField internal var `returnValue`: Int = 0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Int = 0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructU64( + @JvmField internal var `returnValue`: Long = 0.toLong(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Long = 0.toLong(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructU64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructU64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteU64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructU64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructI64( + @JvmField internal var `returnValue`: Long = 0.toLong(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Long = 0.toLong(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructI64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructI64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteI64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructI64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructF32( + @JvmField internal var `returnValue`: Float = 0.0f, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Float = 0.0f, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructF32(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructF32) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteF32 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF32.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructF64( + @JvmField internal var `returnValue`: Double = 0.0, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Double = 0.0, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructF64(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructF64) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteF64 : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructF64.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructPointer( + @JvmField internal var `returnValue`: Pointer = Pointer.NULL, + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: Pointer = Pointer.NULL, + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructPointer(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructPointer) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompletePointer : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructPointer.UniffiByValue,) +} +@Structure.FieldOrder("returnValue", "callStatus") +internal open class UniffiForeignFutureStructRustBuffer( + @JvmField internal var `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructRustBuffer(`returnValue`,`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructRustBuffer) { + `returnValue` = other.`returnValue` + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteRustBuffer : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructRustBuffer.UniffiByValue,) +} +@Structure.FieldOrder("callStatus") +internal open class UniffiForeignFutureStructVoid( + @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), +) : Structure() { + class UniffiByValue( + `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), + ): UniffiForeignFutureStructVoid(`callStatus`,), Structure.ByValue + + internal fun uniffiSetValue(other: UniffiForeignFutureStructVoid) { + `callStatus` = other.`callStatus` + } + +} +internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { + fun callback(`callbackData`: Long,`result`: UniffiForeignFutureStructVoid.UniffiByValue,) +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// A JNA Library to expose the extern-C FFI definitions. +// This is an implementation detail which will be called internally by the public API. + +internal interface UniffiLib : Library { + companion object { + internal val INSTANCE: UniffiLib by lazy { + loadIndirect(componentName = "breez_liquid_sdk") + .also { lib: UniffiLib -> + uniffiCheckContractApiVersion(lib) + uniffiCheckApiChecksums(lib) + } + } + + // The Cleaner for the whole library + internal val CLEANER: UniffiCleaner by lazy { + UniffiCleaner.create() + } + } + + fun uniffi_breez_liquid_sdk_bindings_fn_clone_bindingliquidsdk(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, + ): Pointer + fun uniffi_breez_liquid_sdk_bindings_fn_free_bindingliquidsdk(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, + ): Unit + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_backup(`ptr`: Pointer,uniffi_out_err: UniffiRustCallStatus, + ): Unit + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_get_info(`ptr`: Pointer,`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_prepare_receive_payment(`ptr`: Pointer,`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_prepare_send_payment(`ptr`: Pointer,`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_receive_payment(`ptr`: Pointer,`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_restore(`ptr`: Pointer,`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): Unit + fun uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_send_payment(`ptr`: Pointer,`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun uniffi_breez_liquid_sdk_bindings_fn_func_connect(`req`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): Pointer + fun ffi_breez_liquid_sdk_bindings_rustbuffer_alloc(`size`: Long,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun ffi_breez_liquid_sdk_bindings_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun ffi_breez_liquid_sdk_bindings_rustbuffer_free(`buf`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Long,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_u8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_u8(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_u8(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_u8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Byte + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_i8(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_i8(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_i8(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_i8(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Byte + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_u16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_u16(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_u16(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_u16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Short + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_i16(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_i16(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_i16(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_i16(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Short + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_u32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_u32(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_u32(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_u32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Int + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_i32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_i32(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_i32(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_i32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Int + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_u64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_u64(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_u64(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_u64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Long + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_i64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_i64(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_i64(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_i64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Long + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_f32(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_f32(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_f32(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_f32(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Float + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_f64(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_f64(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_f64(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_f64(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Double + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_pointer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_pointer(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_pointer(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_pointer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Pointer + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_rust_buffer(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_rust_buffer(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_rust_buffer(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_rust_buffer(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): RustBuffer.ByValue + fun ffi_breez_liquid_sdk_bindings_rust_future_poll_void(`handle`: Long,`callback`: UniffiRustFutureContinuationCallback,`callbackData`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_cancel_void(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_free_void(`handle`: Long, + ): Unit + fun ffi_breez_liquid_sdk_bindings_rust_future_complete_void(`handle`: Long,uniffi_out_err: UniffiRustCallStatus, + ): Unit + fun uniffi_breez_liquid_sdk_bindings_checksum_func_connect( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_backup( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_get_info( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_prepare_receive_payment( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_prepare_send_payment( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_receive_payment( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_restore( + ): Short + fun uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_send_payment( + ): Short + fun ffi_breez_liquid_sdk_bindings_uniffi_contract_version( + ): Int + +} + +private fun uniffiCheckContractApiVersion(lib: UniffiLib) { + // Get the bindings contract version from our ComponentInterface + val bindings_contract_version = 26 + // Get the scaffolding contract version by calling the into the dylib + val scaffolding_contract_version = lib.ffi_breez_liquid_sdk_bindings_uniffi_contract_version() + if (bindings_contract_version != scaffolding_contract_version) { + throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project") + } +} + +@Suppress("UNUSED_PARAMETER") +private fun uniffiCheckApiChecksums(lib: UniffiLib) { + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_func_connect() != 18922.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_backup() != 4460.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_get_info() != 5563.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_prepare_receive_payment() != 57331.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_prepare_send_payment() != 35897.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_receive_payment() != 8257.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_restore() != 42575.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_breez_liquid_sdk_bindings_checksum_method_bindingliquidsdk_send_payment() != 37911.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +// Async support + +// Public interface members begin here. + + +// Interface implemented by anything that can contain an object reference. +// +// Such types expose a `destroy()` method that must be called to cleanly +// dispose of the contained objects. Failure to call this method may result +// in memory leaks. +// +// The easiest way to ensure this method is called is to use the `.use` +// helper method to execute a block and destroy the object at the end. +interface Disposable { + fun destroy() + companion object { + fun destroy(vararg args: Any?) { + args.filterIsInstance() + .forEach(Disposable::destroy) + } + } +} + +inline fun T.use(block: (T) -> R) = + try { + block(this) + } finally { + try { + // N.B. our implementation is on the nullable type `Disposable?`. + this?.destroy() + } catch (e: Throwable) { + // swallow + } + } + +/** Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. */ +object NoPointer + +public object FfiConverterULong: FfiConverter { + override fun lift(value: Long): ULong { + return value.toULong() + } + + override fun read(buf: ByteBuffer): ULong { + return lift(buf.getLong()) + } + + override fun lower(value: ULong): Long { + return value.toLong() + } + + override fun allocationSize(value: ULong) = 8UL + + override fun write(value: ULong, buf: ByteBuffer) { + buf.putLong(value.toLong()) + } +} + +public object FfiConverterBoolean: FfiConverter { + override fun lift(value: Byte): Boolean { + return value.toInt() != 0 + } + + override fun read(buf: ByteBuffer): Boolean { + return lift(buf.get()) + } + + override fun lower(value: Boolean): Byte { + return if (value) 1.toByte() else 0.toByte() + } + + override fun allocationSize(value: Boolean) = 1UL + + override fun write(value: Boolean, buf: ByteBuffer) { + buf.put(lower(value)) + } +} + +public object FfiConverterString: FfiConverter { + // Note: we don't inherit from FfiConverterRustBuffer, because we use a + // special encoding when lowering/lifting. We can use `RustBuffer.len` to + // store our length and avoid writing it out to the buffer. + override fun lift(value: RustBuffer.ByValue): String { + try { + val byteArr = ByteArray(value.len.toInt()) + value.asByteBuffer()!!.get(byteArr) + return byteArr.toString(Charsets.UTF_8) + } finally { + RustBuffer.free(value) + } + } + + override fun read(buf: ByteBuffer): String { + val len = buf.getInt() + val byteArr = ByteArray(len) + buf.get(byteArr) + return byteArr.toString(Charsets.UTF_8) + } + + fun toUtf8(value: String): ByteBuffer { + // Make sure we don't have invalid UTF-16, check for lone surrogates. + return Charsets.UTF_8.newEncoder().run { + onMalformedInput(CodingErrorAction.REPORT) + encode(CharBuffer.wrap(value)) + } + } + + override fun lower(value: String): RustBuffer.ByValue { + val byteBuf = toUtf8(value) + // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us + // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`. + val rbuf = RustBuffer.alloc(byteBuf.limit().toULong()) + rbuf.asByteBuffer()!!.put(byteBuf) + return rbuf + } + + // We aren't sure exactly how many bytes our string will be once it's UTF-8 + // encoded. Allocate 3 bytes per UTF-16 code unit which will always be + // enough. + override fun allocationSize(value: String): ULong { + val sizeForLength = 4UL + val sizeForString = value.length.toULong() * 3UL + return sizeForLength + sizeForString + } + + override fun write(value: String, buf: ByteBuffer) { + val byteBuf = toUtf8(value) + buf.putInt(byteBuf.limit()) + buf.put(byteBuf) + } +} + + +// This template implements a class for working with a Rust struct via a Pointer/Arc +// to the live Rust struct on the other side of the FFI. +// +// Each instance implements core operations for working with the Rust `Arc` and the +// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. +// +// There's some subtlety here, because we have to be careful not to operate on a Rust +// struct after it has been dropped, and because we must expose a public API for freeing +// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: +// +// * Each instance holds an opaque pointer to the underlying Rust struct. +// Method calls need to read this pointer from the object's state and pass it in to +// the Rust FFI. +// +// * When an instance is no longer needed, its pointer should be passed to a +// special destructor function provided by the Rust FFI, which will drop the +// underlying Rust struct. +// +// * Given an instance, calling code is expected to call the special +// `destroy` method in order to free it after use, either by calling it explicitly +// or by using a higher-level helper like the `use` method. Failing to do so risks +// leaking the underlying Rust struct. +// +// * We can't assume that calling code will do the right thing, and must be prepared +// to handle Kotlin method calls executing concurrently with or even after a call to +// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. +// +// * We must never allow Rust code to operate on the underlying Rust struct after +// the destructor has been called, and must never call the destructor more than once. +// Doing so may trigger memory unsafety. +// +// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` +// is implemented to call the destructor when the Kotlin object becomes unreachable. +// This is done in a background thread. This is not a panacea, and client code should be aware that +// 1. the thread may starve if some there are objects that have poorly performing +// `drop` methods or do significant work in their `drop` methods. +// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, +// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). +// +// If we try to implement this with mutual exclusion on access to the pointer, there is the +// possibility of a race between a method call and a concurrent call to `destroy`: +// +// * Thread A starts a method call, reads the value of the pointer, but is interrupted +// before it can pass the pointer over the FFI to Rust. +// * Thread B calls `destroy` and frees the underlying Rust struct. +// * Thread A resumes, passing the already-read pointer value to Rust and triggering +// a use-after-free. +// +// One possible solution would be to use a `ReadWriteLock`, with each method call taking +// a read lock (and thus allowed to run concurrently) and the special `destroy` method +// taking a write lock (and thus blocking on live method calls). However, we aim not to +// generate methods with any hidden blocking semantics, and a `destroy` method that might +// block if called incorrectly seems to meet that bar. +// +// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track +// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` +// has been called. These are updated according to the following rules: +// +// * The initial value of the counter is 1, indicating a live object with no in-flight calls. +// The initial value for the flag is false. +// +// * At the start of each method call, we atomically check the counter. +// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. +// If it is nonzero them we atomically increment it by 1 and proceed with the method call. +// +// * At the end of each method call, we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// * When `destroy` is called, we atomically flip the flag from false to true. +// If the flag was already true we silently fail. +// Otherwise we atomically decrement and check the counter. +// If it has reached zero then we destroy the underlying Rust struct. +// +// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, +// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. +// +// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been +// called *and* all in-flight method calls have completed, avoiding violating any of the expectations +// of the underlying Rust code. +// +// This makes a cleaner a better alternative to _not_ calling `destroy()` as +// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` +// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner +// thread may be starved, and the app will leak memory. +// +// In this case, `destroy`ing manually may be a better solution. +// +// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects +// with Rust peers are reclaimed: +// +// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: +// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: +// 3. The memory is reclaimed when the process terminates. +// +// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 +// + + +// The cleaner interface for Object finalization code to run. +// This is the entry point to any implementation that we're using. +// +// The cleaner registers objects and returns cleanables, so now we are +// defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the +// different implmentations available at compile time. +interface UniffiCleaner { + interface Cleanable { + fun clean() + } + + fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable + + companion object +} + +// The fallback Jna cleaner, which is available for both Android, and the JVM. +private class UniffiJnaCleaner : UniffiCleaner { + private val cleaner = com.sun.jna.internal.Cleaner.getCleaner() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + UniffiJnaCleanable(cleaner.register(value, cleanUpTask)) +} + +private class UniffiJnaCleanable( + private val cleanable: com.sun.jna.internal.Cleaner.Cleanable, +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} + +// We decide at uniffi binding generation time whether we were +// using Android or not. +// There are further runtime checks to chose the correct implementation +// of the cleaner. +private fun UniffiCleaner.Companion.create(): UniffiCleaner = + try { + // For safety's sake: if the library hasn't been run in android_cleaner = true + // mode, but is being run on Android, then we still need to think about + // Android API versions. + // So we check if java.lang.ref.Cleaner is there, and use that… + java.lang.Class.forName("java.lang.ref.Cleaner") + JavaLangRefCleaner() + } catch (e: ClassNotFoundException) { + // … otherwise, fallback to the JNA cleaner. + UniffiJnaCleaner() + } + +private class JavaLangRefCleaner : UniffiCleaner { + val cleaner = java.lang.ref.Cleaner.create() + + override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable = + JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) +} + +private class JavaLangRefCleanable( + val cleanable: java.lang.ref.Cleaner.Cleanable +) : UniffiCleaner.Cleanable { + override fun clean() = cleanable.clean() +} +public interface BindingLiquidSdkInterface { + + fun `backup`() + + fun `getInfo`(`req`: GetInfoRequest): GetInfoResponse + + fun `prepareReceivePayment`(`req`: PrepareReceiveRequest): PrepareReceiveResponse + + fun `prepareSendPayment`(`req`: PrepareSendRequest): PrepareSendResponse + + fun `receivePayment`(`req`: PrepareReceiveResponse): ReceivePaymentResponse + + fun `restore`(`req`: RestoreRequest) + + fun `sendPayment`(`req`: PrepareSendResponse): SendPaymentResponse + + companion object +} + +open class BindingLiquidSdk: Disposable, AutoCloseable, BindingLiquidSdkInterface { + + constructor(pointer: Pointer) { + this.pointer = pointer + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + /** + * This constructor can be used to instantiate a fake object. Only used for tests. Any + * attempt to actually use an object constructed this way will fail as there is no + * connected Rust object. + */ + @Suppress("UNUSED_PARAMETER") + constructor(noPointer: NoPointer) { + this.pointer = null + this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) + } + + protected val pointer: Pointer? + protected val cleanable: UniffiCleaner.Cleanable + + private val wasDestroyed = AtomicBoolean(false) + private val callCounter = AtomicLong(1) + + override fun destroy() { + // Only allow a single call to this method. + // TODO: maybe we should log a warning if called more than once? + if (this.wasDestroyed.compareAndSet(false, true)) { + // This decrement always matches the initial count of 1 given at creation time. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + @Synchronized + override fun close() { + this.destroy() + } + + internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { + // Check and increment the call counter, to keep the object alive. + // This needs a compare-and-set retry loop in case of concurrent updates. + do { + val c = this.callCounter.get() + if (c == 0L) { + throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") + } + if (c == Long.MAX_VALUE) { + throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") + } + } while (! this.callCounter.compareAndSet(c, c + 1L)) + // Now we can safely do the method call without the pointer being freed concurrently. + try { + return block(this.uniffiClonePointer()) + } finally { + // This decrement always matches the increment we performed above. + if (this.callCounter.decrementAndGet() == 0L) { + cleanable.clean() + } + } + } + + // Use a static inner class instead of a closure so as not to accidentally + // capture `this` as part of the cleanable's action. + private class UniffiCleanAction(private val pointer: Pointer?) : Runnable { + override fun run() { + pointer?.let { ptr -> + uniffiRustCall { status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_free_bindingliquidsdk(ptr, status) + } + } + } + } + + fun uniffiClonePointer(): Pointer { + return uniffiRustCall() { status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_clone_bindingliquidsdk(pointer!!, status) + } + } + + + @Throws(LiquidSdkException::class)override fun `backup`() + = + callWithPointer { + uniffiRustCallWithError(LiquidSdkException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_backup( + it, _status) +} + } + + + + + @Throws(LiquidSdkException::class)override fun `getInfo`(`req`: GetInfoRequest): GetInfoResponse { + return FfiConverterTypeGetInfoResponse.lift( + callWithPointer { + uniffiRustCallWithError(LiquidSdkException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_get_info( + it, FfiConverterTypeGetInfoRequest.lower(`req`),_status) +} + } + ) + } + + + + @Throws(PaymentException::class)override fun `prepareReceivePayment`(`req`: PrepareReceiveRequest): PrepareReceiveResponse { + return FfiConverterTypePrepareReceiveResponse.lift( + callWithPointer { + uniffiRustCallWithError(PaymentException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_prepare_receive_payment( + it, FfiConverterTypePrepareReceiveRequest.lower(`req`),_status) +} + } + ) + } + + + + @Throws(PaymentException::class)override fun `prepareSendPayment`(`req`: PrepareSendRequest): PrepareSendResponse { + return FfiConverterTypePrepareSendResponse.lift( + callWithPointer { + uniffiRustCallWithError(PaymentException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_prepare_send_payment( + it, FfiConverterTypePrepareSendRequest.lower(`req`),_status) +} + } + ) + } + + + + @Throws(PaymentException::class)override fun `receivePayment`(`req`: PrepareReceiveResponse): ReceivePaymentResponse { + return FfiConverterTypeReceivePaymentResponse.lift( + callWithPointer { + uniffiRustCallWithError(PaymentException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_receive_payment( + it, FfiConverterTypePrepareReceiveResponse.lower(`req`),_status) +} + } + ) + } + + + + @Throws(LiquidSdkException::class)override fun `restore`(`req`: RestoreRequest) + = + callWithPointer { + uniffiRustCallWithError(LiquidSdkException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_restore( + it, FfiConverterTypeRestoreRequest.lower(`req`),_status) +} + } + + + + + @Throws(PaymentException::class)override fun `sendPayment`(`req`: PrepareSendResponse): SendPaymentResponse { + return FfiConverterTypeSendPaymentResponse.lift( + callWithPointer { + uniffiRustCallWithError(PaymentException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_method_bindingliquidsdk_send_payment( + it, FfiConverterTypePrepareSendResponse.lower(`req`),_status) +} + } + ) + } + + + + + + + companion object + +} + +public object FfiConverterTypeBindingLiquidSdk: FfiConverter { + + override fun lower(value: BindingLiquidSdk): Pointer { + return value.uniffiClonePointer() + } + + override fun lift(value: Pointer): BindingLiquidSdk { + return BindingLiquidSdk(value) + } + + override fun read(buf: ByteBuffer): BindingLiquidSdk { + // The Rust code always writes pointers as 8 bytes, and will + // fail to compile if they don't fit. + return lift(Pointer(buf.getLong())) + } + + override fun allocationSize(value: BindingLiquidSdk) = 8UL + + override fun write(value: BindingLiquidSdk, buf: ByteBuffer) { + // The Rust code always expects pointers written as 8 bytes, + // and will fail to compile if they don't fit. + buf.putLong(Pointer.nativeValue(lower(value))) + } +} + + + +data class ConnectRequest ( + var `mnemonic`: kotlin.String, + var `network`: Network, + var `dataDir`: kotlin.String? = null +) { + + companion object +} + +public object FfiConverterTypeConnectRequest: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ConnectRequest { + return ConnectRequest( + FfiConverterString.read(buf), + FfiConverterTypeNetwork.read(buf), + FfiConverterOptionalString.read(buf), + ) + } + + override fun allocationSize(value: ConnectRequest) = ( + FfiConverterString.allocationSize(value.`mnemonic`) + + FfiConverterTypeNetwork.allocationSize(value.`network`) + + FfiConverterOptionalString.allocationSize(value.`dataDir`) + ) + + override fun write(value: ConnectRequest, buf: ByteBuffer) { + FfiConverterString.write(value.`mnemonic`, buf) + FfiConverterTypeNetwork.write(value.`network`, buf) + FfiConverterOptionalString.write(value.`dataDir`, buf) + } +} + + + +data class GetInfoRequest ( + var `withScan`: kotlin.Boolean +) { + + companion object +} + +public object FfiConverterTypeGetInfoRequest: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): GetInfoRequest { + return GetInfoRequest( + FfiConverterBoolean.read(buf), + ) + } + + override fun allocationSize(value: GetInfoRequest) = ( + FfiConverterBoolean.allocationSize(value.`withScan`) + ) + + override fun write(value: GetInfoRequest, buf: ByteBuffer) { + FfiConverterBoolean.write(value.`withScan`, buf) + } +} + + + +data class GetInfoResponse ( + var `balanceSat`: kotlin.ULong, + var `pubkey`: kotlin.String +) { + + companion object +} + +public object FfiConverterTypeGetInfoResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): GetInfoResponse { + return GetInfoResponse( + FfiConverterULong.read(buf), + FfiConverterString.read(buf), + ) + } + + override fun allocationSize(value: GetInfoResponse) = ( + FfiConverterULong.allocationSize(value.`balanceSat`) + + FfiConverterString.allocationSize(value.`pubkey`) + ) + + override fun write(value: GetInfoResponse, buf: ByteBuffer) { + FfiConverterULong.write(value.`balanceSat`, buf) + FfiConverterString.write(value.`pubkey`, buf) + } +} + + + +data class PrepareReceiveRequest ( + var `payerAmountSat`: kotlin.ULong +) { + + companion object +} + +public object FfiConverterTypePrepareReceiveRequest: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): PrepareReceiveRequest { + return PrepareReceiveRequest( + FfiConverterULong.read(buf), + ) + } + + override fun allocationSize(value: PrepareReceiveRequest) = ( + FfiConverterULong.allocationSize(value.`payerAmountSat`) + ) + + override fun write(value: PrepareReceiveRequest, buf: ByteBuffer) { + FfiConverterULong.write(value.`payerAmountSat`, buf) + } +} + + + +data class PrepareReceiveResponse ( + var `pairHash`: kotlin.String, + var `payerAmountSat`: kotlin.ULong, + var `feesSat`: kotlin.ULong +) { + + companion object +} + +public object FfiConverterTypePrepareReceiveResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): PrepareReceiveResponse { + return PrepareReceiveResponse( + FfiConverterString.read(buf), + FfiConverterULong.read(buf), + FfiConverterULong.read(buf), + ) + } + + override fun allocationSize(value: PrepareReceiveResponse) = ( + FfiConverterString.allocationSize(value.`pairHash`) + + FfiConverterULong.allocationSize(value.`payerAmountSat`) + + FfiConverterULong.allocationSize(value.`feesSat`) + ) + + override fun write(value: PrepareReceiveResponse, buf: ByteBuffer) { + FfiConverterString.write(value.`pairHash`, buf) + FfiConverterULong.write(value.`payerAmountSat`, buf) + FfiConverterULong.write(value.`feesSat`, buf) + } +} + + + +data class PrepareSendRequest ( + var `invoice`: kotlin.String +) { + + companion object +} + +public object FfiConverterTypePrepareSendRequest: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): PrepareSendRequest { + return PrepareSendRequest( + FfiConverterString.read(buf), + ) + } + + override fun allocationSize(value: PrepareSendRequest) = ( + FfiConverterString.allocationSize(value.`invoice`) + ) + + override fun write(value: PrepareSendRequest, buf: ByteBuffer) { + FfiConverterString.write(value.`invoice`, buf) + } +} + + + +data class PrepareSendResponse ( + var `invoice`: kotlin.String, + var `feesSat`: kotlin.ULong +) { + + companion object +} + +public object FfiConverterTypePrepareSendResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): PrepareSendResponse { + return PrepareSendResponse( + FfiConverterString.read(buf), + FfiConverterULong.read(buf), + ) + } + + override fun allocationSize(value: PrepareSendResponse) = ( + FfiConverterString.allocationSize(value.`invoice`) + + FfiConverterULong.allocationSize(value.`feesSat`) + ) + + override fun write(value: PrepareSendResponse, buf: ByteBuffer) { + FfiConverterString.write(value.`invoice`, buf) + FfiConverterULong.write(value.`feesSat`, buf) + } +} + + + +data class ReceivePaymentResponse ( + var `id`: kotlin.String, + var `invoice`: kotlin.String +) { + + companion object +} + +public object FfiConverterTypeReceivePaymentResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): ReceivePaymentResponse { + return ReceivePaymentResponse( + FfiConverterString.read(buf), + FfiConverterString.read(buf), + ) + } + + override fun allocationSize(value: ReceivePaymentResponse) = ( + FfiConverterString.allocationSize(value.`id`) + + FfiConverterString.allocationSize(value.`invoice`) + ) + + override fun write(value: ReceivePaymentResponse, buf: ByteBuffer) { + FfiConverterString.write(value.`id`, buf) + FfiConverterString.write(value.`invoice`, buf) + } +} + + + +data class RestoreRequest ( + var `backupPath`: kotlin.String? = null +) { + + companion object +} + +public object FfiConverterTypeRestoreRequest: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): RestoreRequest { + return RestoreRequest( + FfiConverterOptionalString.read(buf), + ) + } + + override fun allocationSize(value: RestoreRequest) = ( + FfiConverterOptionalString.allocationSize(value.`backupPath`) + ) + + override fun write(value: RestoreRequest, buf: ByteBuffer) { + FfiConverterOptionalString.write(value.`backupPath`, buf) + } +} + + + +data class SendPaymentResponse ( + var `txid`: kotlin.String +) { + + companion object +} + +public object FfiConverterTypeSendPaymentResponse: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SendPaymentResponse { + return SendPaymentResponse( + FfiConverterString.read(buf), + ) + } + + override fun allocationSize(value: SendPaymentResponse) = ( + FfiConverterString.allocationSize(value.`txid`) + ) + + override fun write(value: SendPaymentResponse, buf: ByteBuffer) { + FfiConverterString.write(value.`txid`, buf) + } +} + + + + + +sealed class LiquidSdkException(message: String): Exception(message) { + + class Generic(message: String) : LiquidSdkException(message) + + + companion object ErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): LiquidSdkException = FfiConverterTypeLiquidSdkError.lift(error_buf) + } +} + +public object FfiConverterTypeLiquidSdkError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): LiquidSdkException { + + return when(buf.getInt()) { + 1 -> LiquidSdkException.Generic(FfiConverterString.read(buf)) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + + } + + override fun allocationSize(value: LiquidSdkException): ULong { + return 4UL + } + + override fun write(value: LiquidSdkException, buf: ByteBuffer) { + when(value) { + is LiquidSdkException.Generic -> { + buf.putInt(1) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + +enum class Network { + + LIQUID, + LIQUID_TESTNET; + companion object +} + + +public object FfiConverterTypeNetwork: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer) = try { + Network.values()[buf.getInt() - 1] + } catch (e: IndexOutOfBoundsException) { + throw RuntimeException("invalid enum value, something is very wrong!!", e) + } + + override fun allocationSize(value: Network) = 4UL + + override fun write(value: Network, buf: ByteBuffer) { + buf.putInt(value.ordinal + 1) + } +} + + + + + + + +sealed class PaymentException(message: String): Exception(message) { + + class AmountOutOfRange(message: String) : PaymentException(message) + + class FeesExpired(message: String) : PaymentException(message) + + class AlreadyClaimed(message: String) : PaymentException(message) + + class Generic(message: String) : PaymentException(message) + + class InvalidInvoice(message: String) : PaymentException(message) + + class InvalidPreimage(message: String) : PaymentException(message) + + class LwkException(message: String) : PaymentException(message) + + class PairsNotFound(message: String) : PaymentException(message) + + class PersistException(message: String) : PaymentException(message) + + class SendException(message: String) : PaymentException(message) + + class SignerException(message: String) : PaymentException(message) + + + companion object ErrorHandler : UniffiRustCallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): PaymentException = FfiConverterTypePaymentError.lift(error_buf) + } +} + +public object FfiConverterTypePaymentError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): PaymentException { + + return when(buf.getInt()) { + 1 -> PaymentException.AmountOutOfRange(FfiConverterString.read(buf)) + 2 -> PaymentException.FeesExpired(FfiConverterString.read(buf)) + 3 -> PaymentException.AlreadyClaimed(FfiConverterString.read(buf)) + 4 -> PaymentException.Generic(FfiConverterString.read(buf)) + 5 -> PaymentException.InvalidInvoice(FfiConverterString.read(buf)) + 6 -> PaymentException.InvalidPreimage(FfiConverterString.read(buf)) + 7 -> PaymentException.LwkException(FfiConverterString.read(buf)) + 8 -> PaymentException.PairsNotFound(FfiConverterString.read(buf)) + 9 -> PaymentException.PersistException(FfiConverterString.read(buf)) + 10 -> PaymentException.SendException(FfiConverterString.read(buf)) + 11 -> PaymentException.SignerException(FfiConverterString.read(buf)) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + + } + + override fun allocationSize(value: PaymentException): ULong { + return 4UL + } + + override fun write(value: PaymentException, buf: ByteBuffer) { + when(value) { + is PaymentException.AmountOutOfRange -> { + buf.putInt(1) + Unit + } + is PaymentException.FeesExpired -> { + buf.putInt(2) + Unit + } + is PaymentException.AlreadyClaimed -> { + buf.putInt(3) + Unit + } + is PaymentException.Generic -> { + buf.putInt(4) + Unit + } + is PaymentException.InvalidInvoice -> { + buf.putInt(5) + Unit + } + is PaymentException.InvalidPreimage -> { + buf.putInt(6) + Unit + } + is PaymentException.LwkException -> { + buf.putInt(7) + Unit + } + is PaymentException.PairsNotFound -> { + buf.putInt(8) + Unit + } + is PaymentException.PersistException -> { + buf.putInt(9) + Unit + } + is PaymentException.SendException -> { + buf.putInt(10) + Unit + } + is PaymentException.SignerException -> { + buf.putInt(11) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + +public object FfiConverterOptionalString: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.String? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterString.read(buf) + } + + override fun allocationSize(value: kotlin.String?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterString.allocationSize(value) + } + } + + override fun write(value: kotlin.String?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterString.write(value, buf) + } + } +} + @Throws(LiquidSdkException::class) fun `connect`(`req`: ConnectRequest): BindingLiquidSdk { + return FfiConverterTypeBindingLiquidSdk.lift( + uniffiRustCallWithError(LiquidSdkException) { _status -> + UniffiLib.INSTANCE.uniffi_breez_liquid_sdk_bindings_fn_func_connect( + FfiConverterTypeConnectRequest.lower(`req`),_status) +} + ) + } + + + diff --git a/lib/bindings/bindings-flutter/breez_liquid_sdk/include/breez_liquid_sdk.h b/lib/bindings/bindings-flutter/breez_liquid_sdk/include/breez_liquid_sdk.h index a87a1b0..b7f3b17 100644 --- a/lib/bindings/bindings-flutter/breez_liquid_sdk/include/breez_liquid_sdk.h +++ b/lib/bindings/bindings-flutter/breez_liquid_sdk/include/breez_liquid_sdk.h @@ -15,10 +15,10 @@ void store_dart_post_cobject(DartPostCObjectFnType ptr); typedef struct _Dart_Handle* Dart_Handle; /** - * Claim tx feerate for Receive, in sats per vbyte. + * Claim tx feerate, in sats per vbyte. * Since the Liquid blocks are consistently empty for now, we hardcode the minimum feerate. */ -#define LIQUID_CLAIM_TX_FEERATE 0.1 +#define LIQUID_CLAIM_TX_FEERATE_MSAT 100.0 typedef struct wire_cst_list_prim_u_8_strict { uint8_t *ptr; @@ -44,7 +44,6 @@ typedef struct wire_cst_prepare_send_request { } wire_cst_prepare_send_request; typedef struct wire_cst_prepare_receive_response { - struct wire_cst_list_prim_u_8_strict *pair_hash; uint64_t payer_amount_sat; uint64_t fees_sat; } wire_cst_prepare_receive_response; @@ -54,12 +53,8 @@ typedef struct wire_cst_restore_request { } wire_cst_restore_request; typedef struct wire_cst_prepare_send_response { - struct wire_cst_list_prim_u_8_strict *id; - uint64_t payer_amount_sat; - uint64_t receiver_amount_sat; - uint64_t total_fees; - struct wire_cst_list_prim_u_8_strict *funding_address; struct wire_cst_list_prim_u_8_strict *invoice; + uint64_t fees_sat; } wire_cst_prepare_send_response; typedef struct wire_cst_payment { @@ -89,6 +84,11 @@ typedef struct wire_cst_PaymentError_LwkError { struct wire_cst_list_prim_u_8_strict *err; } wire_cst_PaymentError_LwkError; +typedef struct wire_cst_PaymentError_Refunded { + struct wire_cst_list_prim_u_8_strict *err; + struct wire_cst_list_prim_u_8_strict *txid; +} wire_cst_PaymentError_Refunded; + typedef struct wire_cst_PaymentError_SendError { struct wire_cst_list_prim_u_8_strict *err; } wire_cst_PaymentError_SendError; @@ -100,6 +100,7 @@ typedef struct wire_cst_PaymentError_SignerError { typedef union PaymentErrorKind { struct wire_cst_PaymentError_Generic Generic; struct wire_cst_PaymentError_LwkError LwkError; + struct wire_cst_PaymentError_Refunded Refunded; struct wire_cst_PaymentError_SendError SendError; struct wire_cst_PaymentError_SignerError SignerError; } PaymentErrorKind; @@ -137,17 +138,11 @@ void frbgen_breez_liquid_wire_prepare_send_payment(int64_t port_, void frbgen_breez_liquid_wire_receive_payment(int64_t port_, struct wire_cst_prepare_receive_response *req); -void frbgen_breez_liquid_wire_recover_funds(int64_t port_, uintptr_t recovery); - void frbgen_breez_liquid_wire_restore(int64_t port_, struct wire_cst_restore_request *req); void frbgen_breez_liquid_wire_send_payment(int64_t port_, struct wire_cst_prepare_send_response *req); -void frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery(const void *ptr); - -void frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery(const void *ptr); - struct wire_cst_connect_request *frbgen_breez_liquid_cst_new_box_autoadd_connect_request(void); struct wire_cst_get_info_request *frbgen_breez_liquid_cst_new_box_autoadd_get_info_request(void); @@ -182,8 +177,6 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_box_autoadd_u_64); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_cst_new_list_prim_u_8_strict); - dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery); - dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_backup); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_connect); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_empty_wallet_cache); @@ -192,7 +185,6 @@ static int64_t dummy_method_to_enforce_bundling(void) { dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_prepare_receive_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_prepare_send_payment); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_receive_payment); - dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_recover_funds); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_restore); dummy_var ^= ((int64_t) (void*) frbgen_breez_liquid_wire_send_payment); dummy_var ^= ((int64_t) (void*) store_dart_post_cobject); diff --git a/lib/bindings/src/breez_liquid_sdk.udl b/lib/bindings/src/breez_liquid_sdk.udl index fec19f5..45e5f8b 100644 --- a/lib/bindings/src/breez_liquid_sdk.udl +++ b/lib/bindings/src/breez_liquid_sdk.udl @@ -5,14 +5,17 @@ enum LiquidSdkError { [Error] enum PaymentError { - "AmountOutOfRange", "AlreadyClaimed", + "AmountOutOfRange", "Generic", + "InvalidOrExpiredFees", + "InsufficientFunds", "InvalidInvoice", "InvalidPreimage", "LwkError", "PairsNotFound", "PersistError", + "Refunded", "SendError", "SignerError", }; @@ -42,12 +45,8 @@ dictionary PrepareSendRequest { }; dictionary PrepareSendResponse { - string id; - u64 payer_amount_sat; - u64 receiver_amount_sat; - u64 total_fees; - string funding_address; string invoice; + u64 fees_sat; }; dictionary SendPaymentResponse { @@ -59,7 +58,6 @@ dictionary PrepareReceiveRequest { }; dictionary PrepareReceiveResponse { - string pair_hash; u64 payer_amount_sat; u64 fees_sat; }; diff --git a/lib/bindings/src/lib.rs b/lib/bindings/src/lib.rs index 16dc285..274578e 100644 --- a/lib/bindings/src/lib.rs +++ b/lib/bindings/src/lib.rs @@ -21,7 +21,7 @@ impl BindingLiquidSdk { &self, req: PrepareSendRequest, ) -> Result { - self.sdk.prepare_send_payment(req) + self.sdk.prepare_send_payment(&req) } pub fn send_payment( diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index 2e8770a..2916c2e 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -10,7 +10,8 @@ crate-type = ["lib", "cdylib", "staticlib"] [dependencies] anyhow = { workspace = true } bip39 = { version = "2.0.0", features = ["serde"] } -boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "6f45fff8b87c7530c847eb05f018906c48785a6c" } +#boltz-client = { git = "https://github.com/SatoshiPortal/boltz-rust", rev = "a05731cc33030ada9ae14afcafe0cded22842ba6" } +boltz-client = { git = "https://github.com/hydra-yse/boltz-rust", rev = "be8395900495e415699a54e15f4bd0bc6d774237" } flutter_rust_bridge = { version = "=2.0.0-dev.33", features = ["chrono"], optional = true } log = "0.4.20" lwk_common = "0.3.0" @@ -19,8 +20,12 @@ lwk_wollet = "0.3.0" rusqlite = { version = "0.31", features = ["backup", "bundled"] } rusqlite_migration = "1.0" serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0.116" thiserror = { workspace = true } +tungstenite = { version = "0.21.0", features = ["native-tls-vendored"] } openssl = { version = "0.10", features = ["vendored"] } +# TODO Remove once fully migrated to v2 API +elements = "0.24.1" [dev-dependencies] tempdir = "0.3.7" diff --git a/lib/core/src/bindings.rs b/lib/core/src/bindings.rs index f1e6a11..efde068 100644 --- a/lib/core/src/bindings.rs +++ b/lib/core/src/bindings.rs @@ -1,5 +1,4 @@ use anyhow::{anyhow, Result}; -pub(crate) use boltz_client::util::secrets::LBtcReverseRecovery; use std::sync::{Arc, OnceLock}; use crate::{error::*, model::*, sdk::LiquidSdk}; @@ -26,7 +25,7 @@ pub fn prepare_send_payment(req: PrepareSendRequest) -> Result Result { @@ -65,14 +64,6 @@ pub fn list_payments(with_scan: bool, include_pending: bool) -> Result Result { - LIQUID_SDK_INSTANCE - .get() - .ok_or(anyhow!("Not initialized")) - .map_err(|e| LiquidSdkError::Generic { err: e.to_string() })? - .recover_funds(&recovery) -} - pub fn empty_wallet_cache() -> Result<()> { LIQUID_SDK_INSTANCE .get() diff --git a/lib/core/src/boltz_status_stream.rs b/lib/core/src/boltz_status_stream.rs new file mode 100644 index 0000000..9e97a81 --- /dev/null +++ b/lib/core/src/boltz_status_stream.rs @@ -0,0 +1,212 @@ +use std::collections::HashSet; +use std::io::ErrorKind; +use std::net::TcpStream; +use std::str::FromStr; +use std::sync::{Arc, Mutex}; +use std::thread; + +use anyhow::{anyhow, Result}; +use boltz_client::swaps::{ + boltz::{RevSwapStates, SubSwapStates}, + boltzv2::{Subscription, SwapUpdate}, +}; +use boltz_client::SwapType; +use log::{error, info, warn}; +use tungstenite::stream::MaybeTlsStream; +use tungstenite::{Message, WebSocket}; + +use crate::model::*; +use crate::sdk::LiquidSdk; + +pub(super) struct BoltzStatusStream { + // socket: WebSocket>, + swap_in_ids: Arc>>, + swap_out_ids: Arc>>, +} +impl BoltzStatusStream { + pub(super) fn new() -> Self { + BoltzStatusStream { + swap_in_ids: Default::default(), + swap_out_ids: Default::default(), + } + } + + pub(super) fn insert_tracked_swap(&self, id: &str, swap_type: SwapType) { + match swap_type { + SwapType::Submarine => self.swap_in_ids.lock().unwrap().insert(id.to_string()), + SwapType::ReverseSubmarine => self.swap_out_ids.lock().unwrap().insert(id.to_string()), + }; + } + + pub(super) fn resolve_tracked_swap(&self, id: &str, swap_type: SwapType) { + match swap_type { + SwapType::Submarine => self.swap_in_ids.lock().unwrap().remove(id), + SwapType::ReverseSubmarine => self.swap_out_ids.lock().unwrap().remove(id), + }; + } + + pub(super) fn track_pending_swaps(&self, sdk: Arc) -> Result<()> { + // Track subscribed swap IDs + let mut socket = sdk + .boltz_client_v2() + .connect_ws() + .map_err(|e| anyhow!("Failed to connect to websocket: {e:?}"))?; + + // Set underlying TCP stream to nonblocking mode + match socket.get_mut() { + tungstenite::stream::MaybeTlsStream::Plain(s) => s.set_nonblocking(true)?, + tungstenite::stream::MaybeTlsStream::NativeTls(s) => { + s.get_mut().set_nonblocking(true)? + } + _ => Err(anyhow!("Unsupported stream type"))?, + }; + + let swap_in_ids = self.swap_in_ids.clone(); + let swap_out_ids = self.swap_out_ids.clone(); + + thread::spawn(move || loop { + let maybe_subscribe_fn = + |ongoing_swap: &OngoingSwap, socket: &mut WebSocket>| { + let id = ongoing_swap.id(); + + let is_ongoing_swap_already_tracked = match ongoing_swap { + OngoingSwap::Send(_) => swap_in_ids.lock().unwrap().contains(&id), + OngoingSwap::Receive(_) => swap_out_ids.lock().unwrap().contains(&id), + }; + + if !is_ongoing_swap_already_tracked { + info!("Subscribing to status for ongoing swap ID {id}"); + + let subscription = Subscription::new(&id); + let subscribe_json = serde_json::to_string(&subscription) + .map_err(|e| anyhow!("Invalid subscription msg: {e:?}")) + .unwrap(); + socket + .send(tungstenite::Message::Text(subscribe_json)) + .map_err(|e| anyhow!("Failed to subscribe to {id}: {e:?}")) + .unwrap(); + + match ongoing_swap { + OngoingSwap::Send(_) => swap_in_ids.lock().unwrap().insert(id), + OngoingSwap::Receive(_) => swap_out_ids.lock().unwrap().insert(id), + }; + } + }; + + // Initially subscribe to all ongoing swaps + match sdk.list_ongoing_swaps() { + Ok(initial_ongoing_swaps) => { + info!("Got {} initial ongoing swaps", initial_ongoing_swaps.len()); + for ongoing_swap in &initial_ongoing_swaps { + maybe_subscribe_fn(ongoing_swap, &mut socket); + } + } + Err(e) => error!("Failed to list initial ongoing swaps: {e:?}"), + } + + loop { + match &socket.read() { + Ok(Message::Close(_)) => { + warn!("Received close msg, exiting socket loop"); + break; + } + Ok(msg) => { + info!("Received msg : {msg:?}"); + + // Each time socket.read() returns, we have the opportunity to socket.send(). + // We use this window to subscribe to any new ongoing swaps. + // This happens on any non-close socket messages, in particular: + // Ping (periodic keep-alive), Text (status update) + match sdk.list_ongoing_swaps() { + Ok(ongoing_swaps) => { + for ongoing_swap in &ongoing_swaps { + maybe_subscribe_fn(ongoing_swap, &mut socket); + } + } + Err(e) => error!("Failed to list new ongoing swaps: {e:?}"), + } + + // We parse and handle any Text websocket messages, which are likely status updates + if msg.is_text() { + let response: SwapUpdate = serde_json::from_str(&msg.to_string()) + .map_err(|e| anyhow!("WS response is invalid SwapUpdate: {e:?}")) + .unwrap(); + info!("Received update : {response:?}"); + + match response { + // Subscription confirmation + boltz_client::swaps::boltzv2::SwapUpdate::Subscription { + .. + } => {} + + // Status update + boltz_client::swaps::boltzv2::SwapUpdate::Update { + event: _, + channel: _, + args, + } => { + let update = args.first().unwrap().clone(); // TODO + let update_swap_id = update.id.clone(); + let update_state_str = update.status.clone(); + + if swap_in_ids.lock().unwrap().contains(&update_swap_id) { + // Known OngoingSwapIn / Send swap + + match SubSwapStates::from_str(&update_state_str) { + Ok(new_state) => { + let res = sdk.try_handle_submarine_swap_status( + new_state, + &update_swap_id, + ); + info!("ongoingswapin / send try_handle_submarine_swap_status res: {res:?}"); + } + Err(_) => error!("Invalid state for submarine swap {update_swap_id}: {update_state_str}") + } + } else if swap_out_ids.lock().unwrap().contains(&update_swap_id) + { + // Known OngoingSwapOut / receive swap + + match RevSwapStates::from_str(&update_state_str) { + Ok(new_state) => { + let res = sdk.try_handle_reverse_swap_status( + new_state, + &update_swap_id, + ); + info!("OngoingSwapOut / receive try_handle_reverse_swap_status res: {res:?}"); + } + Err(_) => error!("Invalid state for reverse swap {update_swap_id}: {update_state_str}") + } + } else { + // We got an update for a swap we did not track as ongoing + todo!() + } + } + + // Error related to subscription, like "Unknown swap ID" + boltz_client::swaps::boltzv2::SwapUpdate::Error { .. } => todo!(), + } + } + } + Err(tungstenite::Error::Io(io_err)) => { + match io_err.kind() { + // Calling socket.read() on a non-blocking stream when there is nothing + // to read results in an WouldBlock error. In this case, we do nothing + // and continue the loop. + ErrorKind::WouldBlock => {} + _ => { + error!("Received stream IO error : {io_err:?}"); + break; + } + } + } + Err(e) => { + error!("Received stream error : {e:?}"); + break; + } + } + } + }); + + Ok(()) + } +} diff --git a/lib/core/src/error.rs b/lib/core/src/error.rs index afe54a6..27bed16 100644 --- a/lib/core/src/error.rs +++ b/lib/core/src/error.rs @@ -24,15 +24,21 @@ impl From for LiquidSdkError { #[derive(thiserror::Error, Debug)] pub enum PaymentError { - #[error("Invoice amount is out of range")] - AmountOutOfRange, - #[error("The specified funds have already been claimed")] AlreadyClaimed, + #[error("Invoice amount is out of range")] + AmountOutOfRange, + #[error("Generic error: {err}")] Generic { err: String }, + #[error("The provided fees have expired")] + InvalidOrExpiredFees, + + #[error("Cannot pay: not enough funds")] + InsufficientFunds, + #[error("The specified invoice is not valid")] InvalidInvoice, @@ -48,6 +54,9 @@ pub enum PaymentError { #[error("Could not store the swap details locally")] PersistError, + #[error("The payment has been refunded. Reason for failure: {err}")] + Refunded { err: String, txid: String }, + #[error("Could not sign/send the transaction: {err}")] SendError { err: String }, @@ -65,6 +74,13 @@ impl From for PaymentError { PaymentError::Generic { err: msg } } + boltz_client::error::Error::HTTP(ureq) => { + dbg!(ureq.into_response().unwrap().into_string().unwrap()); + + PaymentError::Generic { + err: "Could not contact servers".to_string(), + } + } _ => PaymentError::Generic { err: format!("{err:?}"), }, diff --git a/lib/core/src/frb/bridge.io.rs b/lib/core/src/frb/bridge.io.rs index 772f1df..ce276ad 100644 --- a/lib/core/src/frb/bridge.io.rs +++ b/lib/core/src/frb/bridge.io.rs @@ -4,7 +4,6 @@ // Section: imports use super::*; -use crate::bindings::*; use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use flutter_rust_bridge::for_generated::transform_result_dco; use flutter_rust_bridge::{Handler, IntoIntoDart}; @@ -23,30 +22,6 @@ impl CstDecode unimplemented!() } } -impl CstDecode for usize { - // Codec=Cst (C-struct based), see doc to use other codecs - fn cst_decode(self) -> LBtcReverseRecovery { - CstDecode::< - RustOpaqueNom< - flutter_rust_bridge::for_generated::RustAutoOpaqueInner, - >, - >::cst_decode(self) - .rust_auto_opaque_decode_owned() - } -} -impl - CstDecode< - RustOpaqueNom>, - > for usize -{ - // Codec=Cst (C-struct based), see doc to use other codecs - fn cst_decode( - self, - ) -> RustOpaqueNom> - { - unsafe { decode_rust_opaque_nom(self as _) } - } -} 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 { @@ -178,31 +153,40 @@ impl CstDecode for wire_cst_payment_error { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::error::PaymentError { match self.tag { - 0 => crate::error::PaymentError::AmountOutOfRange, - 1 => crate::error::PaymentError::AlreadyClaimed, + 0 => crate::error::PaymentError::AlreadyClaimed, + 1 => crate::error::PaymentError::AmountOutOfRange, 2 => { let ans = unsafe { self.kind.Generic }; crate::error::PaymentError::Generic { err: ans.err.cst_decode(), } } - 3 => crate::error::PaymentError::InvalidInvoice, - 4 => crate::error::PaymentError::InvalidPreimage, - 5 => { + 3 => crate::error::PaymentError::InvalidOrExpiredFees, + 4 => crate::error::PaymentError::InsufficientFunds, + 5 => crate::error::PaymentError::InvalidInvoice, + 6 => crate::error::PaymentError::InvalidPreimage, + 7 => { let ans = unsafe { self.kind.LwkError }; crate::error::PaymentError::LwkError { err: ans.err.cst_decode(), } } - 6 => crate::error::PaymentError::PairsNotFound, - 7 => crate::error::PaymentError::PersistError, - 8 => { + 8 => crate::error::PaymentError::PairsNotFound, + 9 => crate::error::PaymentError::PersistError, + 10 => { + let ans = unsafe { self.kind.Refunded }; + crate::error::PaymentError::Refunded { + err: ans.err.cst_decode(), + txid: ans.txid.cst_decode(), + } + } + 11 => { let ans = unsafe { self.kind.SendError }; crate::error::PaymentError::SendError { err: ans.err.cst_decode(), } } - 9 => { + 12 => { let ans = unsafe { self.kind.SignerError }; crate::error::PaymentError::SignerError { err: ans.err.cst_decode(), @@ -224,7 +208,6 @@ impl CstDecode for wire_cst_prepare_receiv // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::PrepareReceiveResponse { crate::model::PrepareReceiveResponse { - pair_hash: self.pair_hash.cst_decode(), payer_amount_sat: self.payer_amount_sat.cst_decode(), fees_sat: self.fees_sat.cst_decode(), } @@ -242,12 +225,8 @@ impl CstDecode for wire_cst_prepare_send_resp // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::PrepareSendResponse { crate::model::PrepareSendResponse { - id: self.id.cst_decode(), - payer_amount_sat: self.payer_amount_sat.cst_decode(), - receiver_amount_sat: self.receiver_amount_sat.cst_decode(), - total_fees: self.total_fees.cst_decode(), - funding_address: self.funding_address.cst_decode(), invoice: self.invoice.cst_decode(), + fees_sat: self.fees_sat.cst_decode(), } } } @@ -360,7 +339,6 @@ impl Default for wire_cst_prepare_receive_request { impl NewWithNullPtr for wire_cst_prepare_receive_response { fn new_with_null_ptr() -> Self { Self { - pair_hash: core::ptr::null_mut(), payer_amount_sat: Default::default(), fees_sat: Default::default(), } @@ -386,12 +364,8 @@ impl Default for wire_cst_prepare_send_request { impl NewWithNullPtr for wire_cst_prepare_send_response { fn new_with_null_ptr() -> Self { Self { - id: core::ptr::null_mut(), - payer_amount_sat: Default::default(), - receiver_amount_sat: Default::default(), - total_fees: Default::default(), - funding_address: core::ptr::null_mut(), invoice: core::ptr::null_mut(), + fees_sat: Default::default(), } } } @@ -494,11 +468,6 @@ pub extern "C" fn frbgen_breez_liquid_wire_receive_payment( wire_receive_payment_impl(port_, req) } -#[no_mangle] -pub extern "C" fn frbgen_breez_liquid_wire_recover_funds(port_: i64, recovery: usize) { - wire_recover_funds_impl(port_, recovery) -} - #[no_mangle] pub extern "C" fn frbgen_breez_liquid_wire_restore(port_: i64, req: *mut wire_cst_restore_request) { wire_restore_impl(port_, req) @@ -512,24 +481,6 @@ pub extern "C" fn frbgen_breez_liquid_wire_send_payment( wire_send_payment_impl(port_, req) } -#[no_mangle] -pub extern "C" fn frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ptr: *const std::ffi::c_void, -) { - unsafe { - StdArc::>::increment_strong_count(ptr as _); - } -} - -#[no_mangle] -pub extern "C" fn frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ptr: *const std::ffi::c_void, -) { - unsafe { - StdArc::>::decrement_strong_count(ptr as _); - } -} - #[no_mangle] pub extern "C" fn frbgen_breez_liquid_cst_new_box_autoadd_connect_request( ) -> *mut wire_cst_connect_request { @@ -670,6 +621,7 @@ pub struct wire_cst_payment_error { pub union PaymentErrorKind { Generic: wire_cst_PaymentError_Generic, LwkError: wire_cst_PaymentError_LwkError, + Refunded: wire_cst_PaymentError_Refunded, SendError: wire_cst_PaymentError_SendError, SignerError: wire_cst_PaymentError_SignerError, nil__: (), @@ -686,6 +638,12 @@ pub struct wire_cst_PaymentError_LwkError { } #[repr(C)] #[derive(Clone, Copy)] +pub struct wire_cst_PaymentError_Refunded { + err: *mut wire_cst_list_prim_u_8_strict, + txid: *mut wire_cst_list_prim_u_8_strict, +} +#[repr(C)] +#[derive(Clone, Copy)] pub struct wire_cst_PaymentError_SendError { err: *mut wire_cst_list_prim_u_8_strict, } @@ -702,7 +660,6 @@ pub struct wire_cst_prepare_receive_request { #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_prepare_receive_response { - pair_hash: *mut wire_cst_list_prim_u_8_strict, payer_amount_sat: u64, fees_sat: u64, } @@ -714,12 +671,8 @@ pub struct wire_cst_prepare_send_request { #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_prepare_send_response { - id: *mut wire_cst_list_prim_u_8_strict, - payer_amount_sat: u64, - receiver_amount_sat: u64, - total_fees: u64, - funding_address: *mut wire_cst_list_prim_u_8_strict, invoice: *mut wire_cst_list_prim_u_8_strict, + fees_sat: u64, } #[repr(C)] #[derive(Clone, Copy)] diff --git a/lib/core/src/frb/bridge.rs b/lib/core/src/frb/bridge.rs index 32ffddb..25a1212 100644 --- a/lib/core/src/frb/bridge.rs +++ b/lib/core/src/frb/bridge.rs @@ -19,7 +19,6 @@ // Section: imports -use crate::bindings::*; use flutter_rust_bridge::for_generated::byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt}; use flutter_rust_bridge::for_generated::transform_result_dco; use flutter_rust_bridge::{Handler, IntoIntoDart}; @@ -32,7 +31,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.33"; -pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -1225779344; +pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -451265040; // Section: executor @@ -170,24 +169,6 @@ fn wire_receive_payment_impl( }, ) } -fn wire_recover_funds_impl( - port_: flutter_rust_bridge::for_generated::MessagePort, - recovery: impl CstDecode, -) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::( - flutter_rust_bridge::for_generated::TaskInfo { - debug_name: "recover_funds", - port: Some(port_), - mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal, - }, - move || { - let api_recovery = recovery.cst_decode(); - move |context| { - transform_result_dco((move || crate::bindings::recover_funds(api_recovery))()) - } - }, - ) -} fn wire_restore_impl( port_: flutter_rust_bridge::for_generated::MessagePort, req: impl CstDecode, @@ -275,12 +256,6 @@ impl CstDecode for u8 { self } } -impl CstDecode for usize { - // Codec=Cst (C-struct based), see doc to use other codecs - fn cst_decode(self) -> 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 { @@ -288,26 +263,6 @@ impl SseDecode for flutter_rust_bridge::for_generated::anyhow::Error { } } -impl SseDecode for LBtcReverseRecovery { - // 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 inner.rust_auto_opaque_decode_owned(); - } -} - -impl SseDecode - for RustOpaqueNom> -{ - // 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 unsafe { decode_rust_opaque_nom(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 { @@ -461,36 +416,50 @@ impl SseDecode for crate::error::PaymentError { let mut tag_ = ::sse_decode(deserializer); match tag_ { 0 => { - return crate::error::PaymentError::AmountOutOfRange; + return crate::error::PaymentError::AlreadyClaimed; } 1 => { - return crate::error::PaymentError::AlreadyClaimed; + return crate::error::PaymentError::AmountOutOfRange; } 2 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::Generic { err: var_err }; } 3 => { - return crate::error::PaymentError::InvalidInvoice; + return crate::error::PaymentError::InvalidOrExpiredFees; } 4 => { - return crate::error::PaymentError::InvalidPreimage; + return crate::error::PaymentError::InsufficientFunds; } 5 => { + return crate::error::PaymentError::InvalidInvoice; + } + 6 => { + return crate::error::PaymentError::InvalidPreimage; + } + 7 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::LwkError { err: var_err }; } - 6 => { + 8 => { return crate::error::PaymentError::PairsNotFound; } - 7 => { + 9 => { return crate::error::PaymentError::PersistError; } - 8 => { + 10 => { + let mut var_err = ::sse_decode(deserializer); + let mut var_txid = ::sse_decode(deserializer); + return crate::error::PaymentError::Refunded { + err: var_err, + txid: var_txid, + }; + } + 11 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::SendError { err: var_err }; } - 9 => { + 12 => { let mut var_err = ::sse_decode(deserializer); return crate::error::PaymentError::SignerError { err: var_err }; } @@ -528,11 +497,9 @@ impl SseDecode for crate::model::PrepareReceiveRequest { impl SseDecode for crate::model::PrepareReceiveResponse { // 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_pairHash = ::sse_decode(deserializer); let mut var_payerAmountSat = ::sse_decode(deserializer); let mut var_feesSat = ::sse_decode(deserializer); return crate::model::PrepareReceiveResponse { - pair_hash: var_pairHash, payer_amount_sat: var_payerAmountSat, fees_sat: var_feesSat, }; @@ -552,19 +519,11 @@ impl SseDecode for crate::model::PrepareSendRequest { impl SseDecode for crate::model::PrepareSendResponse { // 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_id = ::sse_decode(deserializer); - let mut var_payerAmountSat = ::sse_decode(deserializer); - let mut var_receiverAmountSat = ::sse_decode(deserializer); - let mut var_totalFees = ::sse_decode(deserializer); - let mut var_fundingAddress = ::sse_decode(deserializer); let mut var_invoice = ::sse_decode(deserializer); + let mut var_feesSat = ::sse_decode(deserializer); return crate::model::PrepareSendResponse { - id: var_id, - payer_amount_sat: var_payerAmountSat, - receiver_amount_sat: var_receiverAmountSat, - total_fees: var_totalFees, - funding_address: var_fundingAddress, invoice: var_invoice, + fees_sat: var_feesSat, }; } } @@ -625,13 +584,6 @@ impl SseDecode for () { fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self {} } -impl SseDecode for usize { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - deserializer.cursor.read_u64::().unwrap() as _ - } -} - fn pde_ffi_dispatcher_primary_impl( func_id: i32, port: flutter_rust_bridge::for_generated::MessagePort, @@ -659,24 +611,6 @@ fn pde_ffi_dispatcher_sync_impl( // Section: rust2dart -// Codec=Dco (DartCObject based), see doc to use other codecs -impl flutter_rust_bridge::IntoDart for FrbWrapper { - fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { - flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, StdArc<_>>(self.0) - .into_dart() - } -} -impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive - for FrbWrapper -{ -} - -impl flutter_rust_bridge::IntoIntoDart> for LBtcReverseRecovery { - fn into_into_dart(self) -> FrbWrapper { - self.into() - } -} - // Codec=Dco (DartCObject based), see doc to use other codecs impl flutter_rust_bridge::IntoDart for crate::model::ConnectRequest { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { @@ -767,23 +701,31 @@ impl flutter_rust_bridge::IntoIntoDart for crate::model:: impl flutter_rust_bridge::IntoDart for crate::error::PaymentError { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { match self { - crate::error::PaymentError::AmountOutOfRange => [0.into_dart()].into_dart(), - crate::error::PaymentError::AlreadyClaimed => [1.into_dart()].into_dart(), + crate::error::PaymentError::AlreadyClaimed => [0.into_dart()].into_dart(), + crate::error::PaymentError::AmountOutOfRange => [1.into_dart()].into_dart(), crate::error::PaymentError::Generic { err } => { [2.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::InvalidInvoice => [3.into_dart()].into_dart(), - crate::error::PaymentError::InvalidPreimage => [4.into_dart()].into_dart(), + crate::error::PaymentError::InvalidOrExpiredFees => [3.into_dart()].into_dart(), + crate::error::PaymentError::InsufficientFunds => [4.into_dart()].into_dart(), + crate::error::PaymentError::InvalidInvoice => [5.into_dart()].into_dart(), + crate::error::PaymentError::InvalidPreimage => [6.into_dart()].into_dart(), crate::error::PaymentError::LwkError { err } => { - [5.into_dart(), err.into_into_dart().into_dart()].into_dart() + [7.into_dart(), err.into_into_dart().into_dart()].into_dart() } - crate::error::PaymentError::PairsNotFound => [6.into_dart()].into_dart(), - crate::error::PaymentError::PersistError => [7.into_dart()].into_dart(), + crate::error::PaymentError::PairsNotFound => [8.into_dart()].into_dart(), + crate::error::PaymentError::PersistError => [9.into_dart()].into_dart(), + crate::error::PaymentError::Refunded { err, txid } => [ + 10.into_dart(), + err.into_into_dart().into_dart(), + txid.into_into_dart().into_dart(), + ] + .into_dart(), crate::error::PaymentError::SendError { err } => { - [8.into_dart(), err.into_into_dart().into_dart()].into_dart() + [11.into_dart(), err.into_into_dart().into_dart()].into_dart() } crate::error::PaymentError::SignerError { err } => { - [9.into_dart(), err.into_into_dart().into_dart()].into_dart() + [12.into_dart(), err.into_into_dart().into_dart()].into_dart() } } } @@ -832,7 +774,6 @@ impl flutter_rust_bridge::IntoIntoDart impl flutter_rust_bridge::IntoDart for crate::model::PrepareReceiveResponse { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ - self.pair_hash.into_into_dart().into_dart(), self.payer_amount_sat.into_into_dart().into_dart(), self.fees_sat.into_into_dart().into_dart(), ] @@ -871,12 +812,8 @@ impl flutter_rust_bridge::IntoIntoDart impl flutter_rust_bridge::IntoDart for crate::model::PrepareSendResponse { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ - self.id.into_into_dart().into_dart(), - self.payer_amount_sat.into_into_dart().into_dart(), - self.receiver_amount_sat.into_into_dart().into_dart(), - self.total_fees.into_into_dart().into_dart(), - self.funding_address.into_into_dart().into_dart(), self.invoice.into_into_dart().into_dart(), + self.fees_sat.into_into_dart().into_dart(), ] .into_dart() } @@ -952,24 +889,6 @@ impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error { } } -impl SseEncode for LBtcReverseRecovery { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - >>::sse_encode(flutter_rust_bridge::for_generated::rust_auto_opaque_encode::<_, StdArc<_>>(self), serializer); - } -} - -impl SseEncode - for RustOpaqueNom> -{ - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - let (ptr, size) = self.sse_encode_raw(); - ::sse_encode(ptr, serializer); - ::sse_encode(size, serializer); - } -} - 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) { @@ -1097,38 +1016,49 @@ impl SseEncode for crate::error::PaymentError { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { match self { - crate::error::PaymentError::AmountOutOfRange => { + crate::error::PaymentError::AlreadyClaimed => { ::sse_encode(0, serializer); } - crate::error::PaymentError::AlreadyClaimed => { + crate::error::PaymentError::AmountOutOfRange => { ::sse_encode(1, serializer); } crate::error::PaymentError::Generic { err } => { ::sse_encode(2, serializer); ::sse_encode(err, serializer); } - crate::error::PaymentError::InvalidInvoice => { + crate::error::PaymentError::InvalidOrExpiredFees => { ::sse_encode(3, serializer); } - crate::error::PaymentError::InvalidPreimage => { + crate::error::PaymentError::InsufficientFunds => { ::sse_encode(4, serializer); } - crate::error::PaymentError::LwkError { err } => { + crate::error::PaymentError::InvalidInvoice => { ::sse_encode(5, serializer); + } + crate::error::PaymentError::InvalidPreimage => { + ::sse_encode(6, serializer); + } + crate::error::PaymentError::LwkError { err } => { + ::sse_encode(7, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::PairsNotFound => { - ::sse_encode(6, serializer); + ::sse_encode(8, serializer); } crate::error::PaymentError::PersistError => { - ::sse_encode(7, serializer); + ::sse_encode(9, serializer); + } + crate::error::PaymentError::Refunded { err, txid } => { + ::sse_encode(10, serializer); + ::sse_encode(err, serializer); + ::sse_encode(txid, serializer); } crate::error::PaymentError::SendError { err } => { - ::sse_encode(8, serializer); + ::sse_encode(11, serializer); ::sse_encode(err, serializer); } crate::error::PaymentError::SignerError { err } => { - ::sse_encode(9, serializer); + ::sse_encode(12, serializer); ::sse_encode(err, serializer); } } @@ -1163,7 +1093,6 @@ impl SseEncode for crate::model::PrepareReceiveRequest { impl SseEncode for crate::model::PrepareReceiveResponse { // 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.pair_hash, serializer); ::sse_encode(self.payer_amount_sat, serializer); ::sse_encode(self.fees_sat, serializer); } @@ -1179,12 +1108,8 @@ impl SseEncode for crate::model::PrepareSendRequest { impl SseEncode for crate::model::PrepareSendResponse { // 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.id, serializer); - ::sse_encode(self.payer_amount_sat, serializer); - ::sse_encode(self.receiver_amount_sat, serializer); - ::sse_encode(self.total_fees, serializer); - ::sse_encode(self.funding_address, serializer); ::sse_encode(self.invoice, serializer); + ::sse_encode(self.fees_sat, serializer); } } @@ -1236,16 +1161,6 @@ impl SseEncode for () { fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) {} } -impl SseEncode for usize { - // Codec=Sse (Serialization based), see doc to use other codecs - fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - serializer - .cursor - .write_u64::(self as _) - .unwrap(); - } -} - #[cfg(not(target_family = "wasm"))] #[path = "bridge.io.rs"] mod io; diff --git a/lib/core/src/lib.rs b/lib/core/src/lib.rs index bf40ec2..5e202cb 100644 --- a/lib/core/src/lib.rs +++ b/lib/core/src/lib.rs @@ -1,8 +1,10 @@ #[cfg(feature = "frb")] pub mod bindings; +pub(crate) mod boltz_status_stream; pub mod error; #[cfg(feature = "frb")] pub mod frb; pub mod model; pub mod persist; pub mod sdk; +pub(crate) mod utils; diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 9f9e631..109a678 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -86,7 +86,6 @@ pub struct PrepareReceiveRequest { #[derive(Debug, Serialize)] pub struct PrepareReceiveResponse { - pub pair_hash: String, pub payer_amount_sat: u64, pub fees_sat: u64, } @@ -104,12 +103,8 @@ pub struct PrepareSendRequest { #[derive(Debug, Serialize, Clone)] pub struct PrepareSendResponse { - pub id: String, - pub payer_amount_sat: u64, - pub receiver_amount_sat: u64, - pub total_fees: u64, - pub funding_address: String, pub invoice: String, + pub fees_sat: u64, } #[derive(Debug, Serialize)] @@ -133,23 +128,38 @@ pub struct RestoreRequest { pub backup_path: Option, } -#[derive(Debug)] +#[derive(Clone, Debug)] pub(crate) enum OngoingSwap { - Send { - id: String, - funding_address: String, - invoice: String, - receiver_amount_sat: u64, - txid: Option, - }, - Receive { - id: String, - preimage: String, - redeem_script: String, - blinding_key: String, - invoice: String, - receiver_amount_sat: u64, - }, + Send(OngoingSwapIn), + Receive(OngoingSwapOut), +} +impl OngoingSwap { + pub(crate) fn id(&self) -> String { + match &self { + OngoingSwap::Send(OngoingSwapIn { id, .. }) + | OngoingSwap::Receive(OngoingSwapOut { id, .. }) => id.clone(), + } + } +} + +#[derive(Clone, Debug)] +pub(crate) struct OngoingSwapIn { + pub(crate) id: String, + pub(crate) invoice: String, + pub(crate) payer_amount_sat: u64, + pub(crate) create_response_json: String, + pub(crate) lockup_txid: Option, +} + +#[derive(Clone, Debug)] +pub(crate) struct OngoingSwapOut { + pub(crate) id: String, + pub(crate) preimage: String, + pub(crate) redeem_script: String, + pub(crate) blinding_key: String, + pub(crate) invoice: String, + pub(crate) receiver_amount_sat: u64, + pub(crate) claim_fees_sat: u64, } #[derive(Debug, Clone, PartialEq, Serialize)] @@ -169,33 +179,32 @@ pub struct Payment { #[serde(rename(serialize = "type"))] pub payment_type: PaymentType, - /// Only for [PaymentType::PendingReceive] pub invoice: Option, } impl From for Payment { fn from(swap: OngoingSwap) -> Self { match swap { - OngoingSwap::Send { + OngoingSwap::Send(OngoingSwapIn { invoice, - receiver_amount_sat, + payer_amount_sat, .. - } => { - let payer_amount_sat = get_invoice_amount!(invoice); + }) => { + let receiver_amount_sat = get_invoice_amount!(invoice); Payment { id: None, timestamp: None, payment_type: PaymentType::PendingSend, amount_sat: payer_amount_sat, invoice: Some(invoice), - fees_sat: Some(receiver_amount_sat - payer_amount_sat), + fees_sat: Some(payer_amount_sat - receiver_amount_sat), } } - OngoingSwap::Receive { + OngoingSwap::Receive(OngoingSwapOut { receiver_amount_sat, invoice, .. - } => { + }) => { let payer_amount_sat = get_invoice_amount!(invoice); Payment { id: None, @@ -212,6 +221,7 @@ impl From for Payment { pub(crate) struct PaymentData { pub payer_amount_sat: u64, + pub receiver_amount_sat: u64, } #[macro_export] diff --git a/lib/core/src/persist/migrations.rs b/lib/core/src/persist/migrations.rs index 2bad5b4..fe7e25a 100644 --- a/lib/core/src/persist/migrations.rs +++ b/lib/core/src/persist/migrations.rs @@ -7,19 +7,21 @@ pub(crate) fn current_migrations() -> Vec<&'static str> { blinding_key TEXT NOT NULL, invoice TEXT NOT NULL, receiver_amount_sat INTEGER NOT NULL, - created_at TEXT DEFAULT CURRENT_TIMESTAMP - ) STRICT;", - "CREATE TABLE IF NOT EXISTS ongoing_send_swaps ( - id TEXT NOT NULL PRIMARY KEY, - funding_address TEXT NOT NULL, - invoice TEXT NOT NULL, - receiver_amount_sat INTEGER NOT NULL, - txid TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + claim_fees_sat INTEGER NOT NULL ) STRICT;", + "CREATE TABLE IF NOT EXISTS ongoing_send_swaps( + id TEXT NOT NULL PRIMARY KEY, + invoice TEXT NOT NULL, + payer_amount_sat INTEGER NOT NULL, + create_response_json TEXT NOT NULL, + lockup_txid TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP + ) STRICT;", "CREATE TABLE IF NOT EXISTS payment_data( id TEXT NOT NULL PRIMARY KEY, - payer_amount_sat INTEGER NOT NULL + payer_amount_sat INTEGER NOT NULL, + receiver_amount_sat INTEGER NOT NULL ) STRICT;", ] } diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index f66649b..7cd8c05 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -1,5 +1,7 @@ mod backup; mod migrations; +mod swap_in; +mod swap_out; use std::{collections::HashMap, fs::create_dir_all, path::PathBuf, str::FromStr}; @@ -8,7 +10,7 @@ use migrations::current_migrations; use rusqlite::{params, Connection}; use rusqlite_migration::{Migrations, M}; -use crate::model::{Network, Network::*, OngoingSwap, PaymentData}; +use crate::model::{Network::*, *}; pub(crate) struct Persister { main_db_dir: PathBuf, @@ -47,69 +49,6 @@ impl Persister { Ok(()) } - pub fn insert_or_update_ongoing_swap(&self, swaps: &[OngoingSwap]) -> Result<()> { - let con = self.get_connection()?; - - for swap in swaps { - match swap { - OngoingSwap::Send { - id, - funding_address, - invoice, - receiver_amount_sat, - txid, - } => { - let mut stmt = con.prepare( - " - INSERT OR REPLACE INTO ongoing_send_swaps ( - id, - funding_address, - invoice, - receiver_amount_sat, - txid - ) - VALUES (?, ?, ?, ?, ?) - ", - )?; - _ = stmt.execute((id, funding_address, invoice, receiver_amount_sat, txid))? - } - OngoingSwap::Receive { - id, - preimage, - redeem_script, - blinding_key, - invoice, - receiver_amount_sat, - } => { - let mut stmt = con.prepare( - " - INSERT OR REPLACE INTO ongoing_receive_swaps ( - id, - preimage, - redeem_script, - blinding_key, - invoice, - receiver_amount_sat - ) - VALUES (?, ?, ?, ?, ?, ?) - ", - )?; - - _ = stmt.execute(( - id, - preimage, - redeem_script, - blinding_key, - invoice, - receiver_amount_sat, - ))? - } - } - } - - Ok(()) - } - pub fn resolve_ongoing_swap( &self, id: &str, @@ -125,9 +64,13 @@ impl Persister { )?; if let Some((txid, payment_data)) = payment_data { tx.execute( - "INSERT INTO payment_data(id, payer_amount_sat) - VALUES(?, ?)", - (txid, payment_data.payer_amount_sat), + "INSERT INTO payment_data(id, payer_amount_sat, receiver_amount_sat) + VALUES(?, ?, ?)", + ( + txid, + payment_data.payer_amount_sat, + payment_data.receiver_amount_sat, + ), )?; } tx.commit()?; @@ -135,75 +78,19 @@ impl Persister { Ok(()) } - pub fn list_ongoing_swaps(&self) -> Result> { + pub(crate) fn list_ongoing_swaps(&self) -> Result> { let con = self.get_connection()?; - let mut ongoing_swaps = self.list_ongoing_send(&con)?; - ongoing_swaps.append(&mut self.list_ongoing_receive(&con)?); - Ok(ongoing_swaps) - } - - fn list_ongoing_send(&self, con: &Connection) -> Result, rusqlite::Error> { - let mut stmt = con.prepare( - " - SELECT - id, - funding_address, - invoice, - receiver_amount_sat, - txid, - created_at - FROM ongoing_send_swaps - ORDER BY created_at - ", - )?; - - let ongoing_send = stmt - .query_map(params![], |row| { - Ok(OngoingSwap::Send { - id: row.get(0)?, - funding_address: row.get(1)?, - invoice: row.get(2)?, - receiver_amount_sat: row.get(3)?, - txid: row.get(4)?, - }) - })? - .map(|i| i.unwrap()) + let ongoing_swap_ins: Vec = self + .list_ongoing_send(&con, vec![])? + .into_iter() + .map(OngoingSwap::Send) .collect(); - - Ok(ongoing_send) - } - - fn list_ongoing_receive(&self, con: &Connection) -> Result, rusqlite::Error> { - let mut stmt = con.prepare( - " - SELECT - id, - preimage, - redeem_script, - blinding_key, - invoice, - receiver_amount_sat, - created_at - FROM ongoing_receive_swaps - ORDER BY created_at - ", - )?; - - let ongoing_receive = stmt - .query_map(params![], |row| { - Ok(OngoingSwap::Receive { - id: row.get(0)?, - preimage: row.get(1)?, - redeem_script: row.get(2)?, - blinding_key: row.get(3)?, - invoice: row.get(4)?, - receiver_amount_sat: row.get(5)?, - }) - })? - .map(|i| i.unwrap()) + let ongoing_swap_outs: Vec = self + .list_ongoing_receive(&con, vec![])? + .into_iter() + .map(OngoingSwap::Receive) .collect(); - - Ok(ongoing_receive) + Ok([ongoing_swap_ins, ongoing_swap_outs].concat()) } pub fn get_payment_data(&self) -> Result> { @@ -211,7 +98,7 @@ impl Persister { let mut stmt = con.prepare( " - SELECT id, payer_amount_sat + SELECT id, payer_amount_sat, receiver_amount_sat FROM payment_data ", )?; @@ -222,6 +109,7 @@ impl Persister { row.get(0)?, PaymentData { payer_amount_sat: row.get(1)?, + receiver_amount_sat: row.get(2)?, }, )) })? diff --git a/lib/core/src/persist/swap_in.rs b/lib/core/src/persist/swap_in.rs new file mode 100644 index 0000000..304202c --- /dev/null +++ b/lib/core/src/persist/swap_in.rs @@ -0,0 +1,88 @@ +use crate::model::*; +use crate::persist::Persister; + +use anyhow::Result; +use rusqlite::{params, Connection, OptionalExtension, Row}; + +impl Persister { + pub(crate) fn insert_or_update_ongoing_swap_in(&self, swap_in: OngoingSwapIn) -> Result<()> { + let con = self.get_connection()?; + + let mut stmt = con.prepare( + " + INSERT OR REPLACE INTO ongoing_send_swaps ( + id, + invoice, + payer_amount_sat, + create_response_json, + lockup_txid + ) + VALUES (?, ?, ?, ?, ?)", + )?; + _ = stmt.execute(( + swap_in.id, + swap_in.invoice, + swap_in.payer_amount_sat, + swap_in.create_response_json, + swap_in.lockup_txid, + ))?; + + Ok(()) + } + + fn list_ongoing_swap_in_query(where_clauses: Vec<&str>) -> String { + let mut where_clause_str = String::new(); + if !where_clauses.is_empty() { + where_clause_str = String::from("WHERE "); + where_clause_str.push_str(where_clauses.join(" AND ").as_str()); + } + + format!( + " + SELECT + id, + invoice, + payer_amount_sat, + create_response_json, + lockup_txid, + created_at + FROM ongoing_send_swaps + {where_clause_str} + ORDER BY created_at + " + ) + } + + pub(crate) fn fetch_ongoing_swap_in( + con: &Connection, + id: &str, + ) -> rusqlite::Result> { + let query = Self::list_ongoing_swap_in_query(vec!["id = ?1"]); + con.query_row(&query, [id], Self::sql_row_to_ongoing_swap_in) + .optional() + } + + fn sql_row_to_ongoing_swap_in(row: &Row) -> rusqlite::Result { + Ok(OngoingSwapIn { + id: row.get(0)?, + invoice: row.get(1)?, + payer_amount_sat: row.get(2)?, + create_response_json: row.get(3)?, + lockup_txid: row.get(4)?, + }) + } + + pub(crate) fn list_ongoing_send( + &self, + con: &Connection, + where_clauses: Vec<&str>, + ) -> rusqlite::Result> { + let query = Self::list_ongoing_swap_in_query(where_clauses); + let ongoing_send = con + .prepare(&query)? + .query_map(params![], Self::sql_row_to_ongoing_swap_in)? + .map(|i| i.unwrap()) + .collect(); + Ok(ongoing_send) + } +} diff --git a/lib/core/src/persist/swap_out.rs b/lib/core/src/persist/swap_out.rs new file mode 100644 index 0000000..52df9b2 --- /dev/null +++ b/lib/core/src/persist/swap_out.rs @@ -0,0 +1,96 @@ +use crate::model::*; +use crate::persist::Persister; + +use anyhow::Result; +use rusqlite::{params, Connection, OptionalExtension, Row}; + +impl Persister { + pub(crate) fn insert_or_update_ongoing_swap_out(&self, swap_out: OngoingSwapOut) -> Result<()> { + let con = self.get_connection()?; + + let mut stmt = con.prepare( + " + INSERT OR REPLACE INTO ongoing_receive_swaps ( + id, + preimage, + redeem_script, + blinding_key, + invoice, + receiver_amount_sat, + claim_fees_sat + ) + VALUES (?, ?, ?, ?, ?, ?, ?)", + )?; + _ = stmt.execute(( + swap_out.id, + swap_out.preimage, + swap_out.redeem_script, + swap_out.blinding_key, + swap_out.invoice, + swap_out.receiver_amount_sat, + swap_out.claim_fees_sat, + ))?; + + Ok(()) + } + + fn list_ongoing_swap_out_query(where_clauses: Vec<&str>) -> String { + let mut where_clause_str = String::new(); + if !where_clauses.is_empty() { + where_clause_str = String::from("WHERE "); + where_clause_str.push_str(where_clauses.join(" AND ").as_str()); + } + + format!( + " + SELECT + id, + preimage, + redeem_script, + blinding_key, + invoice, + receiver_amount_sat, + claim_fees_sat, + created_at + FROM ongoing_receive_swaps + {where_clause_str} + ORDER BY created_at + " + ) + } + + pub(crate) fn fetch_ongoing_swap_out( + con: &Connection, + id: &str, + ) -> rusqlite::Result> { + let query = Self::list_ongoing_swap_out_query(vec!["id = ?1"]); + con.query_row(&query, [id], Self::sql_row_to_ongoing_swap_out) + .optional() + } + + fn sql_row_to_ongoing_swap_out(row: &Row) -> rusqlite::Result { + Ok(OngoingSwapOut { + id: row.get(0)?, + preimage: row.get(1)?, + redeem_script: row.get(2)?, + blinding_key: row.get(3)?, + invoice: row.get(4)?, + receiver_amount_sat: row.get(5)?, + claim_fees_sat: row.get(6)?, + }) + } + + pub(crate) fn list_ongoing_receive( + &self, + con: &Connection, + where_clauses: Vec<&str>, + ) -> rusqlite::Result> { + let query = Self::list_ongoing_swap_out_query(where_clauses); + let ongoing_receive = con + .prepare(&query)? + .query_map(params![], Self::sql_row_to_ongoing_swap_out)? + .map(|i| i.unwrap()) + .collect(); + Ok(ongoing_receive) + } +} diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 5a673c0..3f66169 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -3,7 +3,7 @@ use std::{ path::PathBuf, str::FromStr, sync::{Arc, Mutex}, - thread, + thread::sleep, time::Duration, }; @@ -11,16 +11,15 @@ use anyhow::{anyhow, Result}; use boltz_client::{ network::electrum::ElectrumConfig, swaps::{ - boltz::{ - BoltzApiClient, CreateSwapRequest, RevSwapStates, SubSwapStates, SwapStatusRequest, - BOLTZ_MAINNET_URL, BOLTZ_TESTNET_URL, - }, - liquid::{LBtcSwapScript, LBtcSwapTx}, + boltz::{RevSwapStates, SubSwapStates}, + boltzv2::*, + liquidv2::LBtcSwapTxV2, }, - util::secrets::{LBtcReverseRecovery, LiquidSwapKey, Preimage, SwapKey}, - Bolt11Invoice, Keypair, + util::secrets::{LiquidSwapKey, Preimage, SwapKey}, + Amount, Bolt11Invoice, Keypair, LBtcSwapScriptV2, SwapType, }; -use log::{debug, error, warn}; +use elements::hashes::hex::DisplayHex; +use log::{debug, info, warn}; use lwk_common::{singlesig_desc, Signer, Singlesig}; use lwk_signer::{AnySigner, SwSigner}; use lwk_wollet::{ @@ -29,11 +28,14 @@ use lwk_wollet::{ ElementsNetwork, FsPersister, Wollet as LwkWollet, WolletDescriptor, }; -use crate::{ensure_sdk, error::PaymentError, get_invoice_amount, model::*, persist::Persister}; +use crate::{ + boltz_status_stream::BoltzStatusStream, ensure_sdk, error::PaymentError, get_invoice_amount, + model::*, persist::Persister, utils, +}; -/// Claim tx feerate for Receive, in sats per vbyte. +/// Claim tx feerate, in sats per vbyte. /// Since the Liquid blocks are consistently empty for now, we hardcode the minimum feerate. -pub const LIQUID_CLAIM_TX_FEERATE: f32 = 0.1; +pub const LIQUID_CLAIM_TX_FEERATE_MSAT: f32 = 100.0; pub const DEFAULT_DATA_DIR: &str = ".data"; @@ -46,6 +48,7 @@ pub struct LiquidSdk { lwk_signer: SwSigner, active_address: Option, persister: Persister, + status_stream: BoltzStatusStream, data_dir_path: String, } @@ -82,6 +85,8 @@ impl LiquidSdk { let persister = Persister::new(&data_dir_path, network)?; persister.init()?; + let status_stream = BoltzStatusStream::new(); + let sdk = Arc::new(LiquidSdk { lwk_wollet, network, @@ -90,9 +95,10 @@ impl LiquidSdk { active_address: None, persister, data_dir_path, + status_stream, }); - LiquidSdk::track_pending_swaps(&sdk)?; + sdk.status_stream.track_pending_swaps(sdk.clone())?; Ok(sdk) } @@ -109,128 +115,239 @@ impl LiquidSdk { Ok(descriptor_str.parse()?) } - fn try_resolve_pending_swap( - sdk: &Arc, - client: &BoltzApiClient, - swap: &OngoingSwap, + fn get_submarine_keys(&self, derivation_index: i32) -> Result { + let mnemonic = self + .lwk_signer + .mnemonic() + .ok_or(PaymentError::SignerError { + err: "Could not claim: Mnemonic not found".to_string(), + })?; + let swap_key = SwapKey::from_submarine_account( + &mnemonic.to_string(), + "", + self.network.into(), + derivation_index as u64, + )?; + let lsk = LiquidSwapKey::try_from(swap_key)?; + Ok(lsk.keypair) + } + + pub(crate) fn try_handle_reverse_swap_status( + &self, + swap_state: RevSwapStates, + id: &str, ) -> Result<()> { - match swap { - OngoingSwap::Receive { - id, - preimage, - redeem_script, - blinding_key, - invoice, - .. - } => { - let status = client - .swap_status(SwapStatusRequest { id: id.clone() }) - .map_err(|e| anyhow!("Failed to fetch swap status for ID {id}: {e:?}"))? - .status; + let con = self.persister.get_connection()?; + let ongoing_swap_out = Persister::fetch_ongoing_swap_out(&con, id)? + .ok_or(anyhow!("No ongoing swap out found for ID {id}"))?; - let swap_state = status - .parse::() - .map_err(|_| anyhow!("Invalid swap state received for swap {id}: {status}",))?; - - match swap_state { - RevSwapStates::SwapExpired - | RevSwapStates::InvoiceExpired - | RevSwapStates::TransactionFailed - | RevSwapStates::TransactionRefunded => { - warn!("Cannot claim swap {id}, unrecoverable state: {status}"); - sdk.persister + match swap_state { + RevSwapStates::SwapExpired + | RevSwapStates::InvoiceExpired + | RevSwapStates::TransactionFailed + | RevSwapStates::TransactionRefunded => { + warn!("Cannot claim swap {id}, unrecoverable state: {swap_state:?}"); + self.persister + .resolve_ongoing_swap(id, None) + .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; + } + // We may be offline, or claiming failued due to other reasons until the swap reached these states + // If an ongoing reverse swap is in any of these states, we should be able to claim + RevSwapStates::TransactionMempool + | RevSwapStates::TransactionConfirmed + | RevSwapStates::InvoiceSettled => match self.try_claim_v2(&ongoing_swap_out) { + Ok(txid) => { + let payer_amount_sat = get_invoice_amount!(ongoing_swap_out.invoice); + self.persister + .resolve_ongoing_swap( + id, + Some(( + txid, + PaymentData { + payer_amount_sat, + receiver_amount_sat: ongoing_swap_out.receiver_amount_sat, + }, + )), + ) + .map_err(|e| anyhow!("Could not resolve swap {id}: {e}"))?; + } + Err(err) => { + if let PaymentError::AlreadyClaimed = err { + warn!("Funds already claimed"); + self.persister .resolve_ongoing_swap(id, None) .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; } - RevSwapStates::TransactionMempool | RevSwapStates::TransactionConfirmed => {} - _ => { - return Err(anyhow!("New swap state for swap {id}: {status}")); - } - } - - match sdk.try_claim(preimage, redeem_script, blinding_key) { - Ok(txid) => { - let payer_amount_sat = get_invoice_amount!(invoice); - sdk.persister - .resolve_ongoing_swap( - id, - Some((txid, PaymentData { payer_amount_sat })), - ) - .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; - } - Err(err) => { - if let PaymentError::AlreadyClaimed = err { - warn!("Funds already claimed"); - sdk.persister - .resolve_ongoing_swap(id, None) - .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; - } - warn!("Could not claim swap {id} yet. Err: {err}"); - } + warn!("Could not claim swap {id} yet. Err: {err}"); } + }, + RevSwapStates::Created | RevSwapStates::MinerFeePaid => { + // Too soon to try to claim } - OngoingSwap::Send { - id, invoice, txid, .. - } => { - let Some(txid) = txid.clone() else { - return Err(anyhow!("Transaction not broadcast yet for swap {id}")); - }; - - let status = client - .swap_status(SwapStatusRequest { id: id.clone() }) - .map_err(|e| anyhow!("Failed to fetch swap status for ID {id}: {e:?}"))? - .status; - - let state: SubSwapStates = status - .parse() - .map_err(|_| anyhow!("Invalid swap state received for swap {id}: {status}"))?; - - match state { - SubSwapStates::TransactionClaimed - | SubSwapStates::InvoiceFailedToPay - | SubSwapStates::SwapExpired => { - warn!("Cannot positively resolve swap {id}, unrecoverable state: {status}"); - - let payer_amount_sat = get_invoice_amount!(invoice); - sdk.persister - .resolve_ongoing_swap( - id, - Some((txid, PaymentData { payer_amount_sat })), - ) - .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; - } - _ => { - return Err(anyhow!("New swap state for swap {id}: {status}")); - } - } - } - }; + } Ok(()) } - fn track_pending_swaps(self: &Arc) -> Result<()> { - let cloned = self.clone(); - let client = self.boltz_client(); + pub(crate) fn try_handle_submarine_swap_status( + &self, + swap_state: SubSwapStates, + id: &str, + ) -> Result<()> { + let con = self.persister.get_connection()?; + let ongoing_swap_in = Persister::fetch_ongoing_swap_in(&con, id)? + .ok_or(anyhow!("No ongoing swap in found for ID {id}"))?; - thread::spawn(move || loop { - thread::sleep(Duration::from_secs(5)); - let Ok(ongoing_swaps) = cloned.persister.list_ongoing_swaps() else { - error!("Could not read ongoing swaps from database"); - continue; - }; + let Some(txid) = ongoing_swap_in.lockup_txid.clone() else { + return Err(anyhow!("Swap-in {id} is pending but no txid is present")); + }; + let receiver_amount_sat = get_invoice_amount!(ongoing_swap_in.invoice); + let keypair = self.get_submarine_keys(0)?; + let create_response: CreateSubmarineResponse = + serde_json::from_str(&ongoing_swap_in.create_response_json)?; - for swap in ongoing_swaps { - LiquidSdk::try_resolve_pending_swap(&cloned, &client, &swap).unwrap_or_else(|err| { - match swap { - OngoingSwap::Send { .. } => error!("[Ongoing Send] {err}"), - OngoingSwap::Receive { .. } => error!("[Ongoing Receive] {err}"), - } - }) + match swap_state { + SubSwapStates::TransactionClaimPending => { + let Ok(swap_script) = LBtcSwapScriptV2::submarine_from_swap_resp( + &create_response, + keypair.public_key().into(), + ) else { + self.persister + .resolve_ongoing_swap(id, None) + .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; + + return Err(anyhow!("Could not rebuild refund details for swap-in {id}")); + }; + + self.post_submarine_claim_details( + id, + &swap_script, + &ongoing_swap_in.invoice, + &keypair, + ) + .map_err(|e| anyhow!("Could not post claim details. Err: {e:?}"))?; + + self.persister + .resolve_ongoing_swap( + id, + Some(( + txid, + PaymentData { + payer_amount_sat: ongoing_swap_in.payer_amount_sat, + receiver_amount_sat, + }, + )), + ) + .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; + + Ok(()) } - }); + SubSwapStates::TransactionClaimed => { + warn!("Swap-in {id} has already been claimed. Resolving..."); - Ok(()) + self.persister + .resolve_ongoing_swap( + id, + Some(( + txid, + PaymentData { + payer_amount_sat: ongoing_swap_in.payer_amount_sat, + receiver_amount_sat, + }, + )), + ) + .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; + + warn!("Swap-in {id} resolved successfully"); + Ok(()) + } + SubSwapStates::TransactionLockupFailed + | SubSwapStates::InvoiceFailedToPay + | SubSwapStates::SwapExpired => { + warn!("Swap-in {id} is in an unrecoverable state: {swap_state:?}"); + + // If swap state is unrecoverable, try refunding + let Ok(swap_script) = LBtcSwapScriptV2::submarine_from_swap_resp( + &create_response, + keypair.public_key().into(), + ) else { + self.persister + .resolve_ongoing_swap(id, None) + .map_err(|_| anyhow!("Could not resolve swap {id} in database"))?; + + return Err(anyhow!("Could not rebuild refund details for swap-in {id}")); + }; + + let refund_txid = + self.try_refund(id, &swap_script, &keypair, receiver_amount_sat)?; + + warn!("Swap-in {id} refunded successfully. Txid: {refund_txid}"); + + self.persister + .resolve_ongoing_swap(id, None) + .map_err(|_| anyhow!("Could not resolve swap {id} in database")) + } + _ => Err(anyhow!("New state for submarine swap {id}: {swap_state:?}")), + } + } + + // TODO Not needed anymore with the event stream + // fn try_resolve_pending_swap(&self, swap: &OngoingSwap) -> Result<()> { + // let client = self.boltz_client(); + // let client_v2 = self.boltz_client_v2(); + // + // match swap { + // OngoingSwap::Receive(ongoing_swap_out) => { + // let swap_state = utils::get_rev_swap_status_v2(client_v2, &ongoing_swap_out.id)?; + // self.try_handle_reverse_swap_status(swap_state, &ongoing_swap_out.id)?; + // } + // OngoingSwap::Send(ongoing_swap_in) => { + // let id = &ongoing_swap_in.id; + // let status = client + // .swap_status(SwapStatusRequest { id: id.clone() }) + // .map_err(|e| anyhow!("Failed to fetch swap status for ID {id}: {e:?}"))? + // .status; + // + // let swap_state: SubSwapStates = status.parse().map_err(|_| { + // anyhow!("Invalid submarine swap state received for swap {id}: {status}") + // })?; + // + // self.try_handle_submarine_swap_status(swap_state, &ongoing_swap_in.id)?; + // } + // }; + // + // Ok(()) + // } + + // TODO Not needed anymore with the event stream + // fn track_pending_swaps(self: &Arc) -> Result<()> { + // let cloned = self.clone(); + // thread::spawn(move || loop { + // thread::sleep(Duration::from_secs(5)); + // match cloned.persister.list_ongoing_swaps() { + // Ok(ongoing_swaps) => { + // for swap in ongoing_swaps { + // match cloned.try_resolve_pending_swap(&swap) { + // Ok(_) => info!("Resolved pending swap {}", swap.id()), + // Err(err) => match swap { + // OngoingSwap::Send { .. } => error!("[Ongoing Send] {err}"), + // OngoingSwap::Receive { .. } => error!("[Ongoing Receive] {err}"), + // }, + // } + // } + // } + // Err(e) => { + // error!("Could not read ongoing swaps from database: {e}"); + // continue; + // } + // } + // }); + // + // Ok(()) + // } + + pub(crate) fn list_ongoing_swaps(&self) -> Result> { + self.persister.list_ongoing_swaps() } fn scan(&self) -> Result<(), lwk_wollet::Error> { @@ -261,25 +378,32 @@ impl LiquidSdk { }) } - fn get_signer(&self) -> SwSigner { - self.lwk_signer.clone() + // fn boltz_client(&self) -> BoltzApiClient { + // let base_url = match self.network { + // Network::LiquidTestnet => BOLTZ_TESTNET_URL, + // Network::Liquid => BOLTZ_MAINNET_URL, + // }; + // + // BoltzApiClient::new(base_url) + // } + + pub(crate) fn boltz_client_v2(&self) -> BoltzApiClientV2 { + BoltzApiClientV2::new(self.boltz_url_v2()) } - fn boltz_client(&self) -> BoltzApiClient { - let base_url = match self.network { - Network::LiquidTestnet => BOLTZ_TESTNET_URL, - Network::Liquid => BOLTZ_MAINNET_URL, - }; - - BoltzApiClient::new(base_url) + pub(crate) fn boltz_url_v2(&self) -> &str { + match self.network { + Network::LiquidTestnet => BOLTZ_TESTNET_URL_V2, + Network::Liquid => BOLTZ_MAINNET_URL_V2, + } } - fn get_network_config(&self) -> ElectrumConfig { + fn network_config(&self) -> ElectrumConfig { ElectrumConfig::new( self.network.into(), &self.electrum_url.to_string(), true, - false, + true, 100, ) } @@ -290,200 +414,509 @@ impl LiquidSdk { recipient_address: &str, amount_sat: u64, ) -> Result { + self.scan()?; let lwk_wollet = self.lwk_wollet.lock().unwrap(); let mut pset = lwk_wollet.send_lbtc(amount_sat, recipient_address, fee_rate)?; - let signer = AnySigner::Software(self.get_signer()); + let signer = AnySigner::Software(self.lwk_signer.clone()); signer.sign(&mut pset)?; Ok(lwk_wollet.finalize(&mut pset)?) } - pub fn prepare_send_payment( - &self, - req: PrepareSendRequest, - ) -> Result { - let client = self.boltz_client(); - let invoice = req - .invoice + fn validate_invoice(&self, invoice: &str) -> Result { + let invoice = invoice .trim() .parse::() .map_err(|_| PaymentError::InvalidInvoice)?; - // TODO Separate error type? Or make WalletError more generic? + match (invoice.network().to_string().as_str(), self.network) { + ("bitcoin", Network::Liquid) => {} + ("testnet", Network::LiquidTestnet) => {} + _ => return Err(PaymentError::InvalidInvoice), + } + + ensure_sdk!(!invoice.is_expired(), PaymentError::InvalidInvoice); + + Ok(invoice) + } + + fn validate_submarine_pairs( + client: &BoltzApiClientV2, + receiver_amount_sat: u64, + ) -> Result { let lbtc_pair = client - .get_pairs()? - .get_lbtc_pair() + .get_submarine_pairs()? + .get_lbtc_to_btc_pair() .ok_or(PaymentError::PairsNotFound)?; - let payer_amount_sat = invoice + lbtc_pair.limits.within(receiver_amount_sat)?; + + let fees_sat = lbtc_pair.fees.total(receiver_amount_sat); + + ensure_sdk!( + receiver_amount_sat > fees_sat, + PaymentError::AmountOutOfRange + ); + + Ok(lbtc_pair) + } + + fn get_broadcast_fee_estimation(&self, amount_sat: u64) -> Result { + Ok(self + .build_tx(None, &self.address()?.to_string(), amount_sat)? + .all_fees() + .values() + .sum()) + } + + pub fn prepare_send_payment( + &self, + req: &PrepareSendRequest, + ) -> Result { + let invoice = self.validate_invoice(&req.invoice)?; + let receiver_amount_sat = invoice .amount_milli_satoshis() .ok_or(PaymentError::AmountOutOfRange)? / 1000; - lbtc_pair - .limits - .within(payer_amount_sat) - .map_err(|_| PaymentError::AmountOutOfRange)?; + let client = self.boltz_client_v2(); + let lbtc_pair = Self::validate_submarine_pairs(&client, receiver_amount_sat)?; - let swap_response = client.create_swap(CreateSwapRequest::new_lbtc_submarine( - &lbtc_pair.hash, - &invoice.to_string(), - "", - ))?; - - let id = swap_response.get_id(); - let funding_address = swap_response.get_funding_address()?; - let receiver_amount_sat = swap_response.get_funding_amount()?; - let network_fees: u64 = self - .build_tx(None, &funding_address.to_string(), receiver_amount_sat)? - .all_fees() - .values() - .sum(); - - self.persister - .insert_or_update_ongoing_swap(&[OngoingSwap::Send { - id: id.clone(), - funding_address: funding_address.clone(), - invoice: invoice.to_string(), - receiver_amount_sat: receiver_amount_sat + network_fees, - txid: None, - }]) - .map_err(|_| PaymentError::PersistError)?; + let broadcast_fees_sat = self.get_broadcast_fee_estimation(receiver_amount_sat)?; Ok(PrepareSendResponse { - id, - funding_address, - invoice: invoice.to_string(), - payer_amount_sat, - receiver_amount_sat, - total_fees: receiver_amount_sat + network_fees - payer_amount_sat, + invoice: req.invoice.clone(), + fees_sat: lbtc_pair.fees.total(receiver_amount_sat) + broadcast_fees_sat, }) } + fn verify_payment_hash(preimage: &str, invoice: &str) -> Result<(), PaymentError> { + let preimage = Preimage::from_str(preimage)?; + let preimage_hash = preimage.sha256.to_string(); + let invoice = Bolt11Invoice::from_str(invoice).map_err(|_| PaymentError::InvalidInvoice)?; + let invoice_payment_hash = invoice.payment_hash(); + + (invoice_payment_hash.to_string() == preimage_hash) + .then_some(()) + .ok_or(PaymentError::InvalidPreimage) + } + + fn new_refund_tx(&self, swap_script: &LBtcSwapScriptV2) -> Result { + let wallet = self.lwk_wollet.lock().unwrap(); + let output_address = wallet.address(Some(0))?.address().to_string(); + let network_config = self.network_config(); + Ok(LBtcSwapTxV2::new_refund( + swap_script.clone(), + &output_address, + &network_config, + )?) + } + + fn try_refund( + &self, + swap_id: &str, + swap_script: &LBtcSwapScriptV2, + keypair: &Keypair, + amount_sat: u64, + ) -> Result { + let refund_tx = self.new_refund_tx(swap_script)?; + + let broadcast_fees_sat = Amount::from_sat(self.get_broadcast_fee_estimation(amount_sat)?); + let client = self.boltz_client_v2(); + let is_lowball = Some((&client, boltz_client::network::Chain::from(self.network))); + + match refund_tx.sign_refund( + keypair, + broadcast_fees_sat, + Some((&client, &swap_id.to_string())), + ) { + // Try with cooperative refund + Ok(tx) => { + let txid = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?; + debug!("Successfully broadcast cooperative refund for swap-in {swap_id}"); + Ok(txid) + } + // Try with non-cooperative refund + Err(e) => { + debug!("Cooperative refund failed: {:?}", e); + let tx = refund_tx.sign_refund(keypair, broadcast_fees_sat, None)?; + let txid = refund_tx.broadcast(&tx, &self.network_config(), is_lowball)?; + debug!("Successfully broadcast non-cooperative refund for swap-in {swap_id}"); + Ok(txid) + } + } + } + + fn post_submarine_claim_details( + &self, + swap_id: &str, + swap_script: &LBtcSwapScriptV2, + invoice: &str, + keypair: &Keypair, + ) -> Result<(), PaymentError> { + debug!("Claim is pending for swap-in {swap_id}. Initiating cooperative claim"); + let client = self.boltz_client_v2(); + let refund_tx = self.new_refund_tx(swap_script)?; + + let claim_tx_response = client.get_claim_tx_details(&swap_id.to_string())?; + + debug!("Received claim tx details: {:?}", &claim_tx_response); + + Self::verify_payment_hash(&claim_tx_response.preimage, invoice)?; + + let (partial_sig, pub_nonce) = + refund_tx.submarine_partial_sig(keypair, &claim_tx_response)?; + + client.post_claim_tx_details(&swap_id.to_string(), pub_nonce, partial_sig)?; + debug!("Successfully sent claim details for swap-in {swap_id}"); + Ok(()) + } + + fn lockup_funds( + &self, + swap_id: &str, + create_response: &CreateSubmarineResponse, + ) -> Result { + debug!( + "Initiated swap-in: send {} sats to liquid address {}", + create_response.expected_amount, create_response.address + ); + + let lockup_tx = self.build_tx( + None, + &create_response.address, + create_response.expected_amount, + )?; + + let electrum_client = ElectrumClient::new(&self.electrum_url)?; + let lockup_txid = electrum_client.broadcast(&lockup_tx)?.to_string(); + + debug!( + "Successfully broadcast lockup transaction for swap-in {swap_id}. Txid: {lockup_txid}" + ); + Ok(lockup_txid) + } + pub fn send_payment( &self, - res: &PrepareSendResponse, + req: &PrepareSendResponse, ) -> Result { - let tx = self.build_tx(None, &res.funding_address, res.receiver_amount_sat)?; + let invoice = self.validate_invoice(&req.invoice)?; + let receiver_amount_sat = invoice + .amount_milli_satoshis() + .ok_or(PaymentError::AmountOutOfRange)? + / 1000; - let electrum_client = ElectrumClient::new(&self.electrum_url)?; - let txid = electrum_client.broadcast(&tx)?.to_string(); + let client = self.boltz_client_v2(); + let lbtc_pair = Self::validate_submarine_pairs(&client, receiver_amount_sat)?; - self.persister - .insert_or_update_ongoing_swap(&[OngoingSwap::Send { - id: res.id.clone(), - funding_address: res.funding_address.clone(), - invoice: res.invoice.clone(), - receiver_amount_sat: res.receiver_amount_sat + res.total_fees, - txid: Some(txid.clone()), - }]) - .map_err(|_| PaymentError::PersistError)?; + let broadcast_fees_sat = self.get_broadcast_fee_estimation(receiver_amount_sat)?; - Ok(SendPaymentResponse { txid }) - } + ensure_sdk!( + req.fees_sat == lbtc_pair.fees.total(receiver_amount_sat) + broadcast_fees_sat, + PaymentError::InvalidOrExpiredFees + ); - fn try_claim( - &self, - preimage: &str, - redeem_script: &str, - blinding_key: &str, - ) -> Result { - let network_config = &self.get_network_config(); - let rev_swap_tx = LBtcSwapTx::new_claim( - LBtcSwapScript::reverse_from_str(redeem_script, blinding_key)?, - self.address()?.to_string(), - network_config, + let keypair = self.get_submarine_keys(0)?; + let refund_public_key = boltz_client::PublicKey { + compressed: true, + inner: keypair.public_key(), + }; + + let create_response = client.post_swap_req(&CreateSubmarineRequest { + from: "L-BTC".to_string(), + to: "BTC".to_string(), + invoice: req.invoice.to_string(), + refund_public_key, + pair_hash: Some(lbtc_pair.hash), + // TODO: Add referral id + referral_id: None, + })?; + let create_response_json = + serde_json::to_string(&create_response).map_err(|_| PaymentError::Generic { + err: "Could not store swap response locally".to_string(), + })?; + + let swap_id = &create_response.id; + let swap_script = LBtcSwapScriptV2::submarine_from_swap_resp( + &create_response, + keypair.public_key().into(), )?; - let mnemonic = self - .lwk_signer - .mnemonic() - .ok_or(PaymentError::SignerError { - err: "Could not claim: Mnemonic not found".to_string(), + debug!("Opening WS connection for swap {}", &swap_id); + + let mut socket = client.connect_ws()?; + let subscription = Subscription::new(swap_id); + let subscribe_json = serde_json::to_string(&subscription) + .map_err(|e| anyhow!("Failed to serialize subscription msg: {e:?}"))?; + socket + .send(tungstenite::Message::Text(subscribe_json)) + .map_err(|e| anyhow!("Failed to subscribe to websocket updates: {e:?}"))?; + + // We insert the pending send to avoid it being handled by the status stream + self.status_stream + .insert_tracked_swap(swap_id, SwapType::Submarine); + + self.persister + .insert_or_update_ongoing_swap_in(OngoingSwapIn { + id: swap_id.clone(), + invoice: req.invoice.clone(), + payer_amount_sat: req.fees_sat + receiver_amount_sat, + create_response_json: create_response_json.clone(), + lockup_txid: None, })?; - let swap_key = - SwapKey::from_reverse_account(&mnemonic.to_string(), "", self.network.into(), 0)?; - let lsk = LiquidSwapKey::try_from(swap_key)?; - let preimage = Preimage::from_str(preimage)?; + let result; + let mut lockup_txid = String::new(); + loop { + let data = match utils::get_swap_status_v2(&mut socket, swap_id) { + Ok(data) => data, + Err(_) => { + // TODO: close socket if dead, skip EOF errors + continue; + } + }; - // Create a mock tx to calculate the size, then multiply by fee rate - let absolute_fees = (rev_swap_tx - .sign_claim(&lsk.keypair, &preimage, 100)? - .vsize() as f32 - * LIQUID_CLAIM_TX_FEERATE) - .ceil() as u64; + let state = data + .parse::() + .map_err(|_| PaymentError::Generic { + err: "Invalid state received from swapper".to_string(), + })?; - let signed_tx = rev_swap_tx.sign_claim(&lsk.keypair, &preimage, absolute_fees)?; - let txid = rev_swap_tx.broadcast(signed_tx, network_config)?; + // See https://docs.boltz.exchange/v/api/lifecycle#normal-submarine-swaps + match state { + // Boltz has locked the HTLC, we proceed with locking up the funds + SubSwapStates::InvoiceSet => { + // Check that we have not persisted the swap already + let con = self.persister.get_connection()?; - Ok(txid) + if let Some(ongoing_swap) = Persister::fetch_ongoing_swap_in(&con, swap_id) + .map_err(|_| PaymentError::PersistError)? + { + if ongoing_swap.lockup_txid.is_some() { + continue; + } + }; + + lockup_txid = self.lockup_funds(swap_id, &create_response)?; + self.persister + .insert_or_update_ongoing_swap_in(OngoingSwapIn { + id: swap_id.clone(), + invoice: req.invoice.clone(), + payer_amount_sat: req.fees_sat + receiver_amount_sat, + create_response_json: create_response_json.clone(), + lockup_txid: Some(lockup_txid.clone()), + })?; + } + + // Boltz has detected the lockup in the mempool, we can speed up + // the claim by doing so cooperatively + SubSwapStates::TransactionClaimPending => { + self.post_submarine_claim_details( + swap_id, + &swap_script, + &req.invoice, + &keypair, + )?; + + debug!("Boltz successfully claimed the funds. Resolving swap-in {swap_id}"); + self.persister.resolve_ongoing_swap( + swap_id, + Some(( + lockup_txid.clone(), + PaymentData { + payer_amount_sat: receiver_amount_sat + req.fees_sat, + receiver_amount_sat, + }, + )), + )?; + + self.status_stream + .resolve_tracked_swap(swap_id, SwapType::ReverseSubmarine); + + // TODO: Change lockup txid to claim txid + result = Ok(SendPaymentResponse { txid: lockup_txid }); + + debug!("Successfully resolved swap-in {swap_id}"); + break; + } + + // Either: + // 1. Boltz failed to pay + // 2. The swap has expired (>24h) + // 3. Lockup failed (we sent too little funds) + // We initiate a cooperative refund, and then fallback to a regular one + SubSwapStates::InvoiceFailedToPay + | SubSwapStates::SwapExpired + | SubSwapStates::TransactionLockupFailed => { + let refund_txid = + self.try_refund(swap_id, &swap_script, &keypair, receiver_amount_sat)?; + + result = Err(PaymentError::Refunded { + err: format!( + "Unrecoverable state for swap-in {swap_id}: {}", + state.to_string() + ), + txid: refund_txid.clone(), + }); + break; + } + _ => {} + }; + + sleep(Duration::from_millis(500)); + } + + socket.close(None).unwrap(); + result + } + + fn try_claim_v2(&self, ongoing_swap_out: &OngoingSwapOut) -> Result { + debug!("Trying to claim reverse swap {}", &ongoing_swap_out.id); + + let lsk = self.get_liquid_swap_key()?; + let our_keys = lsk.keypair; + + let create_response: CreateReverseResponse = + serde_json::from_str(&ongoing_swap_out.redeem_script).unwrap(); + let swap_script = LBtcSwapScriptV2::reverse_from_swap_resp( + &create_response, + our_keys.public_key().into(), + )?; + + let claim_address = self.address()?.to_string(); + let claim_tx = LBtcSwapTxV2::new_claim( + swap_script, + claim_address, + &self.network_config(), + self.boltz_url_v2().into(), + ongoing_swap_out.id.clone(), + )?; + + let tx = claim_tx.sign_claim( + &our_keys, + &Preimage::from_str(&ongoing_swap_out.preimage)?, + Amount::from_sat(ongoing_swap_out.claim_fees_sat), + // Enable cooperative claim (Some) or not (None) + Some((&self.boltz_client_v2(), ongoing_swap_out.id.clone())), + // None + )?; + + // Electrum only broadcasts txs with lowball fees on testnet + // For mainnet, we use Boltz to broadcast + match self.network { + Network::Liquid => { + let tx_hex = elements::encode::serialize(&tx).to_lower_hex_string(); + let response = self + .boltz_client_v2() + .broadcast_tx(self.network.into(), &tx_hex)?; + info!("Claim broadcast response: {response:?}"); + } + Network::LiquidTestnet => { + let electrum_client = ElectrumClient::new(&self.electrum_url)?; + electrum_client.broadcast(&tx)?; + } + }; + + info!("Succesfully broadcasted claim tx {}", tx.txid()); + debug!("Claim Tx {:?}", tx); + + Ok(tx.txid().to_string()) + } + + #[allow(dead_code)] + fn validate_reverse_pairs( + client: &BoltzApiClientV2, + payer_amount_sat: u64, + ) -> Result { + let lbtc_pair = client + .get_reverse_pairs()? + .get_btc_to_lbtc_pair() + .ok_or(PaymentError::PairsNotFound)?; + + lbtc_pair.limits.within(payer_amount_sat)?; + + let fees_sat = lbtc_pair.fees.total(payer_amount_sat); + + ensure_sdk!(payer_amount_sat > fees_sat, PaymentError::AmountOutOfRange); + + Ok(lbtc_pair) } pub fn prepare_receive_payment( &self, req: &PrepareReceiveRequest, ) -> Result { - let client = self.boltz_client(); - let lbtc_pair = client - .get_pairs()? - .get_lbtc_pair() + let reverse_pair = self + .boltz_client_v2() + .get_reverse_pairs()? + .get_btc_to_lbtc_pair() .ok_or(PaymentError::PairsNotFound)?; let payer_amount_sat = req.payer_amount_sat; - let fees_boltz = lbtc_pair.fees.reverse_boltz(payer_amount_sat); - let fees_lockup = lbtc_pair.fees.reverse_lockup(); - let fees_claim = lbtc_pair.fees.reverse_claim_estimate(); - let fees_total = fees_boltz + fees_lockup + fees_claim; + let fees_sat = reverse_pair.fees.total(req.payer_amount_sat); - ensure_sdk!( - payer_amount_sat > fees_total, - PaymentError::AmountOutOfRange - ); + ensure_sdk!(payer_amount_sat > fees_sat, PaymentError::AmountOutOfRange); - lbtc_pair + reverse_pair .limits .within(payer_amount_sat) .map_err(|_| PaymentError::AmountOutOfRange)?; - debug!("Creating reverse swap with: payer_amount_sat {payer_amount_sat} sat, fees_total {fees_total} sat"); + debug!("Preparing reverse swap with: payer_amount_sat {payer_amount_sat} sat, fees_sat {fees_sat} sat"); Ok(PrepareReceiveResponse { - pair_hash: lbtc_pair.hash, payer_amount_sat, - fees_sat: fees_total, + fees_sat, }) } pub fn receive_payment( &self, - res: &PrepareReceiveResponse, + req: &PrepareReceiveResponse, ) -> Result { - let client = self.boltz_client(); - let mnemonic = self - .lwk_signer - .mnemonic() - .ok_or(PaymentError::SignerError { - err: "Could not claim: Mnemonic not found".to_string(), - })?; - let swap_key = - SwapKey::from_reverse_account(&mnemonic.to_string(), "", self.network.into(), 0)?; - let lsk = LiquidSwapKey::try_from(swap_key)?; + let payer_amount_sat = req.payer_amount_sat; + let fees_sat = req.fees_sat; + + let reverse_pair = self + .boltz_client_v2() + .get_reverse_pairs()? + .get_btc_to_lbtc_pair() + .ok_or(PaymentError::PairsNotFound)?; + let new_fees_sat = reverse_pair.fees.total(req.payer_amount_sat); + ensure_sdk!(fees_sat == new_fees_sat, PaymentError::InvalidOrExpiredFees); + + debug!("Creating reverse swap with: payer_amount_sat {payer_amount_sat} sat, fees_sat {fees_sat} sat"); + + let lsk = self.get_liquid_swap_key()?; let preimage = Preimage::new(); let preimage_str = preimage.to_string().ok_or(PaymentError::InvalidPreimage)?; let preimage_hash = preimage.sha256.to_string(); - let swap_response = client.create_swap(CreateSwapRequest::new_lbtc_reverse_invoice_amt( - res.pair_hash.clone(), - preimage_hash.clone(), - lsk.keypair.public_key().to_string(), - res.payer_amount_sat, - ))?; + let v2_req = CreateReverseRequest { + invoice_amount: req.payer_amount_sat as u32, // TODO update our model + from: "BTC".to_string(), + to: "L-BTC".to_string(), + preimage_hash: preimage.sha256, + claim_public_key: lsk.keypair.public_key().into(), + address: None, + address_signature: None, + referral_id: None, + }; + let create_response = self.boltz_client_v2().post_reverse_req(v2_req)?; - let swap_id = swap_response.get_id(); - let invoice = swap_response.get_invoice()?; - let blinding_str = swap_response.get_blinding_key()?; - let redeem_script = swap_response.get_redeem_script()?; + // TODO Persisting this in the DB (reusing "redeem_script" field), as we need it later when claiming + let redeem_script = serde_json::to_string(&create_response).unwrap(); + + let swap_id = create_response.id; + let invoice = Bolt11Invoice::from_str(&create_response.invoice) + .map_err(|_| PaymentError::InvalidInvoice)?; + let blinding_str = + create_response + .blinding_key + .ok_or(boltz_client::error::Error::Protocol( + "Boltz response does not contain a blinding key.".to_string(), + ))?; let payer_amount_sat = invoice .amount_milli_satoshis() .ok_or(PaymentError::InvalidInvoice)? @@ -496,14 +929,15 @@ impl LiquidSdk { }; self.persister - .insert_or_update_ongoing_swap(dbg!(&[OngoingSwap::Receive { + .insert_or_update_ongoing_swap_out(OngoingSwapOut { id: swap_id.clone(), preimage: preimage_str, blinding_key: blinding_str, redeem_script, invoice: invoice.to_string(), - receiver_amount_sat: payer_amount_sat - res.fees_sat, - }])) + receiver_amount_sat: payer_amount_sat - req.fees_sat, + claim_fees_sat: reverse_pair.fees.claim_estimate(), + }) .map_err(|_| PaymentError::PersistError)?; Ok(ReceivePaymentResponse { @@ -526,6 +960,7 @@ impl LiquidSdk { let id = tx.txid.to_string(); let data = payment_data.get(&id); let amount_sat = tx.balance.values().sum::(); + let fees_sat = data.map(|d| d.payer_amount_sat - d.receiver_amount_sat); Payment { id: Some(id.clone()), @@ -536,8 +971,7 @@ impl LiquidSdk { false => PaymentType::Sent, }, invoice: None, - fees_sat: data - .map(|d| (amount_sat.abs() - d.payer_amount_sat as i64).unsigned_abs()), + fees_sat, } }) .collect(); @@ -551,25 +985,6 @@ impl LiquidSdk { Ok(payments) } - pub fn recover_funds(&self, recovery: &LBtcReverseRecovery) -> Result { - let script: LBtcSwapScript = recovery.try_into().unwrap(); - let network_config = self.get_network_config(); - debug!("{:?}", script.fetch_utxo(&network_config)); - - let tx = - LBtcSwapTx::new_claim(script.clone(), self.address()?.to_string(), &network_config) - .expect("Expecting valid tx"); - let keypair: Keypair = recovery.try_into().unwrap(); - let preimage: Preimage = recovery.try_into().unwrap(); - - let signed_tx = tx.sign_claim(&keypair, &preimage, 1_000).unwrap(); - let txid = tx.broadcast(signed_tx, &network_config).unwrap(); - - debug!("Funds recovered successfully! Txid: {txid}"); - - Ok(txid) - } - /// Empties all Liquid Wallet caches for this network type. pub fn empty_wallet_cache(&self) -> Result<()> { let mut path = PathBuf::from(self.data_dir_path.clone()); @@ -593,6 +1008,20 @@ impl LiquidSdk { pub fn backup(&self) -> Result<()> { self.persister.backup() } + + fn get_liquid_swap_key(&self) -> Result { + let mnemonic = self + .lwk_signer + .mnemonic() + .ok_or(PaymentError::SignerError { + err: "Mnemonic not found".to_string(), + })?; + let swap_key = + SwapKey::from_reverse_account(&mnemonic.to_string(), "", self.network.into(), 0)?; + LiquidSwapKey::try_from(swap_key).map_err(|e| PaymentError::SignerError { + err: format!("Could not create LiquidSwapKey: {e:?}"), + }) + } } #[cfg(test)] @@ -638,7 +1067,7 @@ mod tests { })?; let invoice = "lntb10u1pnqwkjrpp5j8ucv9mgww0ajk95yfpvuq0gg5825s207clrzl5thvtuzfn68h0sdqqcqzzsxqr23srzjqv8clnrfs9keq3zlg589jvzpw87cqh6rjks0f9g2t9tvuvcqgcl45f6pqqqqqfcqqyqqqqlgqqqqqqgq2qsp5jnuprlxrargr6hgnnahl28nvutj3gkmxmmssu8ztfhmmey3gq2ss9qyyssq9ejvcp6frwklf73xvskzdcuhnnw8dmxag6v44pffwqrxznsly4nqedem3p3zhn6u4ln7k79vk6zv55jjljhnac4gnvr677fyhfgn07qp4x6wrq".to_string(); - sdk.prepare_send_payment(PrepareSendRequest { invoice })?; + sdk.prepare_send_payment(&PrepareSendRequest { invoice })?; assert!(!list_pending(&sdk)?.is_empty()); Ok(()) diff --git a/lib/core/src/utils.rs b/lib/core/src/utils.rs new file mode 100644 index 0000000..3536879 --- /dev/null +++ b/lib/core/src/utils.rs @@ -0,0 +1,142 @@ +use std::net::TcpStream; + +use anyhow::{anyhow, ensure, Result}; +use boltz_client::swaps::boltzv2::SwapUpdate; +use log::{error, info}; +use tungstenite::{stream::MaybeTlsStream, WebSocket}; + +/// Fetch the swap status using the websocket endpoint +pub(crate) fn get_swap_status_v2( + socket: &mut WebSocket>, + swap_id: &str, +) -> Result { + loop { + let response: SwapUpdate = serde_json::from_str(&socket.read()?.to_string()) + .map_err(|e| anyhow!("WS response is invalid SwapUpdate: {e:?}"))?; + + match response { + SwapUpdate::Subscription { + event, + channel, + args, + } => { + ensure!(event == "subscribe", "Wrong WS reply event {event}"); + ensure!(channel == "swap.update", "Wrong WS reply channel {channel}"); + + let first_arg = args.first(); + let is_ok = matches!(first_arg.as_ref(), Some(&x) if x == swap_id); + ensure!(is_ok, "Wrong WS reply subscription ID {first_arg:?}"); + + info!("Subscription successful for swap : {swap_id}"); + } + + SwapUpdate::Update { + event, + channel, + args, + } => { + ensure!(event == "update", "Wrong WS reply event {event}"); + ensure!(channel == "swap.update", "Wrong WS reply channel {channel}"); + + return match args.first() { + Some(update) if update.id == swap_id => { + info!("Got new swap status: {}", update.status); + + Ok(update.status.clone()) + } + Some(update) => Err(anyhow!("WS reply has wrong swap ID {update:?}")), + None => Err(anyhow!("WS reply contains no update")), + }; + } + + SwapUpdate::Error { + event, + channel, + args, + } => { + ensure!(event == "update", "Wrong WS reply event {event}"); + ensure!(channel == "swap.update", "Wrong WS reply channel {channel}"); + + for e in &args { + error!("Got error: {} for swap: {}", e.error, e.id); + } + return Err(anyhow!("Got SwapUpdate errors: {args:?}")); + } + } + } +} + +// Fetch the reverse swap status using the websocket endpoint +// pub(crate) fn get_rev_swap_status_v2( +// client_v2: BoltzApiClientV2, +// swap_id: &str, +// ) -> Result { +// let mut socket = client_v2 +// .connect_ws() +// .map_err(|e| anyhow!("Failed to connect to websocket: {e:?}"))?; +// +// let sub_id = swap_id.to_string(); +// let subscription = Subscription::new(&sub_id); +// let subscribe_json = serde_json::to_string(&subscription) +// .map_err(|e| anyhow!("Failed to serialize subscription msg: {e:?}"))?; +// socket +// .send(tungstenite::Message::Text(subscribe_json)) +// .map_err(|e| anyhow!("Failed to subscribe to websocket updates: {e:?}"))?; +// +// loop { +// let response: SwapUpdate = serde_json::from_str(&socket.read()?.to_string()) +// .map_err(|e| anyhow!("WS response is invalid SwapUpdate: {e:?}"))?; +// +// match response { +// SwapUpdate::Subscription { +// event, +// channel, +// args, +// } => { +// ensure!(event == "subscribe", "Wrong WS reply event {event}"); +// ensure!(channel == "swap.update", "Wrong WS reply channel {channel}"); +// +// let first_arg = args.first(); +// let is_ok = matches!(first_arg.as_ref(), Some(&x) if x == &sub_id); +// ensure!(is_ok, "Wrong WS reply subscription ID {first_arg:?}"); +// +// info!("Subscription successful for swap : {sub_id}"); +// } +// +// SwapUpdate::Update { +// event, +// channel, +// args, +// } => { +// ensure!(event == "update", "Wrong WS reply event {event}"); +// ensure!(channel == "swap.update", "Wrong WS reply channel {channel}"); +// +// return match args.first() { +// Some(update) if update.id == sub_id => { +// info!("Got new reverse swap status: {}", update.status); +// +// RevSwapStates::from_str(&update.status).map_err(|_| { +// anyhow!("Invalid state for rev swap {swap_id}: {}", update.status) +// }) +// } +// Some(update) => Err(anyhow!("WS reply has wrong swap ID {update:?}")), +// None => Err(anyhow!("WS reply contains no update")), +// }; +// } +// +// SwapUpdate::Error { +// event, +// channel, +// args, +// } => { +// ensure!(event == "update", "Wrong WS reply event {event}"); +// ensure!(channel == "swap.update", "Wrong WS reply channel {channel}"); +// +// for e in &args { +// error!("Got error: {} for swap: {}", e.error, e.id); +// } +// return Err(anyhow!("Got SwapUpdate errors: {args:?}")); +// } +// } +// } +// } diff --git a/packages/dart/lib/src/bindings.dart b/packages/dart/lib/src/bindings.dart index 1d9e33b..0648bb1 100644 --- a/packages/dart/lib/src/bindings.dart +++ b/packages/dart/lib/src/bindings.dart @@ -29,28 +29,9 @@ Future receivePayment({required PrepareReceiveResponse r Future> listPayments({required bool withScan, required bool includePending, dynamic hint}) => RustLib.instance.api.listPayments(withScan: withScan, includePending: includePending, hint: hint); -Future recoverFunds({required LBtcReverseRecovery recovery, dynamic hint}) => - RustLib.instance.api.recoverFunds(recovery: recovery, hint: hint); - Future emptyWalletCache({dynamic hint}) => RustLib.instance.api.emptyWalletCache(hint: hint); Future backup({dynamic hint}) => RustLib.instance.api.backup(hint: hint); Future restore({required RestoreRequest req, dynamic hint}) => RustLib.instance.api.restore(req: req, hint: hint); - -// Rust type: RustOpaqueNom> -@sealed -class LBtcReverseRecovery extends RustOpaque { - LBtcReverseRecovery.dcoDecode(List wire) : super.dcoDecode(wire, _kStaticData); - - LBtcReverseRecovery.sseDecode(int ptr, int externalSizeOnNative) - : super.sseDecode(ptr, externalSizeOnNative, _kStaticData); - - static final _kStaticData = RustArcStaticData( - rustArcIncrementStrongCount: RustLib.instance.api.rust_arc_increment_strong_count_LBtcReverseRecovery, - rustArcDecrementStrongCount: RustLib.instance.api.rust_arc_decrement_strong_count_LBtcReverseRecovery, - rustArcDecrementStrongCountPtr: - RustLib.instance.api.rust_arc_decrement_strong_count_LBtcReverseRecoveryPtr, - ); -} diff --git a/packages/dart/lib/src/error.dart b/packages/dart/lib/src/error.dart index 4db5363..7649c98 100644 --- a/packages/dart/lib/src/error.dart +++ b/packages/dart/lib/src/error.dart @@ -12,11 +12,13 @@ part 'error.freezed.dart'; sealed class PaymentError with _$PaymentError implements FrbException { const PaymentError._(); - const factory PaymentError.amountOutOfRange() = PaymentError_AmountOutOfRange; const factory PaymentError.alreadyClaimed() = PaymentError_AlreadyClaimed; + const factory PaymentError.amountOutOfRange() = PaymentError_AmountOutOfRange; const factory PaymentError.generic({ required String err, }) = PaymentError_Generic; + const factory PaymentError.invalidOrExpiredFees() = PaymentError_InvalidOrExpiredFees; + const factory PaymentError.insufficientFunds() = PaymentError_InsufficientFunds; const factory PaymentError.invalidInvoice() = PaymentError_InvalidInvoice; const factory PaymentError.invalidPreimage() = PaymentError_InvalidPreimage; const factory PaymentError.lwkError({ @@ -24,6 +26,10 @@ sealed class PaymentError with _$PaymentError implements FrbException { }) = PaymentError_LwkError; const factory PaymentError.pairsNotFound() = PaymentError_PairsNotFound; const factory PaymentError.persistError() = PaymentError_PersistError; + const factory PaymentError.refunded({ + required String err, + required String txid, + }) = PaymentError_Refunded; const factory PaymentError.sendError({ required String err, }) = PaymentError_SendError; diff --git a/packages/dart/lib/src/error.freezed.dart b/packages/dart/lib/src/error.freezed.dart index d7a1000..95f81a5 100644 --- a/packages/dart/lib/src/error.freezed.dart +++ b/packages/dart/lib/src/error.freezed.dart @@ -33,47 +33,6 @@ class _$PaymentErrorCopyWithImpl<$Res, $Val extends PaymentError> implements $Pa final $Res Function($Val) _then; } -/// @nodoc -abstract class _$$PaymentError_AmountOutOfRangeImplCopyWith<$Res> { - factory _$$PaymentError_AmountOutOfRangeImplCopyWith(_$PaymentError_AmountOutOfRangeImpl value, - $Res Function(_$PaymentError_AmountOutOfRangeImpl) then) = - __$$PaymentError_AmountOutOfRangeImplCopyWithImpl<$Res>; -} - -/// @nodoc -class __$$PaymentError_AmountOutOfRangeImplCopyWithImpl<$Res> - extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_AmountOutOfRangeImpl> - implements _$$PaymentError_AmountOutOfRangeImplCopyWith<$Res> { - __$$PaymentError_AmountOutOfRangeImplCopyWithImpl( - _$PaymentError_AmountOutOfRangeImpl _value, $Res Function(_$PaymentError_AmountOutOfRangeImpl) _then) - : super(_value, _then); -} - -/// @nodoc - -class _$PaymentError_AmountOutOfRangeImpl extends PaymentError_AmountOutOfRange { - const _$PaymentError_AmountOutOfRangeImpl() : super._(); - - @override - String toString() { - return 'PaymentError.amountOutOfRange()'; - } - - @override - bool operator ==(Object other) { - return identical(this, other) || - (other.runtimeType == runtimeType && other is _$PaymentError_AmountOutOfRangeImpl); - } - - @override - int get hashCode => runtimeType.hashCode; -} - -abstract class PaymentError_AmountOutOfRange extends PaymentError { - const factory PaymentError_AmountOutOfRange() = _$PaymentError_AmountOutOfRangeImpl; - const PaymentError_AmountOutOfRange._() : super._(); -} - /// @nodoc abstract class _$$PaymentError_AlreadyClaimedImplCopyWith<$Res> { factory _$$PaymentError_AlreadyClaimedImplCopyWith( @@ -115,6 +74,47 @@ abstract class PaymentError_AlreadyClaimed extends PaymentError { const PaymentError_AlreadyClaimed._() : super._(); } +/// @nodoc +abstract class _$$PaymentError_AmountOutOfRangeImplCopyWith<$Res> { + factory _$$PaymentError_AmountOutOfRangeImplCopyWith(_$PaymentError_AmountOutOfRangeImpl value, + $Res Function(_$PaymentError_AmountOutOfRangeImpl) then) = + __$$PaymentError_AmountOutOfRangeImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$PaymentError_AmountOutOfRangeImplCopyWithImpl<$Res> + extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_AmountOutOfRangeImpl> + implements _$$PaymentError_AmountOutOfRangeImplCopyWith<$Res> { + __$$PaymentError_AmountOutOfRangeImplCopyWithImpl( + _$PaymentError_AmountOutOfRangeImpl _value, $Res Function(_$PaymentError_AmountOutOfRangeImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$PaymentError_AmountOutOfRangeImpl extends PaymentError_AmountOutOfRange { + const _$PaymentError_AmountOutOfRangeImpl() : super._(); + + @override + String toString() { + return 'PaymentError.amountOutOfRange()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$PaymentError_AmountOutOfRangeImpl); + } + + @override + int get hashCode => runtimeType.hashCode; +} + +abstract class PaymentError_AmountOutOfRange extends PaymentError { + const factory PaymentError_AmountOutOfRange() = _$PaymentError_AmountOutOfRangeImpl; + const PaymentError_AmountOutOfRange._() : super._(); +} + /// @nodoc abstract class _$$PaymentError_GenericImplCopyWith<$Res> { factory _$$PaymentError_GenericImplCopyWith( @@ -187,6 +187,88 @@ abstract class PaymentError_Generic extends PaymentError { throw _privateConstructorUsedError; } +/// @nodoc +abstract class _$$PaymentError_InvalidOrExpiredFeesImplCopyWith<$Res> { + factory _$$PaymentError_InvalidOrExpiredFeesImplCopyWith(_$PaymentError_InvalidOrExpiredFeesImpl value, + $Res Function(_$PaymentError_InvalidOrExpiredFeesImpl) then) = + __$$PaymentError_InvalidOrExpiredFeesImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$PaymentError_InvalidOrExpiredFeesImplCopyWithImpl<$Res> + extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_InvalidOrExpiredFeesImpl> + implements _$$PaymentError_InvalidOrExpiredFeesImplCopyWith<$Res> { + __$$PaymentError_InvalidOrExpiredFeesImplCopyWithImpl(_$PaymentError_InvalidOrExpiredFeesImpl _value, + $Res Function(_$PaymentError_InvalidOrExpiredFeesImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$PaymentError_InvalidOrExpiredFeesImpl extends PaymentError_InvalidOrExpiredFees { + const _$PaymentError_InvalidOrExpiredFeesImpl() : super._(); + + @override + String toString() { + return 'PaymentError.invalidOrExpiredFees()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$PaymentError_InvalidOrExpiredFeesImpl); + } + + @override + int get hashCode => runtimeType.hashCode; +} + +abstract class PaymentError_InvalidOrExpiredFees extends PaymentError { + const factory PaymentError_InvalidOrExpiredFees() = _$PaymentError_InvalidOrExpiredFeesImpl; + const PaymentError_InvalidOrExpiredFees._() : super._(); +} + +/// @nodoc +abstract class _$$PaymentError_InsufficientFundsImplCopyWith<$Res> { + factory _$$PaymentError_InsufficientFundsImplCopyWith(_$PaymentError_InsufficientFundsImpl value, + $Res Function(_$PaymentError_InsufficientFundsImpl) then) = + __$$PaymentError_InsufficientFundsImplCopyWithImpl<$Res>; +} + +/// @nodoc +class __$$PaymentError_InsufficientFundsImplCopyWithImpl<$Res> + extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_InsufficientFundsImpl> + implements _$$PaymentError_InsufficientFundsImplCopyWith<$Res> { + __$$PaymentError_InsufficientFundsImplCopyWithImpl( + _$PaymentError_InsufficientFundsImpl _value, $Res Function(_$PaymentError_InsufficientFundsImpl) _then) + : super(_value, _then); +} + +/// @nodoc + +class _$PaymentError_InsufficientFundsImpl extends PaymentError_InsufficientFunds { + const _$PaymentError_InsufficientFundsImpl() : super._(); + + @override + String toString() { + return 'PaymentError.insufficientFunds()'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && other is _$PaymentError_InsufficientFundsImpl); + } + + @override + int get hashCode => runtimeType.hashCode; +} + +abstract class PaymentError_InsufficientFunds extends PaymentError { + const factory PaymentError_InsufficientFunds() = _$PaymentError_InsufficientFundsImpl; + const PaymentError_InsufficientFunds._() : super._(); +} + /// @nodoc abstract class _$$PaymentError_InvalidInvoiceImplCopyWith<$Res> { factory _$$PaymentError_InvalidInvoiceImplCopyWith( @@ -423,6 +505,88 @@ abstract class PaymentError_PersistError extends PaymentError { const PaymentError_PersistError._() : super._(); } +/// @nodoc +abstract class _$$PaymentError_RefundedImplCopyWith<$Res> { + factory _$$PaymentError_RefundedImplCopyWith( + _$PaymentError_RefundedImpl value, $Res Function(_$PaymentError_RefundedImpl) then) = + __$$PaymentError_RefundedImplCopyWithImpl<$Res>; + @useResult + $Res call({String err, String txid}); +} + +/// @nodoc +class __$$PaymentError_RefundedImplCopyWithImpl<$Res> + extends _$PaymentErrorCopyWithImpl<$Res, _$PaymentError_RefundedImpl> + implements _$$PaymentError_RefundedImplCopyWith<$Res> { + __$$PaymentError_RefundedImplCopyWithImpl( + _$PaymentError_RefundedImpl _value, $Res Function(_$PaymentError_RefundedImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? err = null, + Object? txid = null, + }) { + return _then(_$PaymentError_RefundedImpl( + err: null == err + ? _value.err + : err // ignore: cast_nullable_to_non_nullable + as String, + txid: null == txid + ? _value.txid + : txid // ignore: cast_nullable_to_non_nullable + as String, + )); + } +} + +/// @nodoc + +class _$PaymentError_RefundedImpl extends PaymentError_Refunded { + const _$PaymentError_RefundedImpl({required this.err, required this.txid}) : super._(); + + @override + final String err; + @override + final String txid; + + @override + String toString() { + return 'PaymentError.refunded(err: $err, txid: $txid)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$PaymentError_RefundedImpl && + (identical(other.err, err) || other.err == err) && + (identical(other.txid, txid) || other.txid == txid)); + } + + @override + int get hashCode => Object.hash(runtimeType, err, txid); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$PaymentError_RefundedImplCopyWith<_$PaymentError_RefundedImpl> get copyWith => + __$$PaymentError_RefundedImplCopyWithImpl<_$PaymentError_RefundedImpl>(this, _$identity); +} + +abstract class PaymentError_Refunded extends PaymentError { + const factory PaymentError_Refunded({required final String err, required final String txid}) = + _$PaymentError_RefundedImpl; + const PaymentError_Refunded._() : super._(); + + String get err; + String get txid; + @JsonKey(ignore: true) + _$$PaymentError_RefundedImplCopyWith<_$PaymentError_RefundedImpl> get copyWith => + throw _privateConstructorUsedError; +} + /// @nodoc abstract class _$$PaymentError_SendErrorImplCopyWith<$Res> { factory _$$PaymentError_SendErrorImplCopyWith( diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index a1773a6..08158dd 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.33'; @override - int get rustContentHash => -1225779344; + int get rustContentHash => -451265040; static const kDefaultExternalLibraryLoaderConfig = ExternalLibraryLoaderConfig( stem: 'breez_liquid_sdk', @@ -79,17 +79,9 @@ abstract class RustLibApi extends BaseApi { Future receivePayment({required PrepareReceiveResponse req, dynamic hint}); - Future recoverFunds({required LBtcReverseRecovery recovery, dynamic hint}); - Future restore({required RestoreRequest req, dynamic hint}); Future sendPayment({required PrepareSendResponse req, dynamic hint}); - - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_LBtcReverseRecovery; - - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_LBtcReverseRecovery; - - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_LBtcReverseRecoveryPtr; } class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @@ -283,31 +275,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["req"], ); - @override - Future recoverFunds({required LBtcReverseRecovery recovery, dynamic hint}) { - return handler.executeNormal(NormalTask( - callFfi: (port_) { - var arg0 = - cst_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - recovery); - return wire.wire_recover_funds(port_, arg0); - }, - codec: DcoCodec( - decodeSuccessData: dco_decode_String, - decodeErrorData: dco_decode_AnyhowException, - ), - constMeta: kRecoverFundsConstMeta, - argValues: [recovery], - apiImpl: this, - hint: hint, - )); - } - - TaskConstMeta get kRecoverFundsConstMeta => const TaskConstMeta( - debugName: "recover_funds", - argNames: ["recovery"], - ); - @override Future restore({required RestoreRequest req, dynamic hint}) { return handler.executeNormal(NormalTask( @@ -354,34 +321,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { argNames: ["req"], ); - RustArcIncrementStrongCountFnType get rust_arc_increment_strong_count_LBtcReverseRecovery => wire - .rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery; - - RustArcDecrementStrongCountFnType get rust_arc_decrement_strong_count_LBtcReverseRecovery => wire - .rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery; - @protected AnyhowException dco_decode_AnyhowException(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs return AnyhowException(raw as String); } - @protected - LBtcReverseRecovery - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return LBtcReverseRecovery.dcoDecode(raw as List); - } - - @protected - LBtcReverseRecovery - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return LBtcReverseRecovery.dcoDecode(raw as List); - } - @protected String dco_decode_String(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs @@ -543,30 +488,39 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Dco (DartCObject based), see doc to use other codecs switch (raw[0]) { case 0: - return PaymentError_AmountOutOfRange(); - case 1: return PaymentError_AlreadyClaimed(); + case 1: + return PaymentError_AmountOutOfRange(); case 2: return PaymentError_Generic( err: dco_decode_String(raw[1]), ); case 3: - return PaymentError_InvalidInvoice(); + return PaymentError_InvalidOrExpiredFees(); case 4: - return PaymentError_InvalidPreimage(); + return PaymentError_InsufficientFunds(); case 5: + return PaymentError_InvalidInvoice(); + case 6: + return PaymentError_InvalidPreimage(); + case 7: return PaymentError_LwkError( err: dco_decode_String(raw[1]), ); - case 6: - return PaymentError_PairsNotFound(); - case 7: - return PaymentError_PersistError(); case 8: + return PaymentError_PairsNotFound(); + case 9: + return PaymentError_PersistError(); + case 10: + return PaymentError_Refunded( + err: dco_decode_String(raw[1]), + txid: dco_decode_String(raw[2]), + ); + case 11: return PaymentError_SendError( err: dco_decode_String(raw[1]), ); - case 9: + case 12: return PaymentError_SignerError( err: dco_decode_String(raw[1]), ); @@ -595,11 +549,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { PrepareReceiveResponse dco_decode_prepare_receive_response(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 3) throw Exception('unexpected arr length: expect 3 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return PrepareReceiveResponse( - pairHash: dco_decode_String(arr[0]), - payerAmountSat: dco_decode_u_64(arr[1]), - feesSat: dco_decode_u_64(arr[2]), + payerAmountSat: dco_decode_u_64(arr[0]), + feesSat: dco_decode_u_64(arr[1]), ); } @@ -617,14 +570,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { PrepareSendResponse dco_decode_prepare_send_response(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return PrepareSendResponse( - id: dco_decode_String(arr[0]), - payerAmountSat: dco_decode_u_64(arr[1]), - receiverAmountSat: dco_decode_u_64(arr[2]), - totalFees: dco_decode_u_64(arr[3]), - fundingAddress: dco_decode_String(arr[4]), - invoice: dco_decode_String(arr[5]), + invoice: dco_decode_String(arr[0]), + feesSat: dco_decode_u_64(arr[1]), ); } @@ -683,12 +632,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return; } - @protected - int dco_decode_usize(dynamic raw) { - // Codec=Dco (DartCObject based), see doc to use other codecs - return dcoDecodeI64OrU64(raw); - } - @protected AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -696,22 +639,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return AnyhowException(inner); } - @protected - LBtcReverseRecovery - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return LBtcReverseRecovery.sseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); - } - - @protected - LBtcReverseRecovery - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return LBtcReverseRecovery.sseDecode(sse_decode_usize(deserializer), sse_decode_i_32(deserializer)); - } - @protected String sse_decode_String(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -893,27 +820,35 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { var tag_ = sse_decode_i_32(deserializer); switch (tag_) { case 0: - return PaymentError_AmountOutOfRange(); - case 1: return PaymentError_AlreadyClaimed(); + case 1: + return PaymentError_AmountOutOfRange(); case 2: var var_err = sse_decode_String(deserializer); return PaymentError_Generic(err: var_err); case 3: - return PaymentError_InvalidInvoice(); + return PaymentError_InvalidOrExpiredFees(); case 4: - return PaymentError_InvalidPreimage(); + return PaymentError_InsufficientFunds(); case 5: + return PaymentError_InvalidInvoice(); + case 6: + return PaymentError_InvalidPreimage(); + case 7: var var_err = sse_decode_String(deserializer); return PaymentError_LwkError(err: var_err); - case 6: - return PaymentError_PairsNotFound(); - case 7: - return PaymentError_PersistError(); case 8: + return PaymentError_PairsNotFound(); + case 9: + return PaymentError_PersistError(); + case 10: + var var_err = sse_decode_String(deserializer); + var var_txid = sse_decode_String(deserializer); + return PaymentError_Refunded(err: var_err, txid: var_txid); + case 11: var var_err = sse_decode_String(deserializer); return PaymentError_SendError(err: var_err); - case 9: + case 12: var var_err = sse_decode_String(deserializer); return PaymentError_SignerError(err: var_err); default: @@ -938,11 +873,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected PrepareReceiveResponse sse_decode_prepare_receive_response(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs - var var_pairHash = sse_decode_String(deserializer); var var_payerAmountSat = sse_decode_u_64(deserializer); var var_feesSat = sse_decode_u_64(deserializer); - return PrepareReceiveResponse( - pairHash: var_pairHash, payerAmountSat: var_payerAmountSat, feesSat: var_feesSat); + return PrepareReceiveResponse(payerAmountSat: var_payerAmountSat, feesSat: var_feesSat); } @protected @@ -955,19 +888,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected PrepareSendResponse sse_decode_prepare_send_response(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs - var var_id = sse_decode_String(deserializer); - var var_payerAmountSat = sse_decode_u_64(deserializer); - var var_receiverAmountSat = sse_decode_u_64(deserializer); - var var_totalFees = sse_decode_u_64(deserializer); - var var_fundingAddress = sse_decode_String(deserializer); var var_invoice = sse_decode_String(deserializer); - return PrepareSendResponse( - id: var_id, - payerAmountSat: var_payerAmountSat, - receiverAmountSat: var_receiverAmountSat, - totalFees: var_totalFees, - fundingAddress: var_fundingAddress, - invoice: var_invoice); + var var_feesSat = sse_decode_u_64(deserializer); + return PrepareSendResponse(invoice: var_invoice, feesSat: var_feesSat); } @protected @@ -1015,28 +938,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs } - @protected - int sse_decode_usize(SseDeserializer deserializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - return deserializer.buffer.getUint64(); - } - - @protected - int cst_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery raw) { - // Codec=Cst (C-struct based), see doc to use other codecs -// ignore: invalid_use_of_internal_member - return raw.cstEncode(move: true); - } - - @protected - int cst_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery raw) { - // Codec=Cst (C-struct based), see doc to use other codecs -// ignore: invalid_use_of_internal_member - return raw.cstEncode(); - } - @protected bool cst_encode_bool(bool raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1079,33 +980,12 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return raw; } - @protected - int cst_encode_usize(int raw) { - // Codec=Cst (C-struct based), see doc to use other codecs - return raw; - } - @protected void sse_encode_AnyhowException(AnyhowException self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs throw UnimplementedError('Unreachable ()'); } - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize(self.sseEncode(move: true), serializer); - } - - @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_usize(self.sseEncode(move: null), serializer); - } - @protected void sse_encode_String(String self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -1267,29 +1147,37 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { void sse_encode_payment_error(PaymentError self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs switch (self) { - case PaymentError_AmountOutOfRange(): - sse_encode_i_32(0, serializer); case PaymentError_AlreadyClaimed(): + sse_encode_i_32(0, serializer); + case PaymentError_AmountOutOfRange(): sse_encode_i_32(1, serializer); case PaymentError_Generic(err: final err): sse_encode_i_32(2, serializer); sse_encode_String(err, serializer); - case PaymentError_InvalidInvoice(): + case PaymentError_InvalidOrExpiredFees(): sse_encode_i_32(3, serializer); - case PaymentError_InvalidPreimage(): + case PaymentError_InsufficientFunds(): sse_encode_i_32(4, serializer); - case PaymentError_LwkError(err: final err): + case PaymentError_InvalidInvoice(): sse_encode_i_32(5, serializer); + case PaymentError_InvalidPreimage(): + sse_encode_i_32(6, serializer); + case PaymentError_LwkError(err: final err): + sse_encode_i_32(7, serializer); sse_encode_String(err, serializer); case PaymentError_PairsNotFound(): - sse_encode_i_32(6, serializer); - case PaymentError_PersistError(): - sse_encode_i_32(7, serializer); - case PaymentError_SendError(err: final err): sse_encode_i_32(8, serializer); + case PaymentError_PersistError(): + sse_encode_i_32(9, serializer); + case PaymentError_Refunded(err: final err, txid: final txid): + sse_encode_i_32(10, serializer); + sse_encode_String(err, serializer); + sse_encode_String(txid, serializer); + case PaymentError_SendError(err: final err): + sse_encode_i_32(11, serializer); sse_encode_String(err, serializer); case PaymentError_SignerError(err: final err): - sse_encode_i_32(9, serializer); + sse_encode_i_32(12, serializer); sse_encode_String(err, serializer); } } @@ -1309,7 +1197,6 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_prepare_receive_response(PrepareReceiveResponse self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String(self.pairHash, serializer); sse_encode_u_64(self.payerAmountSat, serializer); sse_encode_u_64(self.feesSat, serializer); } @@ -1323,12 +1210,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_prepare_send_response(PrepareSendResponse self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_String(self.id, serializer); - sse_encode_u_64(self.payerAmountSat, serializer); - sse_encode_u_64(self.receiverAmountSat, serializer); - sse_encode_u_64(self.totalFees, serializer); - sse_encode_String(self.fundingAddress, serializer); sse_encode_String(self.invoice, serializer); + sse_encode_u_64(self.feesSat, serializer); } @protected @@ -1372,10 +1255,4 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { void sse_encode_unit(void self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs } - - @protected - void sse_encode_usize(int self, SseSerializer serializer) { - // Codec=Sse (Serialization based), see doc to use other codecs - serializer.buffer.putUint64(self); - } } diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index 4e7eef8..4dfa98e 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -20,22 +20,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { required super.portManager, }); - CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_LBtcReverseRecoveryPtr => wire - ._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr; - @protected AnyhowException dco_decode_AnyhowException(dynamic raw); - @protected - LBtcReverseRecovery - dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - dynamic raw); - - @protected - LBtcReverseRecovery - dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - dynamic raw); - @protected String dco_decode_String(dynamic raw); @@ -141,22 +128,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void dco_decode_unit(dynamic raw); - @protected - int dco_decode_usize(dynamic raw); - @protected AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); - @protected - LBtcReverseRecovery - sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - SseDeserializer deserializer); - - @protected - LBtcReverseRecovery - sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - SseDeserializer deserializer); - @protected String sse_decode_String(SseDeserializer deserializer); @@ -262,9 +236,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_decode_unit(SseDeserializer deserializer); - @protected - int sse_decode_usize(SseDeserializer deserializer); - @protected ffi.Pointer cst_encode_AnyhowException(AnyhowException raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -463,11 +434,11 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_api_fill_to_wire_payment_error(PaymentError apiObj, wire_cst_payment_error wireObj) { - if (apiObj is PaymentError_AmountOutOfRange) { + if (apiObj is PaymentError_AlreadyClaimed) { wireObj.tag = 0; return; } - if (apiObj is PaymentError_AlreadyClaimed) { + if (apiObj is PaymentError_AmountOutOfRange) { wireObj.tag = 1; return; } @@ -477,37 +448,53 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.kind.Generic.err = pre_err; return; } - if (apiObj is PaymentError_InvalidInvoice) { + if (apiObj is PaymentError_InvalidOrExpiredFees) { wireObj.tag = 3; return; } - if (apiObj is PaymentError_InvalidPreimage) { + if (apiObj is PaymentError_InsufficientFunds) { wireObj.tag = 4; return; } + if (apiObj is PaymentError_InvalidInvoice) { + wireObj.tag = 5; + return; + } + if (apiObj is PaymentError_InvalidPreimage) { + wireObj.tag = 6; + return; + } if (apiObj is PaymentError_LwkError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 5; + wireObj.tag = 7; wireObj.kind.LwkError.err = pre_err; return; } if (apiObj is PaymentError_PairsNotFound) { - wireObj.tag = 6; + wireObj.tag = 8; return; } if (apiObj is PaymentError_PersistError) { - wireObj.tag = 7; + wireObj.tag = 9; + return; + } + if (apiObj is PaymentError_Refunded) { + var pre_err = cst_encode_String(apiObj.err); + var pre_txid = cst_encode_String(apiObj.txid); + wireObj.tag = 10; + wireObj.kind.Refunded.err = pre_err; + wireObj.kind.Refunded.txid = pre_txid; return; } if (apiObj is PaymentError_SendError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 8; + wireObj.tag = 11; wireObj.kind.SendError.err = pre_err; return; } if (apiObj is PaymentError_SignerError) { var pre_err = cst_encode_String(apiObj.err); - wireObj.tag = 9; + wireObj.tag = 12; wireObj.kind.SignerError.err = pre_err; return; } @@ -522,7 +509,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_api_fill_to_wire_prepare_receive_response( PrepareReceiveResponse apiObj, wire_cst_prepare_receive_response wireObj) { - wireObj.pair_hash = cst_encode_String(apiObj.pairHash); wireObj.payer_amount_sat = cst_encode_u_64(apiObj.payerAmountSat); wireObj.fees_sat = cst_encode_u_64(apiObj.feesSat); } @@ -536,12 +522,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_api_fill_to_wire_prepare_send_response( PrepareSendResponse apiObj, wire_cst_prepare_send_response wireObj) { - wireObj.id = cst_encode_String(apiObj.id); - wireObj.payer_amount_sat = cst_encode_u_64(apiObj.payerAmountSat); - wireObj.receiver_amount_sat = cst_encode_u_64(apiObj.receiverAmountSat); - wireObj.total_fees = cst_encode_u_64(apiObj.totalFees); - wireObj.funding_address = cst_encode_String(apiObj.fundingAddress); wireObj.invoice = cst_encode_String(apiObj.invoice); + wireObj.fees_sat = cst_encode_u_64(apiObj.feesSat); } @protected @@ -562,14 +544,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.txid = cst_encode_String(apiObj.txid); } - @protected - int cst_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery raw); - - @protected - int cst_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery raw); - @protected bool cst_encode_bool(bool raw); @@ -591,21 +565,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_encode_unit(void raw); - @protected - int cst_encode_usize(int raw); - @protected void sse_encode_AnyhowException(AnyhowException self, SseSerializer serializer); - @protected - void - sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery self, SseSerializer serializer); - - @protected - void sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - LBtcReverseRecovery self, SseSerializer serializer); - @protected void sse_encode_String(String self, SseSerializer serializer); @@ -710,9 +672,6 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_unit(void self, SseSerializer serializer); - - @protected - void sse_encode_usize(int self, SseSerializer serializer); } // Section: wire_class @@ -871,20 +830,6 @@ class RustLibWire implements BaseWire { late final _wire_receive_payment = _wire_receive_paymentPtr .asFunction)>(); - void wire_recover_funds( - int port_, - int recovery, - ) { - return _wire_recover_funds( - port_, - recovery, - ); - } - - late final _wire_recover_fundsPtr = _lookup>( - 'frbgen_breez_liquid_wire_recover_funds'); - late final _wire_recover_funds = _wire_recover_fundsPtr.asFunction(); - void wire_restore( int port_, ffi.Pointer req, @@ -917,38 +862,6 @@ class RustLibWire implements BaseWire { late final _wire_send_payment = _wire_send_paymentPtr.asFunction)>(); - void - rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ffi.Pointer ptr, - ) { - return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ptr, - ); - } - - late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr = - _lookup)>>( - 'frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery'); - late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery = - _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr - .asFunction)>(); - - void - rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ffi.Pointer ptr, - ) { - return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ptr, - ); - } - - late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr = - _lookup)>>( - 'frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery'); - late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery = - _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr - .asFunction)>(); - ffi.Pointer cst_new_box_autoadd_connect_request() { return _cst_new_box_autoadd_connect_request(); } @@ -1123,8 +1036,6 @@ final class wire_cst_prepare_send_request extends ffi.Struct { } final class wire_cst_prepare_receive_response extends ffi.Struct { - external ffi.Pointer pair_hash; - @ffi.Uint64() external int payer_amount_sat; @@ -1137,20 +1048,10 @@ final class wire_cst_restore_request extends ffi.Struct { } final class wire_cst_prepare_send_response extends ffi.Struct { - external ffi.Pointer id; - - @ffi.Uint64() - external int payer_amount_sat; - - @ffi.Uint64() - external int receiver_amount_sat; - - @ffi.Uint64() - external int total_fees; - - external ffi.Pointer funding_address; - external ffi.Pointer invoice; + + @ffi.Uint64() + external int fees_sat; } final class wire_cst_payment extends ffi.Struct { @@ -1191,6 +1092,12 @@ final class wire_cst_PaymentError_LwkError extends ffi.Struct { external ffi.Pointer err; } +final class wire_cst_PaymentError_Refunded extends ffi.Struct { + external ffi.Pointer err; + + external ffi.Pointer txid; +} + final class wire_cst_PaymentError_SendError extends ffi.Struct { external ffi.Pointer err; } @@ -1204,6 +1111,8 @@ final class PaymentErrorKind extends ffi.Union { external wire_cst_PaymentError_LwkError LwkError; + external wire_cst_PaymentError_Refunded Refunded; + external wire_cst_PaymentError_SendError SendError; external wire_cst_PaymentError_SignerError SignerError; @@ -1226,4 +1135,4 @@ final class wire_cst_send_payment_response extends ffi.Struct { external ffi.Pointer txid; } -const double LIQUID_CLAIM_TX_FEERATE = 0.1; +const double LIQUID_CLAIM_TX_FEERATE_MSAT = 100.0; diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index 1dc65fa..169301d 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -79,8 +79,6 @@ class Payment { final int amountSat; final int? feesSat; final PaymentType paymentType; - - /// Only for [PaymentType::PendingReceive] final String? invoice; const Payment({ @@ -141,25 +139,22 @@ class PrepareReceiveRequest { } class PrepareReceiveResponse { - final String pairHash; final int payerAmountSat; final int feesSat; const PrepareReceiveResponse({ - required this.pairHash, required this.payerAmountSat, required this.feesSat, }); @override - int get hashCode => pairHash.hashCode ^ payerAmountSat.hashCode ^ feesSat.hashCode; + int get hashCode => payerAmountSat.hashCode ^ feesSat.hashCode; @override bool operator ==(Object other) => identical(this, other) || other is PrepareReceiveResponse && runtimeType == other.runtimeType && - pairHash == other.pairHash && payerAmountSat == other.payerAmountSat && feesSat == other.feesSat; } @@ -181,42 +176,24 @@ class PrepareSendRequest { } class PrepareSendResponse { - final String id; - final int payerAmountSat; - final int receiverAmountSat; - final int totalFees; - final String fundingAddress; final String invoice; + final int feesSat; const PrepareSendResponse({ - required this.id, - required this.payerAmountSat, - required this.receiverAmountSat, - required this.totalFees, - required this.fundingAddress, required this.invoice, + required this.feesSat, }); @override - int get hashCode => - id.hashCode ^ - payerAmountSat.hashCode ^ - receiverAmountSat.hashCode ^ - totalFees.hashCode ^ - fundingAddress.hashCode ^ - invoice.hashCode; + int get hashCode => invoice.hashCode ^ feesSat.hashCode; @override bool operator ==(Object other) => identical(this, other) || other is PrepareSendResponse && runtimeType == other.runtimeType && - id == other.id && - payerAmountSat == other.payerAmountSat && - receiverAmountSat == other.receiverAmountSat && - totalFees == other.totalFees && - fundingAddress == other.fundingAddress && - invoice == other.invoice; + invoice == other.invoice && + feesSat == other.feesSat; } class ReceivePaymentResponse { diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index 0ed16d8..60ad6b0 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -23,6 +23,6 @@ dev_dependencies: test: ^1.25.4 dependencies: ffi: ^2.1.2 - flutter_rust_bridge: ">=2.0.0-dev.0 <=2.0.0-dev.33" + flutter_rust_bridge: 2.0.0-dev.33 freezed_annotation: ^2.4.1 meta: ^1.11.0 diff --git a/packages/flutter/example/lib/main.dart b/packages/flutter/example/lib/main.dart index 98e3bf5..b805a19 100644 --- a/packages/flutter/example/lib/main.dart +++ b/packages/flutter/example/lib/main.dart @@ -106,11 +106,7 @@ class _MyAppState extends State { return const Text('Loading...'); } - if (snapshot.requireData.pairHash.isEmpty) { - return const Text('No pair hash.'); - } final prepareReceiveResponse = snapshot.data!; - debugPrint(prepareReceiveResponse.pairHash); return Column( children: [ @@ -121,9 +117,6 @@ class _MyAppState extends State { style: Theme.of(context).textTheme.headlineSmall, ), ), - ListTile( - title: Text("Pair Hash: ${prepareReceiveResponse.pairHash}"), - ), ListTile( title: Text("Payer Amount: ${prepareReceiveResponse.payerAmountSat} (in sats)"), ), diff --git a/packages/flutter/ffigen.yaml b/packages/flutter/ffigen.yaml index 2d2c6eb..5f49894 100644 --- a/packages/flutter/ffigen.yaml +++ b/packages/flutter/ffigen.yaml @@ -17,4 +17,4 @@ preamble: | comments: style: any length: full -ignore-source-errors: true \ No newline at end of file +ignore-source-errors: true diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index d4d3196..0db3640 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -164,22 +164,6 @@ class FlutterBreezLiquidBindings { late final _frbgen_breez_liquid_wire_receive_payment = _frbgen_breez_liquid_wire_receive_paymentPtr .asFunction)>(); - void frbgen_breez_liquid_wire_recover_funds( - int port_, - int recovery, - ) { - return _frbgen_breez_liquid_wire_recover_funds( - port_, - recovery, - ); - } - - late final _frbgen_breez_liquid_wire_recover_fundsPtr = - _lookup>( - 'frbgen_breez_liquid_wire_recover_funds'); - late final _frbgen_breez_liquid_wire_recover_funds = - _frbgen_breez_liquid_wire_recover_fundsPtr.asFunction(); - void frbgen_breez_liquid_wire_restore( int port_, ffi.Pointer req, @@ -212,38 +196,6 @@ class FlutterBreezLiquidBindings { late final _frbgen_breez_liquid_wire_send_payment = _frbgen_breez_liquid_wire_send_paymentPtr .asFunction)>(); - void - frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ffi.Pointer ptr, - ) { - return _frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ptr, - ); - } - - late final _frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr = - _lookup)>>( - 'frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery'); - late final _frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery = - _frbgen_breez_liquid_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr - .asFunction)>(); - - void - frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ffi.Pointer ptr, - ) { - return _frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery( - ptr, - ); - } - - late final _frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr = - _lookup)>>( - 'frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery'); - late final _frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecovery = - _frbgen_breez_liquid_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerLBtcReverseRecoveryPtr - .asFunction)>(); - ffi.Pointer frbgen_breez_liquid_cst_new_box_autoadd_connect_request() { return _frbgen_breez_liquid_cst_new_box_autoadd_connect_request(); } @@ -440,8 +392,6 @@ final class wire_cst_prepare_send_request extends ffi.Struct { } final class wire_cst_prepare_receive_response extends ffi.Struct { - external ffi.Pointer pair_hash; - @ffi.Uint64() external int payer_amount_sat; @@ -454,20 +404,10 @@ final class wire_cst_restore_request extends ffi.Struct { } final class wire_cst_prepare_send_response extends ffi.Struct { - external ffi.Pointer id; - - @ffi.Uint64() - external int payer_amount_sat; - - @ffi.Uint64() - external int receiver_amount_sat; - - @ffi.Uint64() - external int total_fees; - - external ffi.Pointer funding_address; - external ffi.Pointer invoice; + + @ffi.Uint64() + external int fees_sat; } final class wire_cst_payment extends ffi.Struct { @@ -508,6 +448,12 @@ final class wire_cst_PaymentError_LwkError extends ffi.Struct { external ffi.Pointer err; } +final class wire_cst_PaymentError_Refunded extends ffi.Struct { + external ffi.Pointer err; + + external ffi.Pointer txid; +} + final class wire_cst_PaymentError_SendError extends ffi.Struct { external ffi.Pointer err; } @@ -521,6 +467,8 @@ final class PaymentErrorKind extends ffi.Union { external wire_cst_PaymentError_LwkError LwkError; + external wire_cst_PaymentError_Refunded Refunded; + external wire_cst_PaymentError_SendError SendError; external wire_cst_PaymentError_SignerError SignerError; @@ -543,4 +491,4 @@ final class wire_cst_send_payment_response extends ffi.Struct { external ffi.Pointer txid; } -const double LIQUID_CLAIM_TX_FEERATE = 0.1; +const double LIQUID_CLAIM_TX_FEERATE_MSAT = 100.0; 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 a450d71..2f04e6f 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 @@ -150,7 +150,6 @@ fun asPrepareReceiveResponse(prepareReceiveResponse: ReadableMap): PrepareReceiv if (!validateMandatoryFields( prepareReceiveResponse, arrayOf( - "pairHash", "payerAmountSat", "feesSat", ), @@ -158,11 +157,9 @@ fun asPrepareReceiveResponse(prepareReceiveResponse: ReadableMap): PrepareReceiv ) { return null } - val pairHash = prepareReceiveResponse.getString("pairHash")!! val payerAmountSat = prepareReceiveResponse.getDouble("payerAmountSat").toULong() val feesSat = prepareReceiveResponse.getDouble("feesSat").toULong() return PrepareReceiveResponse( - pairHash, payerAmountSat, feesSat, ) @@ -170,7 +167,6 @@ fun asPrepareReceiveResponse(prepareReceiveResponse: ReadableMap): PrepareReceiv fun readableMapOf(prepareReceiveResponse: PrepareReceiveResponse): ReadableMap { return readableMapOf( - "pairHash" to prepareReceiveResponse.pairHash, "payerAmountSat" to prepareReceiveResponse.payerAmountSat, "feesSat" to prepareReceiveResponse.feesSat, ) @@ -224,41 +220,25 @@ fun asPrepareSendResponse(prepareSendResponse: ReadableMap): PrepareSendResponse if (!validateMandatoryFields( prepareSendResponse, arrayOf( - "id", - "payerAmountSat", - "receiverAmountSat", - "totalFees", - "fundingAddress", "invoice", + "feesSat", ), ) ) { return null } - val id = prepareSendResponse.getString("id")!! - val payerAmountSat = prepareSendResponse.getDouble("payerAmountSat").toULong() - val receiverAmountSat = prepareSendResponse.getDouble("receiverAmountSat").toULong() - val totalFees = prepareSendResponse.getDouble("totalFees").toULong() - val fundingAddress = prepareSendResponse.getString("fundingAddress")!! val invoice = prepareSendResponse.getString("invoice")!! + val feesSat = prepareSendResponse.getDouble("feesSat").toULong() return PrepareSendResponse( - id, - payerAmountSat, - receiverAmountSat, - totalFees, - fundingAddress, invoice, + feesSat, ) } fun readableMapOf(prepareSendResponse: PrepareSendResponse): ReadableMap { return readableMapOf( - "id" to prepareSendResponse.id, - "payerAmountSat" to prepareSendResponse.payerAmountSat, - "receiverAmountSat" to prepareSendResponse.receiverAmountSat, - "totalFees" to prepareSendResponse.totalFees, - "fundingAddress" to prepareSendResponse.fundingAddress, "invoice" to prepareSendResponse.invoice, + "feesSat" to prepareSendResponse.feesSat, ) } diff --git a/packages/react-native/ios/BreezLiquidSDKMapper.swift b/packages/react-native/ios/BreezLiquidSDKMapper.swift index fbfa04c..8409800 100644 --- a/packages/react-native/ios/BreezLiquidSDKMapper.swift +++ b/packages/react-native/ios/BreezLiquidSDKMapper.swift @@ -154,9 +154,6 @@ enum BreezLiquidSDKMapper { } static func asPrepareReceiveResponse(prepareReceiveResponse: [String: Any?]) throws -> PrepareReceiveResponse { - guard let pairHash = prepareReceiveResponse["pairHash"] as? String else { - throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "pairHash", typeName: "PrepareReceiveResponse")) - } guard let payerAmountSat = prepareReceiveResponse["payerAmountSat"] as? UInt64 else { throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "PrepareReceiveResponse")) } @@ -165,7 +162,6 @@ enum BreezLiquidSDKMapper { } return PrepareReceiveResponse( - pairHash: pairHash, payerAmountSat: payerAmountSat, feesSat: feesSat ) @@ -173,7 +169,6 @@ enum BreezLiquidSDKMapper { static func dictionaryOf(prepareReceiveResponse: PrepareReceiveResponse) -> [String: Any?] { return [ - "pairHash": prepareReceiveResponse.pairHash, "payerAmountSat": prepareReceiveResponse.payerAmountSat, "feesSat": prepareReceiveResponse.feesSat, ] @@ -229,43 +224,23 @@ enum BreezLiquidSDKMapper { } static func asPrepareSendResponse(prepareSendResponse: [String: Any?]) throws -> PrepareSendResponse { - guard let id = prepareSendResponse["id"] as? String else { - throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "id", typeName: "PrepareSendResponse")) - } - guard let payerAmountSat = prepareSendResponse["payerAmountSat"] as? UInt64 else { - throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "payerAmountSat", typeName: "PrepareSendResponse")) - } - guard let receiverAmountSat = prepareSendResponse["receiverAmountSat"] as? UInt64 else { - throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "receiverAmountSat", typeName: "PrepareSendResponse")) - } - guard let totalFees = prepareSendResponse["totalFees"] as? UInt64 else { - throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "totalFees", typeName: "PrepareSendResponse")) - } - guard let fundingAddress = prepareSendResponse["fundingAddress"] as? String else { - throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "fundingAddress", typeName: "PrepareSendResponse")) - } guard let invoice = prepareSendResponse["invoice"] as? String else { throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "invoice", typeName: "PrepareSendResponse")) } + guard let feesSat = prepareSendResponse["feesSat"] as? UInt64 else { + throw LiquidSdkError.Generic(message: errMissingMandatoryField(fieldName: "feesSat", typeName: "PrepareSendResponse")) + } return PrepareSendResponse( - id: id, - payerAmountSat: payerAmountSat, - receiverAmountSat: receiverAmountSat, - totalFees: totalFees, - fundingAddress: fundingAddress, - invoice: invoice + invoice: invoice, + feesSat: feesSat ) } static func dictionaryOf(prepareSendResponse: PrepareSendResponse) -> [String: Any?] { return [ - "id": prepareSendResponse.id, - "payerAmountSat": prepareSendResponse.payerAmountSat, - "receiverAmountSat": prepareSendResponse.receiverAmountSat, - "totalFees": prepareSendResponse.totalFees, - "fundingAddress": prepareSendResponse.fundingAddress, "invoice": prepareSendResponse.invoice, + "feesSat": prepareSendResponse.feesSat, ] } diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index e9e81fb..8bf5d88 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -37,7 +37,6 @@ export interface PrepareReceiveRequest { } export interface PrepareReceiveResponse { - pairHash: string payerAmountSat: number feesSat: number } @@ -47,12 +46,8 @@ export interface PrepareSendRequest { } export interface PrepareSendResponse { - id: string - payerAmountSat: number - receiverAmountSat: number - totalFees: number - fundingAddress: string invoice: string + feesSat: number } export interface ReceivePaymentResponse {