From d9e7a95a1a38669bc9f6a6c901262537d9c5e6ff Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 26 Sep 2025 19:48:46 +0000 Subject: [PATCH 1/9] Update to LDK 0.2 keeping sync, update Cargo.lock with new deps Naively update to LDK 0.2 using the sync wrappers to keep current behavior. --- Cargo.lock | 698 ++++++++++++++++++++++++++--------------- Cargo.toml | 17 +- src/bitcoind_client.rs | 9 +- src/cli.rs | 89 +++--- src/main.rs | 680 +++++++++++++++++++-------------------- src/sweep.rs | 4 +- 6 files changed, 846 insertions(+), 651 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c20c1197..bc396bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "android_system_properties" @@ -26,25 +26,41 @@ dependencies = [ "libc", ] +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-link", +] + +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals", + "bitcoin_hashes", ] [[package]] @@ -61,21 +77,26 @@ checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" [[package]] name = "bech32" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bitcoin" -version = "0.30.2" +version = "0.32.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1945a5048598e4189e239d3f809b19bdad4845c4b2ba400d304d2dcf26d2c462" +checksum = "0fda569d741b895131a88ee5589a467e73e9c4718e958ac9308e4f7dc44b6945" dependencies = [ - "bech32 0.9.1", - "bitcoin-private", + "base58ck", + "bech32 0.11.0", + "bitcoin-internals", + "bitcoin-io", + "bitcoin-units", "bitcoin_hashes", + "hex-conservative", "hex_lit", "secp256k1", + "serde", ] [[package]] @@ -88,148 +109,152 @@ dependencies = [ ] [[package]] -name = "bitcoin-private" -version = "0.1.0" +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoin-io" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin-units" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2" +dependencies = [ + "bitcoin-internals", + "serde", +] [[package]] name = "bitcoin_hashes" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" dependencies = [ - "bitcoin-private", + "bitcoin-io", + "hex-conservative", + "serde", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytes" -version = "1.3.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.0.78" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "chrono" -version = "0.4.23" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", - "num-integer", "num-traits", - "winapi", + "serde", + "windows-link", ] [[package]] name = "chunked_transfer" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" - -[[package]] -name = "codespan-reporting" -version = "0.11.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "cxx" -version = "1.0.86" +name = "dnssec-prover" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d1075c37807dcf850c379432f0df05ba52cc30f279c5cfc43cc221ce7f8579" +checksum = "ec4f825369fc7134da70ca4040fddc8e03b80a46d249ae38d9c1c39b7b4476bf" dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", + "bitcoin_hashes", + "tokio", ] [[package]] -name = "cxx-build" -version = "1.0.86" +name = "find-msvc-tools" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5044281f61b27bc598f2f6647d480aed48d2bf52d6eb0b627d84c0361b17aa70" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.107", -] +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" [[package]] -name = "cxxbridge-flags" -version = "1.0.86" +name = "fuchsia-cprng" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b50bc93ba22c27b0d31128d2d130a0a6b3d267ae27ef7e4fae2167dfe8781c" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] -name = "cxxbridge-macro" -version = "1.0.86" +name = "getrandom" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e61fda7e62115119469c7b3591fd913ecca96fb766cfd3f2e2502ab7bc87a5" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", + "cfg-if", + "libc", + "wasi", ] [[package]] -name = "fuchsia-cprng" -version = "0.1.1" +name = "gimli" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] -name = "gimli" -version = "0.28.1" +name = "hashbrown" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" [[package]] name = "hermit-abi" -version = "0.2.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex-conservative" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] [[package]] name = "hex_lit" @@ -239,40 +264,41 @@ checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" [[package]] name = "iana-time-zone" -version = "0.1.53" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "winapi", + "windows-core", ] [[package]] name = "iana-time-zone-haiku" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "cxx", - "cxx-build", + "cc", ] [[package]] name = "itoa" -version = "1.0.5" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -289,6 +315,7 @@ dependencies = [ "lightning", "lightning-background-processor", "lightning-block-sync", + "lightning-dns-resolver", "lightning-invoice", "lightning-net-tokio", "lightning-persister", @@ -300,62 +327,102 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] -name = "lightning" -version = "0.0.123" +name = "libm" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd92d4aa159374be430c7590e169b4a6c0fb79018f5bc4ea1bffde536384db3" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "lightning" +version = "0.2.0+git" dependencies = [ + "bech32 0.11.0", "bitcoin", - "hex-conservative", + "dnssec-prover", + "hashbrown", + "libm", + "lightning-invoice", + "lightning-macros", + "lightning-types", + "musig2", + "possiblyrandom", ] [[package]] name = "lightning-background-processor" -version = "0.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1c2c64050e37cee7c3b6b022106523784055ac3ee572d360780a1d6fe8062c" +version = "0.2.0+git" dependencies = [ "bitcoin", + "bitcoin-io", + "bitcoin_hashes", "lightning", + "lightning-liquidity", "lightning-rapid-gossip-sync", + "possiblyrandom", ] [[package]] name = "lightning-block-sync" -version = "0.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e1e70fa351daccede0c366cf16320b16a3e42b05ae3c7ec9c0df6b5d3a3e18" +version = "0.2.0+git" dependencies = [ "bitcoin", "chunked_transfer", - "hex-conservative", "lightning", "serde_json", "tokio", ] +[[package]] +name = "lightning-dns-resolver" +version = "0.3.0+git" +dependencies = [ + "dnssec-prover", + "lightning", + "lightning-types", + "tokio", +] + [[package]] name = "lightning-invoice" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d07d01cf197bf2184b929b7dc94aa70d935aac6df896c256a3a9475b7e9d40" +version = "0.34.0+git" dependencies = [ - "bech32 0.9.1", + "bech32 0.11.0", "bitcoin", + "lightning-types", + "serde", +] + +[[package]] +name = "lightning-liquidity" +version = "0.2.0+git" +dependencies = [ + "bitcoin", + "chrono", "lightning", - "secp256k1", + "lightning-invoice", + "lightning-macros", + "lightning-types", + "serde", + "serde_json", +] + +[[package]] +name = "lightning-macros" +version = "0.2.0+git" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "lightning-net-tokio" -version = "0.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e6a4d49c50a1344916d080dc8c012ce3a778cdd45de8def75350b2b40fe018" +version = "0.2.0+git" dependencies = [ "bitcoin", "lightning", @@ -364,93 +431,84 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8dd33971815fa074b05678e09a6d4b15c78225ea34d66ed4f17c35a53467a9" +version = "0.2.0+git" dependencies = [ "bitcoin", "lightning", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "lightning-rapid-gossip-sync" -version = "0.0.123" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d861b0f0cd5f8fe8c63760023c4fd4fd32c384881b41780b62ced2a8a619f91" +version = "0.2.0+git" dependencies = [ "bitcoin", + "bitcoin-io", + "bitcoin_hashes", "lightning", ] [[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +name = "lightning-types" +version = "0.3.0+git" dependencies = [ - "cc", + "bitcoin", ] [[package]] name = "log" -version = "0.4.17" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +name = "musig2" +version = "0.1.0" +source = "git+https://github.com/arik-so/rust-musig2?rev=6f95a05718cbb44d8fe3fa6021aea8117aa38d50#6f95a05718cbb44d8fe3fa6021aea8117aa38d50" dependencies = [ - "autocfg", - "num-traits", + "bitcoin", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.15.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" dependencies = [ "hermit-abi", "libc", @@ -458,39 +516,46 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.17.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "possiblyrandom" +version = "0.2.0" +dependencies = [ + "getrandom", +] [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -534,104 +599,117 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] -name = "ryu" -version = "1.0.12" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "scratch" -version = "1.0.3" +name = "ryu" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "secp256k1" -version = "0.27.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "bitcoin_hashes", "secp256k1-sys", + "serde", ] [[package]] name = "secp256k1-sys" -version = "0.8.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" dependencies = [ "cc", ] [[package]] name = "serde" -version = "1.0.152" +version = "1.0.227" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80ece43fc6fbed4eb5392ab50c07334d3e577cbf40997ee896fe7af40bba4245" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.227" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a576275b607a2c86ea29e410193df32bc680303c82f31e275bbfcafe8b33be5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.227" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +checksum = "51e694923b8824cf0e9b382adf0f60d4e05f348f357b38833a3fa5ed7c2ede04" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "serde_json" -version = "1.0.91" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", + "memchr", "ryu", "serde", + "serde_core", ] [[package]] -name = "socket2" -version = "0.5.5" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" -dependencies = [ - "libc", - "windows-sys", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "syn" -version = "1.0.107" +name = "socket2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "libc", + "windows-sys 0.52.0", ] [[package]] name = "syn" -version = "2.0.47" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - [[package]] name = "tokio" -version = "1.35.1" +version = "1.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "68722da18b0fc4a05fdc1120b302b82051265792a1e1b399086e9b204b10ad3d" dependencies = [ "backtrace", "bytes", @@ -641,68 +719,64 @@ dependencies = [ "pin-project-lite", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn", ] [[package]] name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-width" -version = "0.1.10" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 1.0.107", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -710,22 +784,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.107", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] [[package]] name = "winapi" @@ -744,19 +821,69 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" dependencies = [ - "winapi", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" +name = "windows-implement" +version = "0.60.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" @@ -764,7 +891,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -773,13 +909,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -788,38 +940,86 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml index 20ebddb9..4dcbe569 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,14 +8,15 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lightning = { version = "0.1.0", features = ["dnssec"] } -lightning-block-sync = { version = "0.1.0", features = [ "rpc-client", "tokio" ] } -lightning-dns-resolver = { version = "0.2.0" } -lightning-invoice = { version = "0.33.0" } -lightning-net-tokio = { version = "0.1.0" } -lightning-persister = { version = "0.1.0" } -lightning-background-processor = { version = "0.1.0", features = [ "futures" ] } -lightning-rapid-gossip-sync = { version = "0.1.0" } +lightning = { version = "0.2.0-beta1", features = ["dnssec"] } +lightning-block-sync = { version = "0.2.0-beta1", features = [ "rpc-client", "tokio" ] } +lightning-dns-resolver = { version = "0.3.0-beta1" } +lightning-invoice = { version = "0.34.0-beta1" } +lightning-net-tokio = { version = "0.2.0-beta1" } +lightning-persister = { version = "0.2.0-beta1" } +lightning-background-processor = { version = "0.2.0-beta1" } +lightning-rapid-gossip-sync = { version = "0.2.0-beta1" } +lightning-macros = { version = "0.2.0-beta1" } base64 = "0.13.0" bitcoin = "0.32" diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs index f16c5f41..80a1e9dc 100644 --- a/src/bitcoind_client.rs +++ b/src/bitcoind_client.rs @@ -16,9 +16,10 @@ use bitcoin::key::XOnlyPublicKey; use bitcoin::psbt::Psbt; use bitcoin::{Network, OutPoint, TxOut, WPubkeyHash}; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::events::bump_transaction::{Utxo, WalletSource}; +use lightning::events::bump_transaction::Utxo; +use lightning::events::bump_transaction::sync::WalletSourceSync; use lightning::log_error; -use lightning::sign::ChangeDestinationSource; +use lightning::sign::ChangeDestinationSourceSync; use lightning::util::logger::Logger; use lightning_block_sync::http::HttpEndpoint; use lightning_block_sync::rpc::RpcClient; @@ -405,14 +406,14 @@ impl BroadcasterInterface for BitcoindClient { } } -impl ChangeDestinationSource for BitcoindClient { +impl ChangeDestinationSourceSync for BitcoindClient { fn get_change_destination_script(&self) -> Result { let future = self.get_new_address(); Ok(self.run_future_in_blocking_context(async move { future.await.script_pubkey() })) } } -impl WalletSource for BitcoindClient { +impl WalletSourceSync for BitcoindClient { fn list_confirmed_utxos(&self) -> Result, ()> { let future = self.list_unspent(); let utxos = self.run_future_in_blocking_context(async move { future.await.0 }); diff --git a/src/cli.rs b/src/cli.rs index 528cc73f..df0fb2e4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,10 +9,8 @@ use bitcoin::hashes::Hash; use bitcoin::network::Network; use bitcoin::secp256k1::PublicKey; use lightning::chain::channelmonitor::Balance; -use lightning::ln::bolt11_payment::payment_parameters_from_invoice; -use lightning::ln::bolt11_payment::payment_parameters_from_variable_amount_invoice; use lightning::ln::channelmanager::{ - Bolt11InvoiceParameters, PaymentId, RecipientOnionFields, Retry, + Bolt11InvoiceParameters, OptionalOfferPaymentParams, PaymentId, RecipientOnionFields, Retry, }; use lightning::ln::msgs::SocketAddress; use lightning::ln::types::ChannelId; @@ -20,11 +18,11 @@ use lightning::offers::offer::{self, Offer}; use lightning::onion_message::dns_resolution::HumanReadableName; use lightning::onion_message::messenger::Destination; use lightning::routing::gossip::NodeId; -use lightning::routing::router::{PaymentParameters, RouteParameters}; +use lightning::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig}; use lightning::sign::{EntropySource, KeysManager}; use lightning::types::payment::{PaymentHash, PaymentPreimage}; use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, UserConfig}; -use lightning::util::persist::KVStore; +use lightning::util::persist::KVStoreSync; use lightning::util::ser::Writeable; use lightning_invoice::Bolt11Invoice; use lightning_persister::fs_store::FilesystemStore; @@ -229,13 +227,15 @@ pub(crate) fn poll_for_user_input( }, ); fs_store - .write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()) + .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) .unwrap(); - let retry = Retry::Timeout(Duration::from_secs(10)); + let params = OptionalOfferPaymentParams { + retry_strategy: Retry::Timeout(Duration::from_secs(10)), + ..Default::default() + }; let amt = Some(amt_msat); - let pay = channel_manager - .pay_for_offer(&offer, None, amt, None, payment_id, retry, None); + let pay = channel_manager.pay_for_offer(&offer, amt, payment_id, params); if pay.is_ok() { println!("Payment in flight"); } else { @@ -292,14 +292,17 @@ pub(crate) fn poll_for_user_input( }, ); fs_store - .write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()) + .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) .unwrap(); - let retry = Retry::Timeout(Duration::from_secs(10)); - let pay = |a, b, c, d, e, f| { - channel_manager.pay_for_offer_from_human_readable_name(a, b, c, d, e, f) + let params = OptionalOfferPaymentParams { + retry_strategy: Retry::Timeout(Duration::from_secs(10)), + ..Default::default() + }; + let pay = |a, b, c, d, e| { + channel_manager.pay_for_offer_from_human_readable_name(a, b, c, d, e) }; - let pay = pay(hrn, amt_msat, payment_id, retry, None, dns_resolvers); + let pay = pay(hrn, amt_msat, payment_id, params, dns_resolvers); if pay.is_ok() { println!("Payment in flight"); } else { @@ -358,7 +361,7 @@ pub(crate) fn poll_for_user_input( ); }, "getoffer" => { - let offer_builder = channel_manager.create_offer_builder(None); + let offer_builder = channel_manager.create_offer_builder(); if let Err(e) = offer_builder { println!("ERROR: Failed to initiate offer building: {:?}", e); continue; @@ -418,7 +421,7 @@ pub(crate) fn poll_for_user_input( expiry_secs.unwrap(), ); fs_store - .write("", "", INBOUND_PAYMENTS_FNAME, &inbound_payments.encode()) + .write("", "", INBOUND_PAYMENTS_FNAME, inbound_payments.encode()) .unwrap(); }, "connectpeer" => { @@ -618,8 +621,12 @@ fn node_info( let local_balance_sat = balances.iter().map(|b| b.claimable_amount_satoshis()).sum::(); println!("\t\t local_balance_sats: {}", local_balance_sat); let close_fees_map = |b| match b { - &Balance::ClaimableOnChannelClose { transaction_fee_satoshis, .. } => { - transaction_fee_satoshis + &Balance::ClaimableOnChannelClose { + ref balance_candidates, + confirmed_balance_candidate_index, + .. + } => { + balance_candidates[confirmed_balance_candidate_index].transaction_fee_satoshis }, _ => 0, }; @@ -829,36 +836,20 @@ fn send_payment( ) { let payment_id = PaymentId((*invoice.payment_hash()).to_byte_array()); let payment_secret = Some(*invoice.payment_secret()); - let zero_amt_invoice = - invoice.amount_milli_satoshis().is_none() || invoice.amount_milli_satoshis() == Some(0); - let pay_params_opt = if zero_amt_invoice { - if let Some(amt_msat) = required_amount_msat { - payment_parameters_from_variable_amount_invoice(invoice, amt_msat) - } else { - println!("Need an amount for the given 0-value invoice"); - print!("> "); - return; - } - } else { - if required_amount_msat.is_some() && invoice.amount_milli_satoshis() != required_amount_msat - { + match (invoice.amount_milli_satoshis(), required_amount_msat) { + // pay_for_bolt11_invoice only validates that the amount we pay is >= the invoice's + // required amount, not that its equal (to allow for overpayment). As that is somewhat + // surprising, here we check and reject all disagreements in amount. + (Some(inv_amt), Some(req_amt)) if inv_amt != req_amt => { println!( "Amount didn't match invoice value of {}msat", invoice.amount_milli_satoshis().unwrap_or(0) ); print!("> "); return; - } - payment_parameters_from_invoice(invoice) - }; - let (payment_hash, recipient_onion, route_params) = match pay_params_opt { - Ok(res) => res, - Err(e) => { - println!("Failed to parse invoice: {:?}", e); - print!("> "); - return; }, - }; + _ => {}, + } outbound_payments.payments.insert( payment_id, PaymentInfo { @@ -868,13 +859,13 @@ fn send_payment( amt_msat: MillisatAmount(invoice.amount_milli_satoshis()), }, ); - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap(); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); - match channel_manager.send_payment( - payment_hash, - recipient_onion, + match channel_manager.pay_for_bolt11_invoice( + invoice, payment_id, - route_params, + required_amount_msat, + RouteParametersConfig::default(), Retry::Timeout(Duration::from_secs(10)), ) { Ok(_) => { @@ -887,7 +878,7 @@ fn send_payment( println!("ERROR: failed to send payment: {:?}", e); print!("> "); outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap(); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); }, }; } @@ -912,7 +903,7 @@ fn keysend( amt_msat: MillisatAmount(Some(amt_msat)), }, ); - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap(); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); match channel_manager.send_spontaneous_payment( Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), @@ -928,7 +919,7 @@ fn keysend( println!("ERROR: failed to send payment: {:?}", e); print!("> "); outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap(); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); }, }; } diff --git a/src/main.rs b/src/main.rs index 370838da..0ef56d39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use bitcoin_bech32::WitnessProgram; use disk::{INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME}; use lightning::chain::{chainmonitor, ChannelMonitorUpdateStatus}; use lightning::chain::{BestBlock, Filter, Watch}; -use lightning::events::bump_transaction::{BumpTransactionEventHandler, Wallet}; +use lightning::events::bump_transaction::sync::{BumpTransactionEventHandlerSync, WalletSync}; use lightning::events::{Event, PaymentFailureReason, PaymentPurpose}; use lightning::ln::channelmanager::{self, RecentPaymentDetails}; use lightning::ln::channelmanager::{ @@ -35,19 +35,21 @@ use lightning::routing::gossip; use lightning::routing::gossip::{NodeId, P2PGossipSync}; use lightning::routing::router::DefaultRouter; use lightning::routing::scoring::ProbabilisticScoringFeeParameters; -use lightning::sign::{EntropySource, InMemorySigner, KeysManager}; +use lightning::sign::{EntropySource, InMemorySigner, KeysManager, NodeSigner}; use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::util::config::UserConfig; use lightning::util::hash_tables::hash_map::Entry; use lightning::util::hash_tables::HashMap; use lightning::util::persist::{ - self, KVStore, MonitorUpdatingPersister, OUTPUT_SWEEPER_PERSISTENCE_KEY, + self, KVStoreSync, MonitorUpdatingPersister, OUTPUT_SWEEPER_PERSISTENCE_KEY, OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, }; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use lightning::util::sweep as ldk_sweep; use lightning::{chain, impl_writeable_tlv_based, impl_writeable_tlv_based_enum}; -use lightning_background_processor::{process_events_async, GossipSync}; +use lightning_background_processor::{ + process_events_async_with_kv_store_sync, GossipSync, NO_LIQUIDITY_MANAGER, +}; use lightning_block_sync::init; use lightning_block_sync::poll; use lightning_block_sync::SpvClient; @@ -151,6 +153,7 @@ type ChainMonitor = chainmonitor::ChainMonitor< Arc, >, >, + Arc, >; pub(crate) type GossipVerifier = lightning_block_sync::gossip::GossipVerifier< @@ -169,6 +172,7 @@ pub(crate) type PeerManager = LdkPeerManager< Arc, IgnoringMessageHandler, Arc, + Arc, >; pub(crate) type ChannelManager = @@ -190,14 +194,14 @@ type OnionMessenger = LdkOnionMessenger< IgnoringMessageHandler, >; -pub(crate) type BumpTxEventHandler = BumpTransactionEventHandler< +pub(crate) type BumpTxEventHandler = BumpTransactionEventHandlerSync< Arc, - Arc, Arc>>, + Arc, Arc>>, Arc, Arc, >; -pub(crate) type OutputSweeper = ldk_sweep::OutputSweeper< +pub(crate) type OutputSweeper = ldk_sweep::OutputSweeperSync< Arc, Arc, Arc, @@ -210,339 +214,333 @@ pub(crate) type OutputSweeper = ldk_sweep::OutputSweeper< // Needed due to rust-lang/rust#63033. struct OutputSweeperWrapper(Arc); -async fn handle_ldk_events( - channel_manager: Arc, bitcoind_client: &BitcoindClient, - network_graph: &NetworkGraph, keys_manager: &KeysManager, - bump_tx_event_handler: &BumpTxEventHandler, peer_manager: Arc, +fn handle_ldk_events<'a>( + channel_manager: Arc, bitcoind_client: &'a BitcoindClient, + network_graph: &'a NetworkGraph, keys_manager: &'a KeysManager, + bump_tx_event_handler: &'a BumpTxEventHandler, peer_manager: Arc, inbound_payments: Arc>, outbound_payments: Arc>, fs_store: Arc, output_sweeper: OutputSweeperWrapper, network: Network, event: Event, -) { - match event { - Event::FundingGenerationReady { - temporary_channel_id, - counterparty_node_id, - channel_value_satoshis, - output_script, - .. - } => { - // Construct the raw transaction with one output, that is paid the amount of the - // channel. - let addr = WitnessProgram::from_scriptpubkey( - &output_script.as_bytes(), - match network { - Network::Bitcoin => bitcoin_bech32::constants::Network::Bitcoin, - Network::Regtest => bitcoin_bech32::constants::Network::Regtest, - Network::Signet => bitcoin_bech32::constants::Network::Signet, - Network::Testnet | _ => bitcoin_bech32::constants::Network::Testnet, - }, - ) - .expect("Lightning funding tx should always be to a SegWit output") - .to_address(); - let mut outputs = vec![StdHashMap::new()]; - outputs[0].insert(addr, channel_value_satoshis as f64 / 100_000_000.0); - let raw_tx = bitcoind_client.create_raw_transaction(outputs).await; - - // Have your wallet put the inputs into the transaction such that the output is - // satisfied. - let funded_tx = bitcoind_client.fund_raw_transaction(raw_tx).await; - - // Sign the final funding transaction and give it to LDK, who will eventually broadcast it. - let signed_tx = bitcoind_client.sign_raw_transaction_with_wallet(funded_tx.hex).await; - assert_eq!(signed_tx.complete, true); - let final_tx: Transaction = - encode::deserialize(&hex_utils::to_vec(&signed_tx.hex).unwrap()).unwrap(); - // Give the funding transaction back to LDK for opening the channel. - if channel_manager - .funding_transaction_generated(temporary_channel_id, counterparty_node_id, final_tx) - .is_err() - { - println!( - "\nERROR: Channel went away before we could fund it. The peer disconnected or refused the channel."); - print!("> "); - std::io::stdout().flush().unwrap(); - } - }, - Event::FundingTxBroadcastSafe { .. } => { - // We don't use the manual broadcasting feature, so this event should never be seen. - }, - Event::PaymentClaimable { payment_hash, purpose, amount_msat, .. } => { - println!( - "\nEVENT: received payment from payment hash {} of {} millisatoshis", - payment_hash, amount_msat, - ); - print!("> "); - std::io::stdout().flush().unwrap(); - let payment_preimage = match purpose { - PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => payment_preimage, - PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => payment_preimage, - PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => payment_preimage, - PaymentPurpose::SpontaneousPayment(preimage) => Some(preimage), - }; - channel_manager.claim_funds(payment_preimage.unwrap()); - }, - Event::PaymentClaimed { payment_hash, purpose, amount_msat, .. } => { - println!( - "\nEVENT: claimed payment from payment hash {} of {} millisatoshis", - payment_hash, amount_msat, - ); - print!("> "); - std::io::stdout().flush().unwrap(); - let (payment_preimage, payment_secret) = match purpose { - PaymentPurpose::Bolt11InvoicePayment { - payment_preimage, payment_secret, .. - } => (payment_preimage, Some(payment_secret)), - PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => { - (payment_preimage, Some(payment_secret)) - }, - PaymentPurpose::Bolt12RefundPayment { - payment_preimage, payment_secret, .. - } => (payment_preimage, Some(payment_secret)), - PaymentPurpose::SpontaneousPayment(preimage) => (Some(preimage), None), - }; - let mut inbound = inbound_payments.lock().unwrap(); - match inbound.payments.entry(payment_hash) { - Entry::Occupied(mut e) => { - let payment = e.get_mut(); - payment.status = HTLCStatus::Succeeded; - payment.preimage = payment_preimage; - payment.secret = payment_secret; - }, - Entry::Vacant(e) => { - e.insert(PaymentInfo { - preimage: payment_preimage, - secret: payment_secret, - status: HTLCStatus::Succeeded, - amt_msat: MillisatAmount(Some(amount_msat)), - }); - }, - } - fs_store.write("", "", INBOUND_PAYMENTS_FNAME, &inbound.encode()).unwrap(); - }, - Event::PaymentSent { - payment_preimage, payment_hash, fee_paid_msat, payment_id, .. - } => { - let mut outbound = outbound_payments.lock().unwrap(); - for (id, payment) in outbound.payments.iter_mut() { - if *id == payment_id.unwrap() { - payment.preimage = Some(payment_preimage); - payment.status = HTLCStatus::Succeeded; +) -> impl core::future::Future + 'a { + async move { + match event { + Event::FundingGenerationReady { + temporary_channel_id, + counterparty_node_id, + channel_value_satoshis, + output_script, + .. + } => { + // Construct the raw transaction with one output, that is paid the amount of the + // channel. + let addr = WitnessProgram::from_scriptpubkey( + &output_script.as_bytes(), + match network { + Network::Bitcoin => bitcoin_bech32::constants::Network::Bitcoin, + Network::Regtest => bitcoin_bech32::constants::Network::Regtest, + Network::Signet => bitcoin_bech32::constants::Network::Signet, + Network::Testnet | _ => bitcoin_bech32::constants::Network::Testnet, + }, + ) + .expect("Lightning funding tx should always be to a SegWit output") + .to_address(); + let mut outputs = vec![StdHashMap::new()]; + outputs[0].insert(addr, channel_value_satoshis as f64 / 100_000_000.0); + let raw_tx = bitcoind_client.create_raw_transaction(outputs).await; + + // Have your wallet put the inputs into the transaction such that the output is + // satisfied. + let funded_tx = bitcoind_client.fund_raw_transaction(raw_tx).await; + + // Sign the final funding transaction and give it to LDK, who will eventually broadcast it. + let signed_tx = bitcoind_client.sign_raw_transaction_with_wallet(funded_tx.hex).await; + assert_eq!(signed_tx.complete, true); + let final_tx: Transaction = + encode::deserialize(&hex_utils::to_vec(&signed_tx.hex).unwrap()).unwrap(); + // Give the funding transaction back to LDK for opening the channel. + if channel_manager + .funding_transaction_generated(temporary_channel_id, counterparty_node_id, final_tx) + .is_err() + { println!( - "\nEVENT: successfully sent payment of {} millisatoshis{} from \ - payment hash {} with preimage {}", - payment.amt_msat, - if let Some(fee) = fee_paid_msat { - format!(" (fee {} msat)", fee) - } else { - "".to_string() - }, - payment_hash, - payment_preimage - ); + "\nERROR: Channel went away before we could fund it. The peer disconnected or refused the channel."); print!("> "); std::io::stdout().flush().unwrap(); } - } - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound.encode()).unwrap(); - }, - Event::OpenChannelRequest { - ref temporary_channel_id, ref counterparty_node_id, .. - } => { - let mut random_bytes = [0u8; 16]; - random_bytes.copy_from_slice(&keys_manager.get_secure_random_bytes()[..16]); - let user_channel_id = u128::from_be_bytes(random_bytes); - let res = channel_manager.accept_inbound_channel( - temporary_channel_id, - counterparty_node_id, - user_channel_id, - ); - - if let Err(e) = res { - print!( - "\nEVENT: Failed to accept inbound channel ({}) from {}: {:?}", - temporary_channel_id, - hex_utils::hex_str(&counterparty_node_id.serialize()), - e, - ); - } else { - print!( - "\nEVENT: Accepted inbound channel ({}) from {}", - temporary_channel_id, - hex_utils::hex_str(&counterparty_node_id.serialize()), + }, + Event::FundingTxBroadcastSafe { .. } => { + // We don't use the manual broadcasting feature, so this event should never be seen. + }, + Event::PaymentClaimable { payment_hash, purpose, amount_msat, .. } => { + println!( + "\nEVENT: received payment from payment hash {} of {} millisatoshis", + payment_hash, amount_msat, ); - } - print!("> "); - std::io::stdout().flush().unwrap(); - }, - Event::PaymentPathSuccessful { .. } => {}, - Event::PaymentPathFailed { .. } => {}, - Event::ProbeSuccessful { .. } => {}, - Event::ProbeFailed { .. } => {}, - Event::PaymentFailed { payment_hash, reason, payment_id, .. } => { - if let Some(hash) = payment_hash { - print!( - "\nEVENT: Failed to send payment to payment ID {}, payment hash {}: {:?}", - payment_id, - hash, - if let Some(r) = reason { r } else { PaymentFailureReason::RetriesExhausted } + print!("> "); + std::io::stdout().flush().unwrap(); + let payment_preimage = match purpose { + PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => payment_preimage, + PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => payment_preimage, + PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => payment_preimage, + PaymentPurpose::SpontaneousPayment(preimage) => Some(preimage), + }; + channel_manager.claim_funds(payment_preimage.unwrap()); + }, + Event::PaymentClaimed { payment_hash, purpose, amount_msat, .. } => { + println!( + "\nEVENT: claimed payment from payment hash {} of {} millisatoshis", + payment_hash, amount_msat, ); - } else { - print!( - "\nEVENT: Failed fetch invoice for payment ID {}: {:?}", - payment_id, - if let Some(r) = reason { r } else { PaymentFailureReason::RetriesExhausted } + print!("> "); + std::io::stdout().flush().unwrap(); + let (payment_preimage, payment_secret) = match purpose { + PaymentPurpose::Bolt11InvoicePayment { + payment_preimage, payment_secret, .. + } => (payment_preimage, Some(payment_secret)), + PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => { + (payment_preimage, Some(payment_secret)) + }, + PaymentPurpose::Bolt12RefundPayment { + payment_preimage, payment_secret, .. + } => (payment_preimage, Some(payment_secret)), + PaymentPurpose::SpontaneousPayment(preimage) => (Some(preimage), None), + }; + let mut inbound = inbound_payments.lock().unwrap(); + match inbound.payments.entry(payment_hash) { + Entry::Occupied(mut e) => { + let payment = e.get_mut(); + payment.status = HTLCStatus::Succeeded; + payment.preimage = payment_preimage; + payment.secret = payment_secret; + }, + Entry::Vacant(e) => { + e.insert(PaymentInfo { + preimage: payment_preimage, + secret: payment_secret, + status: HTLCStatus::Succeeded, + amt_msat: MillisatAmount(Some(amount_msat)), + }); + }, + } + fs_store.write("", "", INBOUND_PAYMENTS_FNAME, inbound.encode()).unwrap(); + }, + Event::PaymentSent { + payment_preimage, payment_hash, fee_paid_msat, payment_id, .. + } => { + let mut outbound = outbound_payments.lock().unwrap(); + for (id, payment) in outbound.payments.iter_mut() { + if *id == payment_id.unwrap() { + payment.preimage = Some(payment_preimage); + payment.status = HTLCStatus::Succeeded; + println!( + "\nEVENT: successfully sent payment of {} millisatoshis{} from \ + payment hash {} with preimage {}", + payment.amt_msat, + if let Some(fee) = fee_paid_msat { + format!(" (fee {} msat)", fee) + } else { + "".to_string() + }, + payment_hash, + payment_preimage + ); + print!("> "); + std::io::stdout().flush().unwrap(); + } + } + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound.encode()).unwrap(); + }, + Event::OpenChannelRequest { + ref temporary_channel_id, ref counterparty_node_id, .. + } => { + let mut random_bytes = [0u8; 16]; + random_bytes.copy_from_slice(&keys_manager.get_secure_random_bytes()[..16]); + let user_channel_id = u128::from_be_bytes(random_bytes); + let res = channel_manager.accept_inbound_channel( + temporary_channel_id, + counterparty_node_id, + user_channel_id, + None, ); - } - print!("> "); - std::io::stdout().flush().unwrap(); - let mut outbound = outbound_payments.lock().unwrap(); - if outbound.payments.contains_key(&payment_id) { - let payment = outbound.payments.get_mut(&payment_id).unwrap(); - payment.status = HTLCStatus::Failed; - } - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound.encode()).unwrap(); - }, - Event::InvoiceReceived { .. } => { - // We don't use the manual invoice payment logic, so this event should never be seen. - }, - Event::PaymentForwarded { - prev_channel_id, - next_channel_id, - total_fee_earned_msat, - claim_from_onchain_tx, - outbound_amount_forwarded_msat, - .. - } => { - let read_only_network_graph = network_graph.read_only(); - let nodes = read_only_network_graph.nodes(); - let channels = channel_manager.list_channels(); - - let node_str = |channel_id: &Option| match channel_id { - None => String::new(), - Some(channel_id) => match channels.iter().find(|c| c.channel_id == *channel_id) { + if let Err(e) = res { + print!( + "\nEVENT: Failed to accept inbound channel ({}) from {}: {:?}", + temporary_channel_id, + hex_utils::hex_str(&counterparty_node_id.serialize()), + e, + ); + } else { + print!( + "\nEVENT: Accepted inbound channel ({}) from {}", + temporary_channel_id, + hex_utils::hex_str(&counterparty_node_id.serialize()), + ); + } + print!("> "); + std::io::stdout().flush().unwrap(); + }, + Event::PaymentPathSuccessful { .. } => {}, + Event::PaymentPathFailed { .. } => {}, + Event::ProbeSuccessful { .. } => {}, + Event::ProbeFailed { .. } => {}, + Event::PaymentFailed { payment_hash, reason, payment_id, .. } => { + if let Some(hash) = payment_hash { + print!( + "\nEVENT: Failed to send payment to payment ID {}, payment hash {}: {:?}", + payment_id, + hash, + if let Some(r) = reason { r } else { PaymentFailureReason::RetriesExhausted } + ); + } else { + print!( + "\nEVENT: Failed fetch invoice for payment ID {}: {:?}", + payment_id, + if let Some(r) = reason { r } else { PaymentFailureReason::RetriesExhausted } + ); + } + print!("> "); + std::io::stdout().flush().unwrap(); + + let mut outbound = outbound_payments.lock().unwrap(); + if outbound.payments.contains_key(&payment_id) { + let payment = outbound.payments.get_mut(&payment_id).unwrap(); + payment.status = HTLCStatus::Failed; + } + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound.encode()).unwrap(); + }, + Event::InvoiceReceived { .. } => { + // We don't use the manual invoice payment logic, so this event should never be seen. + }, + Event::PaymentForwarded { + prev_channel_id, + next_channel_id, + total_fee_earned_msat, + claim_from_onchain_tx, + outbound_amount_forwarded_msat, + .. + } => { + let read_only_network_graph = network_graph.read_only(); + let nodes = read_only_network_graph.nodes(); + let channels = channel_manager.list_channels(); + + let node_str = |channel_id: &Option| match channel_id { None => String::new(), - Some(channel) => { - match nodes.get(&NodeId::from_pubkey(&channel.counterparty.node_id)) { - None => "private node".to_string(), - Some(node) => match &node.announcement_info { - None => "unnamed node".to_string(), - Some(announcement) => { - format!("node {}", announcement.alias()) + Some(channel_id) => match channels.iter().find(|c| c.channel_id == *channel_id) { + None => String::new(), + Some(channel) => { + match nodes.get(&NodeId::from_pubkey(&channel.counterparty.node_id)) { + None => "private node".to_string(), + Some(node) => match &node.announcement_info { + None => "unnamed node".to_string(), + Some(announcement) => { + format!("node {}", announcement.alias()) + }, }, - }, - } + } + }, }, - }, - }; - let channel_str = |channel_id: &Option| { - channel_id - .map(|channel_id| format!(" with channel {}", channel_id)) - .unwrap_or_default() - }; - let from_prev_str = - format!(" from {}{}", node_str(&prev_channel_id), channel_str(&prev_channel_id)); - let to_next_str = - format!(" to {}{}", node_str(&next_channel_id), channel_str(&next_channel_id)); - - let from_onchain_str = if claim_from_onchain_tx { - "from onchain downstream claim" - } else { - "from HTLC fulfill message" - }; - let amt_args = if let Some(v) = outbound_amount_forwarded_msat { - format!("{}", v) - } else { - "?".to_string() - }; - if let Some(fee_earned) = total_fee_earned_msat { + }; + let channel_str = |channel_id: &Option| { + channel_id + .map(|channel_id| format!(" with channel {}", channel_id)) + .unwrap_or_default() + }; + let from_prev_str = + format!(" from {}{}", node_str(&prev_channel_id), channel_str(&prev_channel_id)); + let to_next_str = + format!(" to {}{}", node_str(&next_channel_id), channel_str(&next_channel_id)); + + let from_onchain_str = if claim_from_onchain_tx { + "from onchain downstream claim" + } else { + "from HTLC fulfill message" + }; + let amt_args = if let Some(v) = outbound_amount_forwarded_msat { + format!("{}", v) + } else { + "?".to_string() + }; + if let Some(fee_earned) = total_fee_earned_msat { + println!( + "\nEVENT: Forwarded payment for {} msat{}{}, earning {} msat {}", + amt_args, from_prev_str, to_next_str, fee_earned, from_onchain_str + ); + } else { + println!( + "\nEVENT: Forwarded payment for {} msat{}{}, claiming onchain {}", + amt_args, from_prev_str, to_next_str, from_onchain_str + ); + } + print!("> "); + std::io::stdout().flush().unwrap(); + }, + Event::HTLCHandlingFailed { .. } => {}, + Event::SpendableOutputs { outputs, channel_id } => { + output_sweeper.0.track_spendable_outputs(outputs, channel_id, false, None).unwrap(); + }, + Event::ChannelPending { channel_id, counterparty_node_id, .. } => { println!( - "\nEVENT: Forwarded payment for {} msat{}{}, earning {} msat {}", - amt_args, from_prev_str, to_next_str, fee_earned, from_onchain_str + "\nEVENT: Channel {} with peer {} is pending awaiting funding lock-in!", + channel_id, + hex_utils::hex_str(&counterparty_node_id.serialize()), ); - } else { + print!("> "); + std::io::stdout().flush().unwrap(); + }, + Event::ChannelReady { + ref channel_id, + ref counterparty_node_id, + .. + } => { println!( - "\nEVENT: Forwarded payment for {} msat{}{}, claiming onchain {}", - amt_args, from_prev_str, to_next_str, from_onchain_str + "\nEVENT: Channel {} with peer {} is ready to be used!", + channel_id, + hex_utils::hex_str(&counterparty_node_id.serialize()), ); - } - print!("> "); - std::io::stdout().flush().unwrap(); - }, - Event::HTLCHandlingFailed { .. } => {}, - Event::PendingHTLCsForwardable { time_forwardable } => { - let forwarding_channel_manager = channel_manager.clone(); - let min = time_forwardable.as_millis() as u64; - tokio::spawn(async move { - let millis_to_sleep = thread_rng().gen_range(min, min * 5) as u64; - tokio::time::sleep(Duration::from_millis(millis_to_sleep)).await; - forwarding_channel_manager.process_pending_htlc_forwards(); - }); - }, - Event::SpendableOutputs { outputs, channel_id } => { - output_sweeper.0.track_spendable_outputs(outputs, channel_id, false, None).unwrap(); - }, - Event::ChannelPending { channel_id, counterparty_node_id, .. } => { - println!( - "\nEVENT: Channel {} with peer {} is pending awaiting funding lock-in!", - channel_id, - hex_utils::hex_str(&counterparty_node_id.serialize()), - ); - print!("> "); - std::io::stdout().flush().unwrap(); - }, - Event::ChannelReady { - ref channel_id, - user_channel_id: _, - ref counterparty_node_id, - channel_type: _, - } => { - println!( - "\nEVENT: Channel {} with peer {} is ready to be used!", - channel_id, - hex_utils::hex_str(&counterparty_node_id.serialize()), - ); - print!("> "); - std::io::stdout().flush().unwrap(); - }, - Event::ChannelClosed { channel_id, reason, counterparty_node_id, .. } => { - println!( - "\nEVENT: Channel {} with counterparty {} closed due to: {:?}", - channel_id, - counterparty_node_id.map(|id| format!("{}", id)).unwrap_or("".to_owned()), - reason - ); - print!("> "); - std::io::stdout().flush().unwrap(); - }, - Event::DiscardFunding { .. } => { - // A "real" node should probably "lock" the UTXOs spent in funding transactions until - // the funding transaction either confirms, or this event is generated. - }, - Event::HTLCIntercepted { .. } => {}, - Event::OnionMessageIntercepted { .. } => { - // We don't use the onion message interception feature, so this event should never be - // seen. - }, - Event::OnionMessagePeerConnected { .. } => { - // We don't use the onion message interception feature, so we have no use for this - // event. - }, - Event::BumpTransaction(event) => bump_tx_event_handler.handle_event(&event), - Event::ConnectionNeeded { node_id, addresses } => { - tokio::spawn(async move { - for address in addresses { - if let Ok(sockaddrs) = address.to_socket_addrs() { - for addr in sockaddrs { - let pm = Arc::clone(&peer_manager); - if cli::connect_peer_if_necessary(node_id, addr, pm).await.is_ok() { - return; + print!("> "); + std::io::stdout().flush().unwrap(); + }, + Event::ChannelClosed { channel_id, reason, counterparty_node_id, .. } => { + println!( + "\nEVENT: Channel {} with counterparty {} closed due to: {:?}", + channel_id, + counterparty_node_id.map(|id| format!("{}", id)).unwrap_or("".to_owned()), + reason + ); + print!("> "); + std::io::stdout().flush().unwrap(); + }, + Event::DiscardFunding { .. } => { + // A "real" node should probably "lock" the UTXOs spent in funding transactions until + // the funding transaction either confirms, or this event is generated. + }, + Event::HTLCIntercepted { .. } => {}, + Event::OnionMessageIntercepted { .. } => { + // We don't use the onion message interception feature, so this event should never be + // seen. + }, + Event::OnionMessagePeerConnected { .. } => { + // We don't use the onion message interception feature, so we have no use for this + // event. + }, + Event::BumpTransaction(event) => bump_tx_event_handler.handle_event(&event), + Event::ConnectionNeeded { node_id, addresses } => { + tokio::spawn(async move { + for address in addresses { + if let Ok(sockaddrs) = address.to_socket_addrs() { + for addr in sockaddrs { + let pm = Arc::clone(&peer_manager); + if cli::connect_peer_if_necessary(node_id, addr, pm).await.is_ok() { + return; + } } } } - } - }); - }, + }); + }, + _ => {}, + } } } @@ -635,9 +633,9 @@ async fn start_ldk() { let cur = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let keys_manager = Arc::new(KeysManager::new(&keys_seed, cur.as_secs(), cur.subsec_nanos())); - let bump_tx_event_handler = Arc::new(BumpTransactionEventHandler::new( + let bump_tx_event_handler = Arc::new(BumpTransactionEventHandlerSync::new( Arc::clone(&broadcaster), - Arc::new(Wallet::new(Arc::clone(&bitcoind_client), Arc::clone(&logger))), + Arc::new(WalletSync::new(Arc::clone(&bitcoind_client), Arc::clone(&logger))), Arc::clone(&keys_manager), Arc::clone(&logger), )); @@ -664,6 +662,8 @@ async fn start_ldk() { Arc::clone(&logger), Arc::clone(&fee_estimator), Arc::clone(&persister), + Arc::clone(&keys_manager), + keys_manager.get_peer_storage_key(), )); // Step 7: Read ChannelMonitor state from disk @@ -800,11 +800,11 @@ async fn start_ldk() { ]; for (blockhash, channel_monitor) in channelmonitors.drain(..) { - let outpoint = channel_monitor.get_funding_txo().0; + let funding_txo = channel_monitor.get_funding_txo(); chain_listener_channel_monitors.push(( blockhash, (channel_monitor, broadcaster.clone(), fee_estimator.clone(), logger.clone()), - outpoint, + funding_txo, )); } @@ -828,11 +828,10 @@ async fn start_ldk() { }; // Step 14: Give ChannelMonitors to ChainMonitor - for item in chain_listener_channel_monitors.drain(..) { - let channel_monitor = item.1 .0; - let funding_outpoint = item.2; + for (_, (channel_monitor, _, _, _), _) in chain_listener_channel_monitors { + let channel_id = channel_monitor.channel_id(); assert_eq!( - chain_monitor.watch_channel(funding_outpoint, channel_monitor), + chain_monitor.watch_channel(channel_id, channel_monitor), Ok(ChannelMonitorUpdateStatus::Completed) ); } @@ -867,10 +866,11 @@ async fn start_ldk() { let current_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); rand::thread_rng().fill_bytes(&mut ephemeral_bytes); let lightning_msg_handler = MessageHandler { - chan_handler: channel_manager.clone(), - route_handler: gossip_sync.clone(), - onion_message_handler: onion_messenger.clone(), + chan_handler: Arc::clone(&channel_manager), + route_handler: Arc::clone(&gossip_sync), + onion_message_handler: Arc::clone(&onion_messenger), custom_message_handler: IgnoringMessageHandler {}, + send_only_message_handler: Arc::clone(&chain_monitor), }; let peer_manager: Arc = Arc::new(PeerManager::new( lightning_msg_handler, @@ -962,7 +962,7 @@ async fn start_ldk() { } } fs_store - .write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.lock().unwrap().encode()) + .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.lock().unwrap().encode()) .unwrap(); // Step 20: Handle LDK Events @@ -1012,16 +1012,18 @@ async fn start_ldk() { // Step 22: Background Processing let (bp_exit, bp_exit_check) = tokio::sync::watch::channel(()); - let mut background_processor = tokio::spawn(process_events_async( + let mut background_processor = tokio::spawn(process_events_async_with_kv_store_sync( Arc::clone(&persister), event_handler, - chain_monitor.clone(), - channel_manager.clone(), + Arc::clone(&chain_monitor), + Arc::clone(&channel_manager), Some(onion_messenger), - GossipSync::p2p(gossip_sync.clone()), - peer_manager.clone(), - logger.clone(), - Some(scorer.clone()), + GossipSync::p2p(Arc::clone(&gossip_sync)), + Arc::clone(&peer_manager), + NO_LIQUIDITY_MANAGER, + Some(Arc::clone(&output_sweeper)), + Arc::clone(&logger), + Some(Arc::clone(&scorer)), move |t| { let mut bp_exit_fut_check = bp_exit_check.clone(); Box::pin(async move { @@ -1146,7 +1148,7 @@ async fn start_ldk() { persist::CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, persist::CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE, persist::CHANNEL_MANAGER_PERSISTENCE_KEY, - &channel_manager.encode(), + channel_manager.encode(), ) .unwrap(); use lightning::util::logger::Logger; diff --git a/src/sweep.rs b/src/sweep.rs index 7ab3b824..b3423ca3 100644 --- a/src/sweep.rs +++ b/src/sweep.rs @@ -5,7 +5,7 @@ use std::{fs, io}; use lightning::sign::{EntropySource, KeysManager, SpendableOutputDescriptor}; use lightning::util::logger::Logger; -use lightning::util::persist::KVStore; +use lightning::util::persist::KVStoreSync; use lightning::util::ser::{Readable, WithoutLength, Writeable}; use lightning_persister::fs_store::FilesystemStore; @@ -62,7 +62,7 @@ pub(crate) async fn migrate_deprecated_spendable_outputs( if !outputs.is_empty() { let key = hex_utils::hex_str(&keys_manager.get_secure_random_bytes()); persister - .write("spendable_outputs", "", &key, &WithoutLength(&outputs).encode()) + .write("spendable_outputs", "", &key, WithoutLength(&outputs).encode()) .unwrap(); fs::remove_dir_all(&processing_spendables_dir).unwrap(); } From 6e2539f86e76fe424de09abb9950868d11b91bbf Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 07:48:12 +0000 Subject: [PATCH 2/9] Switch to async traits for bitcoind and KVStore outside of monitors With LDK 0.2, most traits now offer async variants. In the previous commit we ignored these but here we migrate to them everywhere except in the `ChannelMonitor` persistence. Note that we have to switch to async stdin reading in cli.rs to ensure we always reach an `await` point in a timely manner on all tasks. --- Cargo.toml | 4 +- src/bitcoind_client.rs | 158 +++++++++++++++-------------------------- src/cli.rs | 154 ++++++++++++++++++++++----------------- src/main.rs | 131 ++++++++++++++++++---------------- src/sweep.rs | 5 +- 5 files changed, 224 insertions(+), 228 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4dcbe569..2a788107 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ lightning-block-sync = { version = "0.2.0-beta1", features = [ "rpc-client", "to lightning-dns-resolver = { version = "0.3.0-beta1" } lightning-invoice = { version = "0.34.0-beta1" } lightning-net-tokio = { version = "0.2.0-beta1" } -lightning-persister = { version = "0.2.0-beta1" } +lightning-persister = { version = "0.2.0-beta1", features = [ "tokio" ] } lightning-background-processor = { version = "0.2.0-beta1" } lightning-rapid-gossip-sync = { version = "0.2.0-beta1" } lightning-macros = { version = "0.2.0-beta1" } @@ -27,7 +27,7 @@ libc = "0.2" chrono = { version = "0.4", default-features = false, features = ["clock"] } rand = "0.4" serde_json = { version = "1.0" } -tokio = { version = "1", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] } +tokio = { version = "1", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time", "io-std" ] } [profile.release] panic = "abort" diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs index 80a1e9dc..01e2bed3 100644 --- a/src/bitcoind_client.rs +++ b/src/bitcoind_client.rs @@ -16,10 +16,10 @@ use bitcoin::key::XOnlyPublicKey; use bitcoin::psbt::Psbt; use bitcoin::{Network, OutPoint, TxOut, WPubkeyHash}; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::events::bump_transaction::Utxo; -use lightning::events::bump_transaction::sync::WalletSourceSync; +use lightning::events::bump_transaction::{Utxo, WalletSource}; use lightning::log_error; -use lightning::sign::ChangeDestinationSourceSync; +use lightning::sign::ChangeDestinationSource; +use lightning::util::async_poll::AsyncResult; use lightning::util::logger::Logger; use lightning_block_sync::http::HttpEndpoint; use lightning_block_sync::rpc::RpcClient; @@ -32,7 +32,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; use std::time::Duration; -use tokio::runtime::{self, Runtime}; +use tokio::runtime::Handle; pub struct BitcoindClient { pub(crate) bitcoind_rpc_client: Arc, @@ -42,8 +42,7 @@ pub struct BitcoindClient { rpc_user: String, rpc_password: String, fees: Arc>, - main_runtime_handle: runtime::Handle, - inner_runtime: Arc, + main_runtime_handle: Handle, logger: Arc, } @@ -71,7 +70,7 @@ const MIN_FEERATE: u32 = 253; impl BitcoindClient { pub(crate) async fn new( host: String, port: u16, rpc_user: String, rpc_password: String, network: Network, - handle: runtime::Handle, logger: Arc, + handle: Handle, logger: Arc, ) -> std::io::Result { let http_endpoint = HttpEndpoint::for_host(host.clone()).with_port(port); let rpc_credentials = @@ -100,15 +99,6 @@ impl BitcoindClient { fees.insert(ConfirmationTarget::ChannelCloseMinimum, AtomicU32::new(MIN_FEERATE)); fees.insert(ConfirmationTarget::OutputSpendingFee, AtomicU32::new(MIN_FEERATE)); - let mut builder = runtime::Builder::new_multi_thread(); - let runtime = - builder.enable_all().worker_threads(1).thread_name("rpc-worker").build().unwrap(); - let inner_runtime = Arc::new(runtime); - // Tokio will panic if we drop a runtime while in another runtime. Because the entire - // application runs inside a tokio runtime, we have to ensure this runtime is never - // `drop`'d, which we do by leaking an Arc reference. - std::mem::forget(Arc::clone(&inner_runtime)); - let client = Self { bitcoind_rpc_client: Arc::new(bitcoind_rpc_client), host, @@ -118,7 +108,6 @@ impl BitcoindClient { network, fees: Arc::new(fees), main_runtime_handle: handle.clone(), - inner_runtime, logger, }; BitcoindClient::poll_for_fee_estimates( @@ -131,7 +120,7 @@ impl BitcoindClient { fn poll_for_fee_estimates( fees: Arc>, rpc_client: Arc, - handle: tokio::runtime::Handle, + handle: Handle, ) { handle.spawn(async move { loop { @@ -241,39 +230,6 @@ impl BitcoindClient { }); } - fn run_future_in_blocking_context(&self, future: F) -> F::Output - where - F::Output: Send + 'static, - { - // Tokio deliberately makes it nigh impossible to block on a future in a sync context that - // is running in an async task (which makes it really hard to interact with sync code that - // has callbacks in an async project). - // - // Reading the docs, it *seems* like - // `tokio::task::block_in_place(tokio::runtime::Handle::spawn(future))` should do the - // trick, and 99.999% of the time it does! But tokio has a "non-stealable I/O driver" - if - // the task we're running happens to, by sheer luck, be holding the "I/O driver" when we go - // into a `block_in_place` call, and the inner future requires I/O (which of course it - // does, its a future!), the whole thing will come to a grinding halt as no other thread is - // allowed to poll I/O until the blocked one finishes. - // - // This is, of course, nuts, and an almost trivial performance penalty of occasional - // additional wakeups would solve this, but tokio refuses to do so because any performance - // penalty at all would be too much (tokio issue #4730). - // - // Instead, we have to do a rather insane dance - we have to spawn the `future` we want to - // run on a *different* (threaded) tokio runtime (doing the `block_in_place` dance to avoid - // blocking too many threads on the main runtime). We want to block on that `future` being - // run on the other runtime's threads, but tokio only provides `block_on` to do so, which - // runs the `future` itself on the current thread, panicing if this thread is already a - // part of a tokio runtime (which in this case it is - the main tokio runtime). Thus, we - // have to `spawn` the `future` on the secondary runtime and then `block_on` the resulting - // `JoinHandle` on the main runtime. - tokio::task::block_in_place(move || { - self.main_runtime_handle.block_on(self.inner_runtime.spawn(future)).unwrap() - }) - } - pub fn get_new_rpc_client(&self) -> RpcClient { let http_endpoint = HttpEndpoint::for_host(self.host.clone()).with_port(self.port); let rpc_credentials = base64::encode(format!("{}:{}", self.rpc_user, self.rpc_password)); @@ -406,60 +362,64 @@ impl BroadcasterInterface for BitcoindClient { } } -impl ChangeDestinationSourceSync for BitcoindClient { - fn get_change_destination_script(&self) -> Result { - let future = self.get_new_address(); - Ok(self.run_future_in_blocking_context(async move { future.await.script_pubkey() })) +impl ChangeDestinationSource for BitcoindClient { + fn get_change_destination_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf> { + Box::pin(async move { + Ok(self.get_new_address().await.script_pubkey()) + }) } } -impl WalletSourceSync for BitcoindClient { - fn list_confirmed_utxos(&self) -> Result, ()> { - let future = self.list_unspent(); - let utxos = self.run_future_in_blocking_context(async move { future.await.0 }); - Ok(utxos - .into_iter() - .filter_map(|utxo| { - let outpoint = OutPoint { txid: utxo.txid, vout: utxo.vout }; - let value = bitcoin::Amount::from_sat(utxo.amount); - match utxo.address.witness_program() { - Some(prog) if prog.is_p2wpkh() => { - WPubkeyHash::from_slice(prog.program().as_bytes()) - .map(|wpkh| Utxo::new_v0_p2wpkh(outpoint, value, &wpkh)) - .ok() - }, - Some(prog) if prog.is_p2tr() => { - // TODO: Add `Utxo::new_v1_p2tr` upstream. - XOnlyPublicKey::from_slice(prog.program().as_bytes()) - .map(|_| Utxo { - outpoint, - output: TxOut { - value, - script_pubkey: utxo.address.script_pubkey(), - }, - satisfaction_weight: 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64 + - 1 /* witness items */ + 1 /* schnorr sig len */ + 64, /* schnorr sig */ - }) - .ok() - }, - _ => None, - } - }) - .collect()) +impl WalletSource for BitcoindClient { + fn list_confirmed_utxos<'a>(&'a self) -> AsyncResult<'a, Vec> { + Box::pin(async move { + let utxos = self.list_unspent().await.0; + Ok(utxos + .into_iter() + .filter_map(|utxo| { + let outpoint = OutPoint { txid: utxo.txid, vout: utxo.vout }; + let value = bitcoin::Amount::from_sat(utxo.amount); + match utxo.address.witness_program() { + Some(prog) if prog.is_p2wpkh() => { + WPubkeyHash::from_slice(prog.program().as_bytes()) + .map(|wpkh| Utxo::new_v0_p2wpkh(outpoint, value, &wpkh)) + .ok() + }, + Some(prog) if prog.is_p2tr() => { + // TODO: Add `Utxo::new_v1_p2tr` upstream. + XOnlyPublicKey::from_slice(prog.program().as_bytes()) + .map(|_| Utxo { + outpoint, + output: TxOut { + value, + script_pubkey: utxo.address.script_pubkey(), + }, + satisfaction_weight: 1 /* empty script_sig */ * WITNESS_SCALE_FACTOR as u64 + + 1 /* witness items */ + 1 /* schnorr sig len */ + 64, /* schnorr sig */ + }) + .ok() + }, + _ => None, + } + }) + .collect()) + }) } - fn get_change_script(&self) -> Result { - let future = self.get_new_address(); - Ok(self.run_future_in_blocking_context(async move { future.await.script_pubkey() })) + fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf> { + Box::pin(async move { + Ok(self.get_new_address().await.script_pubkey()) + }) } - fn sign_psbt(&self, tx: Psbt) -> Result { - let mut tx_bytes = Vec::new(); - let _ = tx.unsigned_tx.consensus_encode(&mut tx_bytes).map_err(|_| ()); - let tx_hex = hex_utils::hex_str(&tx_bytes); - let future = self.sign_raw_transaction_with_wallet(tx_hex); - let signed_tx = self.run_future_in_blocking_context(async move { future.await }); - let signed_tx_bytes = hex_utils::to_vec(&signed_tx.hex).ok_or(())?; - Transaction::consensus_decode(&mut signed_tx_bytes.as_slice()).map_err(|_| ()) + fn sign_psbt<'a>(&'a self, tx: Psbt) -> AsyncResult<'a, Transaction> { + Box::pin(async move { + let mut tx_bytes = Vec::new(); + let _ = tx.unsigned_tx.consensus_encode(&mut tx_bytes).map_err(|_| ()); + let tx_hex = hex_utils::hex_str(&tx_bytes); + let signed_tx = self.sign_raw_transaction_with_wallet(tx_hex).await; + let signed_tx_bytes = hex_utils::to_vec(&signed_tx.hex).ok_or(())?; + Transaction::consensus_decode(&mut signed_tx_bytes.as_slice()).map_err(|_| ()) + }) } } diff --git a/src/cli.rs b/src/cli.rs index df0fb2e4..1286ce52 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -22,7 +22,7 @@ use lightning::routing::router::{PaymentParameters, RouteParameters, RouteParame use lightning::sign::{EntropySource, KeysManager}; use lightning::types::payment::{PaymentHash, PaymentPreimage}; use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, UserConfig}; -use lightning::util::persist::KVStoreSync; +use lightning::util::persist::KVStore; use lightning::util::ser::Writeable; use lightning_invoice::Bolt11Invoice; use lightning_persister::fs_store::FilesystemStore; @@ -34,6 +34,8 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; +use tokio::io::{AsyncBufReadExt, BufReader}; + pub(crate) struct LdkUserInfo { pub(crate) bitcoind_rpc_username: String, pub(crate) bitcoind_rpc_password: String, @@ -46,7 +48,7 @@ pub(crate) struct LdkUserInfo { pub(crate) network: Network, } -pub(crate) fn poll_for_user_input( +pub(crate) async fn poll_for_user_input( peer_manager: Arc, channel_manager: Arc, chain_monitor: Arc, keys_manager: Arc, network_graph: Arc, inbound_payments: Arc>, @@ -58,18 +60,20 @@ pub(crate) fn poll_for_user_input( ); println!("LDK logs are available at /.ldk/logs"); println!("Local Node ID is {}.", channel_manager.get_our_node_id()); + + let mut input = BufReader::new(tokio::io::stdin()).lines(); 'read_command: loop { print!("> "); std::io::stdout().flush().unwrap(); // Without flushing, the `>` doesn't print - let mut line = String::new(); - if let Err(e) = std::io::stdin().read_line(&mut line) { - break println!("ERROR: {}", e); - } - - if line.len() == 0 { - // We hit EOF / Ctrl-D - break; - } + let line = match input.next_line().await { + Ok(Some(l)) => l, + Err(e) => { + break println!("ERROR: {}", e); + }, + Ok(None) => { + break println!("ERROR: End of stdin"); + }, + }; let mut words = line.split_whitespace(); if let Some(word) = words.next() { @@ -199,15 +203,17 @@ pub(crate) fn poll_for_user_input( print!("Paying offer for {} msat. Continue (Y/N)? >", amt_msat); std::io::stdout().flush().unwrap(); - if let Err(e) = std::io::stdin().read_line(&mut line) { - println!("ERROR: {}", e); - break 'read_command; - } - - if line.len() == 0 { - // We hit EOF / Ctrl-D - break 'read_command; - } + let line = match input.next_line().await { + Ok(Some(l)) => l, + Err(e) => { + println!("ERROR: {}", e); + break 'read_command; + }, + Ok(None) => { + println!("ERROR: End of stdin"); + break 'read_command; + }, + }; if line.starts_with("Y") { break; @@ -228,6 +234,7 @@ pub(crate) fn poll_for_user_input( ); fs_store .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) + .await .unwrap(); let params = OptionalOfferPaymentParams { @@ -293,6 +300,7 @@ pub(crate) fn poll_for_user_input( ); fs_store .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) + .await .unwrap(); let params = OptionalOfferPaymentParams { @@ -314,9 +322,9 @@ pub(crate) fn poll_for_user_input( &channel_manager, &invoice, user_provided_amt, - &mut outbound_payments.lock().unwrap(), - Arc::clone(&fs_store), - ), + &outbound_payments, + &*fs_store, + ).await, Err(e) => { println!("ERROR: invalid invoice: {:?}", e); }, @@ -356,9 +364,9 @@ pub(crate) fn poll_for_user_input( dest_pubkey, amt_msat, &*keys_manager, - &mut outbound_payments.lock().unwrap(), - Arc::clone(&fs_store), - ); + &outbound_payments, + &*fs_store, + ).await; }, "getoffer" => { let offer_builder = channel_manager.create_offer_builder(); @@ -413,16 +421,18 @@ pub(crate) fn poll_for_user_input( continue; } - let mut inbound_payments = inbound_payments.lock().unwrap(); - get_invoice( - amt_msat.unwrap(), - &mut inbound_payments, - &channel_manager, - expiry_secs.unwrap(), - ); - fs_store - .write("", "", INBOUND_PAYMENTS_FNAME, inbound_payments.encode()) - .unwrap(); + let write_future = { + let mut inbound_payments = inbound_payments.lock().unwrap(); + get_invoice( + amt_msat.unwrap(), + &mut inbound_payments, + &channel_manager, + expiry_secs.unwrap(), + ); + fs_store + .write("", "", INBOUND_PAYMENTS_FNAME, inbound_payments.encode()) + }; + write_future.await.unwrap(); }, "connectpeer" => { let peer_pubkey_and_ip_addr = words.next(); @@ -830,9 +840,9 @@ fn open_channel( } } -fn send_payment( +async fn send_payment( channel_manager: &ChannelManager, invoice: &Bolt11Invoice, required_amount_msat: Option, - outbound_payments: &mut OutboundPaymentInfoStorage, fs_store: Arc, + outbound_payments: &Mutex, fs_store: &FilesystemStore, ) { let payment_id = PaymentId((*invoice.payment_hash()).to_byte_array()); let payment_secret = Some(*invoice.payment_secret()); @@ -850,16 +860,20 @@ fn send_payment( }, _ => {}, } - outbound_payments.payments.insert( - payment_id, - PaymentInfo { - preimage: None, - secret: payment_secret, - status: HTLCStatus::Pending, - amt_msat: MillisatAmount(invoice.amount_milli_satoshis()), - }, - ); - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); + let write_future = { + let mut outbound_payments = outbound_payments.lock().unwrap(); + outbound_payments.payments.insert( + payment_id, + PaymentInfo { + preimage: None, + secret: payment_secret, + status: HTLCStatus::Pending, + amt_msat: MillisatAmount(invoice.amount_milli_satoshis()), + }, + ); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) + }; + write_future.await.unwrap(); match channel_manager.pay_for_bolt11_invoice( invoice, @@ -877,15 +891,19 @@ fn send_payment( Err(e) => { println!("ERROR: failed to send payment: {:?}", e); print!("> "); - outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); + let write_future = { + let mut outbound_payments = outbound_payments.lock().unwrap(); + outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) + }; + write_future.await.unwrap(); }, }; } -fn keysend( +async fn keysend( channel_manager: &ChannelManager, payee_pubkey: PublicKey, amt_msat: u64, entropy_source: &E, - outbound_payments: &mut OutboundPaymentInfoStorage, fs_store: Arc, + outbound_payments: &Mutex, fs_store: &FilesystemStore, ) { let payment_preimage = PaymentPreimage(entropy_source.get_secure_random_bytes()); let payment_id = PaymentId(Sha256::hash(&payment_preimage.0[..]).to_byte_array()); @@ -894,16 +912,20 @@ fn keysend( PaymentParameters::for_keysend(payee_pubkey, 40, false), amt_msat, ); - outbound_payments.payments.insert( - payment_id, - PaymentInfo { - preimage: None, - secret: None, - status: HTLCStatus::Pending, - amt_msat: MillisatAmount(Some(amt_msat)), - }, - ); - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); + let write_future = { + let mut outbound_payments = outbound_payments.lock().unwrap(); + outbound_payments.payments.insert( + payment_id, + PaymentInfo { + preimage: None, + secret: None, + status: HTLCStatus::Pending, + amt_msat: MillisatAmount(Some(amt_msat)), + }, + ); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) + }; + write_future.await.unwrap(); match channel_manager.send_spontaneous_payment( Some(payment_preimage), RecipientOnionFields::spontaneous_empty(), @@ -918,8 +940,12 @@ fn keysend( Err(e) => { println!("ERROR: failed to send payment: {:?}", e); print!("> "); - outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()).unwrap(); + let write_future = { + let mut outbound_payments = outbound_payments.lock().unwrap(); + outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) + }; + write_future.await.unwrap(); }, }; } diff --git a/src/main.rs b/src/main.rs index 0ef56d39..297c77a3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,7 @@ use bitcoin_bech32::WitnessProgram; use disk::{INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME}; use lightning::chain::{chainmonitor, ChannelMonitorUpdateStatus}; use lightning::chain::{BestBlock, Filter, Watch}; -use lightning::events::bump_transaction::sync::{BumpTransactionEventHandlerSync, WalletSync}; +use lightning::events::bump_transaction::{BumpTransactionEventHandler, Wallet}; use lightning::events::{Event, PaymentFailureReason, PaymentPurpose}; use lightning::ln::channelmanager::{self, RecentPaymentDetails}; use lightning::ln::channelmanager::{ @@ -41,15 +41,13 @@ use lightning::util::config::UserConfig; use lightning::util::hash_tables::hash_map::Entry; use lightning::util::hash_tables::HashMap; use lightning::util::persist::{ - self, KVStoreSync, MonitorUpdatingPersister, OUTPUT_SWEEPER_PERSISTENCE_KEY, + self, KVStore, MonitorUpdatingPersister, OUTPUT_SWEEPER_PERSISTENCE_KEY, OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, }; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; use lightning::util::sweep as ldk_sweep; use lightning::{chain, impl_writeable_tlv_based, impl_writeable_tlv_based_enum}; -use lightning_background_processor::{ - process_events_async_with_kv_store_sync, GossipSync, NO_LIQUIDITY_MANAGER, -}; +use lightning_background_processor::{process_events_async, GossipSync, NO_LIQUIDITY_MANAGER}; use lightning_block_sync::init; use lightning_block_sync::poll; use lightning_block_sync::SpvClient; @@ -194,14 +192,14 @@ type OnionMessenger = LdkOnionMessenger< IgnoringMessageHandler, >; -pub(crate) type BumpTxEventHandler = BumpTransactionEventHandlerSync< +pub(crate) type BumpTxEventHandler = BumpTransactionEventHandler< Arc, - Arc, Arc>>, + Arc, Arc>>, Arc, Arc, >; -pub(crate) type OutputSweeper = ldk_sweep::OutputSweeperSync< +pub(crate) type OutputSweeper = ldk_sweep::OutputSweeper< Arc, Arc, Arc, @@ -305,50 +303,56 @@ fn handle_ldk_events<'a>( } => (payment_preimage, Some(payment_secret)), PaymentPurpose::SpontaneousPayment(preimage) => (Some(preimage), None), }; - let mut inbound = inbound_payments.lock().unwrap(); - match inbound.payments.entry(payment_hash) { - Entry::Occupied(mut e) => { - let payment = e.get_mut(); - payment.status = HTLCStatus::Succeeded; - payment.preimage = payment_preimage; - payment.secret = payment_secret; - }, - Entry::Vacant(e) => { - e.insert(PaymentInfo { - preimage: payment_preimage, - secret: payment_secret, - status: HTLCStatus::Succeeded, - amt_msat: MillisatAmount(Some(amount_msat)), - }); - }, - } - fs_store.write("", "", INBOUND_PAYMENTS_FNAME, inbound.encode()).unwrap(); + let write_future = { + let mut inbound = inbound_payments.lock().unwrap(); + match inbound.payments.entry(payment_hash) { + Entry::Occupied(mut e) => { + let payment = e.get_mut(); + payment.status = HTLCStatus::Succeeded; + payment.preimage = payment_preimage; + payment.secret = payment_secret; + }, + Entry::Vacant(e) => { + e.insert(PaymentInfo { + preimage: payment_preimage, + secret: payment_secret, + status: HTLCStatus::Succeeded, + amt_msat: MillisatAmount(Some(amount_msat)), + }); + }, + } + fs_store.write("", "", INBOUND_PAYMENTS_FNAME, inbound.encode()) + }; + write_future.await.unwrap(); }, Event::PaymentSent { payment_preimage, payment_hash, fee_paid_msat, payment_id, .. } => { - let mut outbound = outbound_payments.lock().unwrap(); - for (id, payment) in outbound.payments.iter_mut() { - if *id == payment_id.unwrap() { - payment.preimage = Some(payment_preimage); - payment.status = HTLCStatus::Succeeded; - println!( - "\nEVENT: successfully sent payment of {} millisatoshis{} from \ - payment hash {} with preimage {}", - payment.amt_msat, - if let Some(fee) = fee_paid_msat { - format!(" (fee {} msat)", fee) - } else { - "".to_string() - }, - payment_hash, - payment_preimage - ); - print!("> "); - std::io::stdout().flush().unwrap(); + let write_future = { + let mut outbound = outbound_payments.lock().unwrap(); + for (id, payment) in outbound.payments.iter_mut() { + if *id == payment_id.unwrap() { + payment.preimage = Some(payment_preimage); + payment.status = HTLCStatus::Succeeded; + println!( + "\nEVENT: successfully sent payment of {} millisatoshis{} from \ + payment hash {} with preimage {}", + payment.amt_msat, + if let Some(fee) = fee_paid_msat { + format!(" (fee {} msat)", fee) + } else { + "".to_string() + }, + payment_hash, + payment_preimage + ); + print!("> "); + std::io::stdout().flush().unwrap(); + } } - } - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound.encode()).unwrap(); + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound.encode()) + }; + write_future.await.unwrap(); }, Event::OpenChannelRequest { ref temporary_channel_id, ref counterparty_node_id, .. @@ -402,12 +406,15 @@ fn handle_ldk_events<'a>( print!("> "); std::io::stdout().flush().unwrap(); - let mut outbound = outbound_payments.lock().unwrap(); - if outbound.payments.contains_key(&payment_id) { - let payment = outbound.payments.get_mut(&payment_id).unwrap(); - payment.status = HTLCStatus::Failed; - } - fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound.encode()).unwrap(); + let write_future = { + let mut outbound = outbound_payments.lock().unwrap(); + if outbound.payments.contains_key(&payment_id) { + let payment = outbound.payments.get_mut(&payment_id).unwrap(); + payment.status = HTLCStatus::Failed; + } + fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound.encode()) + }; + write_future.await.unwrap(); }, Event::InvoiceReceived { .. } => { // We don't use the manual invoice payment logic, so this event should never be seen. @@ -477,7 +484,7 @@ fn handle_ldk_events<'a>( }, Event::HTLCHandlingFailed { .. } => {}, Event::SpendableOutputs { outputs, channel_id } => { - output_sweeper.0.track_spendable_outputs(outputs, channel_id, false, None).unwrap(); + output_sweeper.0.track_spendable_outputs(outputs, channel_id, false, None).await.unwrap(); }, Event::ChannelPending { channel_id, counterparty_node_id, .. } => { println!( @@ -524,7 +531,7 @@ fn handle_ldk_events<'a>( // We don't use the onion message interception feature, so we have no use for this // event. }, - Event::BumpTransaction(event) => bump_tx_event_handler.handle_event(&event), + Event::BumpTransaction(event) => bump_tx_event_handler.handle_event(&event).await, Event::ConnectionNeeded { node_id, addresses } => { tokio::spawn(async move { for address in addresses { @@ -633,9 +640,9 @@ async fn start_ldk() { let cur = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap(); let keys_manager = Arc::new(KeysManager::new(&keys_seed, cur.as_secs(), cur.subsec_nanos())); - let bump_tx_event_handler = Arc::new(BumpTransactionEventHandlerSync::new( + let bump_tx_event_handler = Arc::new(BumpTransactionEventHandler::new( Arc::clone(&broadcaster), - Arc::new(WalletSync::new(Arc::clone(&bitcoind_client), Arc::clone(&logger))), + Arc::new(Wallet::new(Arc::clone(&bitcoind_client), Arc::clone(&logger))), Arc::clone(&keys_manager), Arc::clone(&logger), )); @@ -759,7 +766,7 @@ async fn start_ldk() { OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_KEY, - ) { + ).await { Err(e) if e.kind() == io::ErrorKind::NotFound => { let sweeper = OutputSweeper::new( channel_manager.current_best_block(), @@ -963,6 +970,7 @@ async fn start_ldk() { } fs_store .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.lock().unwrap().encode()) + .await .unwrap(); // Step 20: Handle LDK Events @@ -1012,7 +1020,7 @@ async fn start_ldk() { // Step 22: Background Processing let (bp_exit, bp_exit_check) = tokio::sync::watch::channel(()); - let mut background_processor = tokio::spawn(process_events_async_with_kv_store_sync( + let mut background_processor = tokio::spawn(process_events_async( Arc::clone(&persister), event_handler, Arc::clone(&chain_monitor), @@ -1113,7 +1121,7 @@ async fn start_ldk() { let cli_chain_monitor = Arc::clone(&chain_monitor); let cli_persister = Arc::clone(&persister); let cli_peer_manager = Arc::clone(&peer_manager); - let cli_poll = tokio::task::spawn_blocking(move || { + let cli_poll = tokio::task::spawn( cli::poll_for_user_input( cli_peer_manager, cli_channel_manager, @@ -1125,7 +1133,7 @@ async fn start_ldk() { ldk_data_dir, cli_persister, ) - }); + ); // Exit if either CLI polling exits or the background processor exits (which shouldn't happen // unless we fail to write to the filesystem). @@ -1150,6 +1158,7 @@ async fn start_ldk() { persist::CHANNEL_MANAGER_PERSISTENCE_KEY, channel_manager.encode(), ) + .await .unwrap(); use lightning::util::logger::Logger; lightning::log_error!( diff --git a/src/sweep.rs b/src/sweep.rs index b3423ca3..4dd21ee4 100644 --- a/src/sweep.rs +++ b/src/sweep.rs @@ -5,7 +5,7 @@ use std::{fs, io}; use lightning::sign::{EntropySource, KeysManager, SpendableOutputDescriptor}; use lightning::util::logger::Logger; -use lightning::util::persist::KVStoreSync; +use lightning::util::persist::KVStore; use lightning::util::ser::{Readable, WithoutLength, Writeable}; use lightning_persister::fs_store::FilesystemStore; @@ -63,6 +63,7 @@ pub(crate) async fn migrate_deprecated_spendable_outputs( let key = hex_utils::hex_str(&keys_manager.get_secure_random_bytes()); persister .write("spendable_outputs", "", &key, WithoutLength(&outputs).encode()) + .await .unwrap(); fs::remove_dir_all(&processing_spendables_dir).unwrap(); } @@ -90,7 +91,7 @@ pub(crate) async fn migrate_deprecated_spendable_outputs( } let spend_delay = Some(best_block.height + 2); - sweeper.track_spendable_outputs(outputs.clone(), None, false, spend_delay).unwrap(); + sweeper.track_spendable_outputs(outputs.clone(), None, false, spend_delay).await.unwrap(); fs::remove_dir_all(&spendables_dir).unwrap(); fs::remove_dir_all(&pending_spendables_dir).unwrap(); From 74949502e18eb63658a48e849ba67d420e3940b3 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 30 Sep 2025 09:39:47 +0000 Subject: [PATCH 3/9] Use the new `load_existing_monitor` over `watch_channel` Now that we have `ChainMonitor::load_existing_monitor` we can use it during startup to avoid the excess persistence at startup. --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 297c77a3..e2e1a5f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -838,7 +838,7 @@ async fn start_ldk() { for (_, (channel_monitor, _, _, _), _) in chain_listener_channel_monitors { let channel_id = channel_monitor.channel_id(); assert_eq!( - chain_monitor.watch_channel(channel_id, channel_monitor), + chain_monitor.load_existing_monitor(channel_id, channel_monitor), Ok(ChannelMonitorUpdateStatus::Completed) ); } From fd355dc87203d1e8d7497ae2ad1c8132ce17c2fa Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 07:39:18 +0000 Subject: [PATCH 4/9] Switch to (beta) async persistence of `ChannelMonitor`s --- src/main.rs | 54 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index e2e1a5f2..37c33015 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,8 +40,9 @@ use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::util::config::UserConfig; use lightning::util::hash_tables::hash_map::Entry; use lightning::util::hash_tables::HashMap; +use lightning::util::native_async::FutureSpawner; use lightning::util::persist::{ - self, KVStore, MonitorUpdatingPersister, OUTPUT_SWEEPER_PERSISTENCE_KEY, + self, KVStore, MonitorUpdatingPersisterAsync, OUTPUT_SWEEPER_PERSISTENCE_KEY, OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, }; use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; @@ -61,6 +62,7 @@ use std::convert::TryInto; use std::fmt; use std::fs; use std::fs::File; +use std::future::Future; use std::io::{BufReader, Write}; use std::net::ToSocketAddrs; use std::path::Path; @@ -141,15 +143,14 @@ type ChainMonitor = chainmonitor::ChainMonitor< Arc, Arc, Arc, - Arc< - MonitorUpdatingPersister< - Arc, - Arc, - Arc, - Arc, - Arc, - Arc, - >, + chainmonitor::AsyncPersister< + Arc, + TokioSpawner, + Arc, + Arc, + Arc, + Arc, + Arc, >, Arc, >; @@ -212,6 +213,14 @@ pub(crate) type OutputSweeper = ldk_sweep::OutputSweeper< // Needed due to rust-lang/rust#63033. struct OutputSweeperWrapper(Arc); +// Trivially bridge the LDK FutureSpawner trait to tokio +struct TokioSpawner; +impl FutureSpawner for TokioSpawner{ + fn spawn + Send + 'static>(&self, future: T) { + tokio::spawn(future); + } +} + fn handle_ldk_events<'a>( channel_manager: Arc, bitcoind_client: &'a BitcoindClient, network_graph: &'a NetworkGraph, keys_manager: &'a KeysManager, @@ -649,36 +658,37 @@ async fn start_ldk() { // Step 5: Initialize Persistence let fs_store = Arc::new(FilesystemStore::new(ldk_data_dir.clone().into())); - let persister = Arc::new(MonitorUpdatingPersister::new( + let persister = MonitorUpdatingPersisterAsync::new( Arc::clone(&fs_store), + TokioSpawner, Arc::clone(&logger), 1000, Arc::clone(&keys_manager), Arc::clone(&keys_manager), Arc::clone(&bitcoind_client), Arc::clone(&bitcoind_client), - )); + ); // Alternatively, you can use the `FilesystemStore` as a `Persist` directly, at the cost of // larger `ChannelMonitor` update writes (but no deletion or cleanup): //let persister = Arc::clone(&fs_store); - // Step 6: Initialize the ChainMonitor - let chain_monitor: Arc = Arc::new(chainmonitor::ChainMonitor::new( + // Step 6: Read ChannelMonitor state from disk + let mut channelmonitors = persister.read_all_channel_monitors_with_updates().await.unwrap(); + // If you are using the `FilesystemStore` as a `Persist` directly, use + // `lightning::util::persist::read_channel_monitors` like this: + //read_channel_monitors(Arc::clone(&persister), Arc::clone(&keys_manager), Arc::clone(&keys_manager)).unwrap(); + + // Step 7: Initialize the ChainMonitor + let chain_monitor: Arc = Arc::new(chainmonitor::ChainMonitor::new_async_beta( None, Arc::clone(&broadcaster), Arc::clone(&logger), Arc::clone(&fee_estimator), - Arc::clone(&persister), + persister, Arc::clone(&keys_manager), keys_manager.get_peer_storage_key(), )); - // Step 7: Read ChannelMonitor state from disk - let mut channelmonitors = persister.read_all_channel_monitors_with_updates().unwrap(); - // If you are using the `FilesystemStore` as a `Persist` directly, use - // `lightning::util::persist::read_channel_monitors` like this: - //read_channel_monitors(Arc::clone(&persister), Arc::clone(&keys_manager), Arc::clone(&keys_manager)).unwrap(); - // Step 8: Poll for the best chain tip, which may be used by the channel manager & spv client let polled_chain_tip = init::validate_best_block_header(bitcoind_client.as_ref()) .await @@ -837,6 +847,8 @@ async fn start_ldk() { // Step 14: Give ChannelMonitors to ChainMonitor for (_, (channel_monitor, _, _, _), _) in chain_listener_channel_monitors { let channel_id = channel_monitor.channel_id(); + // Note that this may not return `Completed` for ChannelMonitors which were last written by + // a version of LDK prior to 0.1. assert_eq!( chain_monitor.load_existing_monitor(channel_id, channel_monitor), Ok(ChannelMonitorUpdateStatus::Completed) From 3a0fffecf8d0063d914a00aa4019084a673bc5c0 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 08:07:30 +0000 Subject: [PATCH 5/9] Marginally simplify checking if we're already connected --- src/cli.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 1286ce52..c20ce40f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -755,10 +755,8 @@ fn list_payments( pub(crate) async fn connect_peer_if_necessary( pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc, ) -> Result<(), ()> { - for peer_details in peer_manager.list_peers() { - if peer_details.counterparty_node_id == pubkey { - return Ok(()); - } + if peer_manager.peer_by_node_id(&pubkey).is_some() { + return Ok(()); } let res = do_connect_peer(pubkey, peer_addr, peer_manager).await; if res.is_err() { From 24467de78ddac41617ceed7f4e0a4675388456bf Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 08:10:25 +0000 Subject: [PATCH 6/9] Fix outbound payment amount tracking --- src/cli.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index c20ce40f..b4bbe3a7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -844,7 +844,7 @@ async fn send_payment( ) { let payment_id = PaymentId((*invoice.payment_hash()).to_byte_array()); let payment_secret = Some(*invoice.payment_secret()); - match (invoice.amount_milli_satoshis(), required_amount_msat) { + let amt_msat = match (invoice.amount_milli_satoshis(), required_amount_msat) { // pay_for_bolt11_invoice only validates that the amount we pay is >= the invoice's // required amount, not that its equal (to allow for overpayment). As that is somewhat // surprising, here we check and reject all disagreements in amount. @@ -856,8 +856,14 @@ async fn send_payment( print!("> "); return; }, - _ => {}, - } + (Some(inv_amt), _) => inv_amt, + (_, Some(req_amt)) => req_amt, + (None, None) => { + println!("Need an amount to pay an amountless invoice"); + print!("> "); + return; + }, + }; let write_future = { let mut outbound_payments = outbound_payments.lock().unwrap(); outbound_payments.payments.insert( @@ -866,7 +872,7 @@ async fn send_payment( preimage: None, secret: payment_secret, status: HTLCStatus::Pending, - amt_msat: MillisatAmount(invoice.amount_milli_satoshis()), + amt_msat: MillisatAmount(Some(amt_msat)), }, ); fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) @@ -882,7 +888,6 @@ async fn send_payment( ) { Ok(_) => { let payee_pubkey = invoice.recover_payee_pub_key(); - let amt_msat = invoice.amount_milli_satoshis().unwrap(); println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey); print!("> "); }, From 32578c2ddd610da6f5ea658ee0d66fc64e9b8b9c Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 08:38:46 +0000 Subject: [PATCH 7/9] Stop persisting peer addresses to disk and use the public gossip db Its always been a bit strange that we persist the addresses of our peers to disk when there's a public gossip db, its not really a thing most LN nodes do. It might be useful for private channels but there's not a lot of reason to have it in `ldk-sample`. --- src/cli.rs | 21 ++++---------------- src/disk.rs | 32 +++--------------------------- src/main.rs | 56 ++++++++++++++++++++++++++++++----------------------- 3 files changed, 39 insertions(+), 70 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index b4bbe3a7..7a671c3b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,4 @@ -use crate::disk::{self, INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME}; +use crate::disk::{INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME}; use crate::hex_utils; use crate::{ ChainMonitor, ChannelManager, HTLCStatus, InboundPaymentInfoStorage, MillisatAmount, @@ -29,7 +29,6 @@ use lightning_persister::fs_store::FilesystemStore; use std::env; use std::io::Write; use std::net::{SocketAddr, ToSocketAddrs}; -use std::path::Path; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -52,8 +51,7 @@ pub(crate) async fn poll_for_user_input( peer_manager: Arc, channel_manager: Arc, chain_monitor: Arc, keys_manager: Arc, network_graph: Arc, inbound_payments: Arc>, - outbound_payments: Arc>, ldk_data_dir: String, - fs_store: Arc, + outbound_payments: Arc>, fs_store: Arc, ) { println!( "LDK startup successful. Enter \"help\" to view available commands. Press Ctrl-D to quit." @@ -144,24 +142,13 @@ pub(crate) async fn poll_for_user_input( } } - if open_channel( + let _ = open_channel( pubkey, chan_amt_sat.unwrap(), announce_channel, with_anchors, channel_manager.clone(), - ) - .is_ok() - { - if peer_addr_str.is_some() { - let peer_data_path = - format!("{}/channel_peer_data", ldk_data_dir.clone()); - let _ = disk::persist_channel_peer( - Path::new(&peer_data_path), - peer_pubkey_and_ip_addr, - ); - } - } + ); }, "sendpayment" => { let invoice_str = words.next(); diff --git a/src/disk.rs b/src/disk.rs index 00f0ea4d..19a77584 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -1,15 +1,13 @@ -use crate::{cli, InboundPaymentInfoStorage, NetworkGraph, OutboundPaymentInfoStorage}; -use bitcoin::secp256k1::PublicKey; +use crate::{InboundPaymentInfoStorage, NetworkGraph, OutboundPaymentInfoStorage}; use bitcoin::Network; use chrono::Utc; use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringDecayParameters}; -use lightning::util::hash_tables::{new_hash_map, HashMap}; +use lightning::util::hash_tables::new_hash_map; use lightning::util::logger::{Level, Logger, Record}; use lightning::util::ser::{Readable, ReadableArgs}; use std::fs; use std::fs::File; -use std::io::{BufRead, BufReader, Write}; -use std::net::SocketAddr; +use std::io::{BufReader, Write}; use std::path::Path; use std::sync::Arc; @@ -54,30 +52,6 @@ impl Logger for FilesystemLogger { .unwrap(); } } -pub(crate) fn persist_channel_peer(path: &Path, peer_info: &str) -> std::io::Result<()> { - let mut file = fs::OpenOptions::new().create(true).append(true).open(path)?; - file.write_all(format!("{}\n", peer_info).as_bytes()) -} - -pub(crate) fn read_channel_peer_data( - path: &Path, -) -> Result, std::io::Error> { - let mut peer_data = new_hash_map(); - if !Path::new(&path).exists() { - return Ok(new_hash_map()); - } - let file = File::open(path)?; - let reader = BufReader::new(file); - for line in reader.lines() { - match cli::parse_peer_info(line.unwrap()) { - Ok((pubkey, socket_addr)) => { - peer_data.insert(pubkey, socket_addr); - }, - Err(e) => return Err(e), - } - } - Ok(peer_data) -} pub(crate) fn read_network( path: &Path, network: Network, logger: Arc, diff --git a/src/main.rs b/src/main.rs index 37c33015..cf5cb113 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1060,37 +1060,46 @@ async fn start_ldk() { // Regularly reconnect to channel peers. let connect_cm = Arc::clone(&channel_manager); let connect_pm = Arc::clone(&peer_manager); - let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir); let stop_connect = Arc::clone(&stop_listen_connect); + let graph_connect = Arc::clone(&network_graph); tokio::spawn(async move { let mut interval = tokio::time::interval(Duration::from_secs(1)); interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); loop { interval.tick().await; - match disk::read_channel_peer_data(Path::new(&peer_data_path)) { - Ok(info) => { - for node_id in connect_cm - .list_channels() - .iter() - .map(|chan| chan.counterparty.node_id) - .filter(|id| connect_pm.peer_by_node_id(id).is_none()) - { - if stop_connect.load(Ordering::Acquire) { - return; - } - for (pubkey, peer_addr) in info.iter() { - if *pubkey == node_id { - let _ = cli::do_connect_peer( - *pubkey, - peer_addr.clone(), - Arc::clone(&connect_pm), - ) - .await; + for node_id in connect_cm + .list_channels() + .iter() + .map(|chan| chan.counterparty.node_id) + .filter(|id| connect_pm.peer_by_node_id(id).is_none()) + { + if stop_connect.load(Ordering::Acquire) { + return; + } + let id = NodeId::from_pubkey(&node_id); + let addrs = if let Some(node) = graph_connect.read_only().node(&id) { + if let Some(ann) = &node.announcement_info { + ann.addresses().iter().filter_map(|addr| { + match addr { + lightning::ln::msgs::SocketAddress::OnionV2(_) => None, + lightning::ln::msgs::SocketAddress::OnionV3 { .. } => None, + _ => Some(addr.clone()), } - } + }).collect::>() + } else { + Vec::new() } - }, - Err(e) => println!("ERROR: errored reading channel peer info from disk: {:?}", e), + } else { + Vec::new() + }; + for addr in addrs { + let sockaddrs = addr.to_socket_addrs(); + if sockaddrs.is_err() { continue; } + for sockaddr in sockaddrs.unwrap() { + let _ = + cli::do_connect_peer(node_id, sockaddr, Arc::clone(&connect_pm)).await; + } + } } } }); @@ -1142,7 +1151,6 @@ async fn start_ldk() { network_graph, inbound_payments, outbound_payments, - ldk_data_dir, cli_persister, ) ); From 06a8e3c7e6aaaa114c70898987cacb84de877259 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 30 Sep 2025 09:38:39 +0000 Subject: [PATCH 8/9] Drop `tokio` blocking now that `poll_for_user_input` is async --- src/cli.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 7a671c3b..e1affd55 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -111,12 +111,12 @@ pub(crate) async fn poll_for_user_input( }, }; - if tokio::runtime::Handle::current() - .block_on(connect_peer_if_necessary( - pubkey, - peer_addr, - peer_manager.clone(), - )) + if connect_peer_if_necessary( + pubkey, + peer_addr, + peer_manager.clone(), + ) + .await .is_err() { continue; @@ -435,12 +435,12 @@ pub(crate) async fn poll_for_user_input( continue; }, }; - if tokio::runtime::Handle::current() - .block_on(connect_peer_if_necessary( - pubkey, - peer_addr, - peer_manager.clone(), - )) + if connect_peer_if_necessary( + pubkey, + peer_addr, + peer_manager.clone(), + ) + .await .is_ok() { println!("SUCCESS: connected to peer {}", pubkey); From 009a548e53323c727d423a4f5880c1ed855d06ec Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 1 Oct 2025 10:19:22 +0000 Subject: [PATCH 9/9] Drop redundant `FilesystemStore` initialization --- src/main.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index cf5cb113..83c10efa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1027,13 +1027,10 @@ async fn start_ldk() { } }; - // Step 21: Persist ChannelManager and NetworkGraph - let persister = Arc::new(FilesystemStore::new(ldk_data_dir.clone().into())); - - // Step 22: Background Processing + // Step 21: Background Processing let (bp_exit, bp_exit_check) = tokio::sync::watch::channel(()); let mut background_processor = tokio::spawn(process_events_async( - Arc::clone(&persister), + Arc::clone(&fs_store), event_handler, Arc::clone(&chain_monitor), Arc::clone(&channel_manager), @@ -1133,14 +1130,14 @@ async fn start_ldk() { ldk_data_dir.clone(), Arc::clone(&keys_manager), Arc::clone(&logger), - Arc::clone(&persister), + Arc::clone(&fs_store), Arc::clone(&output_sweeper), )); // Start the CLI. let cli_channel_manager = Arc::clone(&channel_manager); let cli_chain_monitor = Arc::clone(&chain_monitor); - let cli_persister = Arc::clone(&persister); + let cli_fs_store = Arc::clone(&fs_store); let cli_peer_manager = Arc::clone(&peer_manager); let cli_poll = tokio::task::spawn( cli::poll_for_user_input( @@ -1151,7 +1148,7 @@ async fn start_ldk() { network_graph, inbound_payments, outbound_payments, - cli_persister, + cli_fs_store, ) ); @@ -1171,7 +1168,7 @@ async fn start_ldk() { peer_manager.disconnect_all_peers(); if let Err(e) = bg_res { - let persist_res = persister + let persist_res = fs_store .write( persist::CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE, persist::CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE,