From 6a5b1fda4c4c1f713f2d9bc500208b56775818f6 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 26 Sep 2025 19:48:46 +0000 Subject: [PATCH 01/17] 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 | 683 ++++++++++++++++++++-------------------- src/sweep.rs | 4 +- 6 files changed, 848 insertions(+), 652 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..fc1ed702 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", features = ["dnssec"] } +lightning-block-sync = { version = "0.2.0", features = [ "rpc-client", "tokio" ] } +lightning-dns-resolver = { version = "0.3.0" } +lightning-invoice = { version = "0.34.0" } +lightning-net-tokio = { version = "0.2.0" } +lightning-persister = { version = "0.2.0" } +lightning-background-processor = { version = "0.2.0" } +lightning-rapid-gossip-sync = { version = "0.2.0" } +lightning-macros = { version = "0.2.0" } 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..be50b3fa 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; + } } } } - } - }); - }, + }); + }, + _ => {}, + } } } @@ -633,11 +631,12 @@ async fn start_ldk() { key }; 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 keys_manager = + Arc::new(KeysManager::new(&keys_seed, cur.as_secs(), cur.subsec_nanos(), true)); - 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 +663,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 +801,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 +829,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 +867,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 +963,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 +1013,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 +1149,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 54a1a6c396540434e28153203d1ab35ba8269574 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:31:06 +0000 Subject: [PATCH 02/17] f fmt --- src/cli.rs | 4 +--- src/main.rs | 29 ++++++++++++++++++++++------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index df0fb2e4..e099b006 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -625,9 +625,7 @@ fn node_info( ref balance_candidates, confirmed_balance_candidate_index, .. - } => { - balance_candidates[confirmed_balance_candidate_index].transaction_fee_satoshis - }, + } => balance_candidates[confirmed_balance_candidate_index].transaction_fee_satoshis, _ => 0, }; let close_fees_sats = balances.iter().map(close_fees_map).sum::(); diff --git a/src/main.rs b/src/main.rs index be50b3fa..9a01e43a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -253,13 +253,18 @@ fn handle_ldk_events<'a>( 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; + 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) + .funding_transaction_generated( + temporary_channel_id, + counterparty_node_id, + final_tx, + ) .is_err() { println!( @@ -279,9 +284,13 @@ fn handle_ldk_events<'a>( print!("> "); std::io::stdout().flush().unwrap(); let payment_preimage = match purpose { - PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => payment_preimage, + PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => { + payment_preimage + }, PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => payment_preimage, - PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => payment_preimage, + PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => { + payment_preimage + }, PaymentPurpose::SpontaneousPayment(preimage) => Some(preimage), }; channel_manager.claim_funds(payment_preimage.unwrap()); @@ -295,13 +304,19 @@ fn handle_ldk_events<'a>( std::io::stdout().flush().unwrap(); let (payment_preimage, payment_secret) = match purpose { PaymentPurpose::Bolt11InvoicePayment { - payment_preimage, payment_secret, .. + payment_preimage, + payment_secret, + .. } => (payment_preimage, Some(payment_secret)), - PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. } => { + PaymentPurpose::Bolt12OfferPayment { + payment_preimage, payment_secret, .. + } => { (payment_preimage, Some(payment_secret)) }, PaymentPurpose::Bolt12RefundPayment { - payment_preimage, payment_secret, .. + payment_preimage, + payment_secret, + .. } => (payment_preimage, Some(payment_secret)), PaymentPurpose::SpontaneousPayment(preimage) => (Some(preimage), None), }; From 6c7306ba5e1940195fe7e9af8c0824dbaa3d6b76 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 07:48:12 +0000 Subject: [PATCH 03/17] 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 fc1ed702..91750b0b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ lightning-block-sync = { version = "0.2.0", features = [ "rpc-client", "tokio" ] lightning-dns-resolver = { version = "0.3.0" } lightning-invoice = { version = "0.34.0" } lightning-net-tokio = { version = "0.2.0" } -lightning-persister = { version = "0.2.0" } +lightning-persister = { version = "0.2.0", features = [ "tokio" ] } lightning-background-processor = { version = "0.2.0" } lightning-rapid-gossip-sync = { version = "0.2.0" } lightning-macros = { version = "0.2.0" } @@ -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..db4f9407 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 e099b006..3c02ce49 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(); @@ -828,9 +838,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()); @@ -848,16 +858,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, @@ -875,15 +889,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()); @@ -892,16 +910,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(), @@ -916,8 +938,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 9a01e43a..8f2b36fd 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, @@ -320,50 +318,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, .. @@ -417,12 +421,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. @@ -492,7 +499,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!( @@ -539,7 +546,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 { @@ -649,9 +656,9 @@ async fn start_ldk() { let keys_manager = Arc::new(KeysManager::new(&keys_seed, cur.as_secs(), cur.subsec_nanos(), true)); - 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), )); @@ -775,7 +782,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(), @@ -979,6 +986,7 @@ async fn start_ldk() { } fs_store .write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.lock().unwrap().encode()) + .await .unwrap(); // Step 20: Handle LDK Events @@ -1028,7 +1036,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), @@ -1129,7 +1137,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, @@ -1141,7 +1149,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). @@ -1166,6 +1174,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 70d2fa6e7fa660e9b2a2d513ec42fe809578b139 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:58:07 +0000 Subject: [PATCH 04/17] f fmt --- src/bitcoind_client.rs | 8 ++---- src/cli.rs | 31 ++++++++++++--------- src/main.rs | 61 +++++++++++++++++++++++++++--------------- 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs index db4f9407..71dfc09f 100644 --- a/src/bitcoind_client.rs +++ b/src/bitcoind_client.rs @@ -364,9 +364,7 @@ impl BroadcasterInterface for BitcoindClient { 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()) - }) + Box::pin(async move { Ok(self.get_new_address().await.script_pubkey()) }) } } @@ -407,9 +405,7 @@ impl WalletSource for BitcoindClient { } fn get_change_script<'a>(&'a self) -> AsyncResult<'a, ScriptBuf, ()> { - Box::pin(async move { - Ok(self.get_new_address().await.script_pubkey()) - }) + Box::pin(async move { Ok(self.get_new_address().await.script_pubkey()) }) } fn sign_psbt<'a>(&'a self, tx: Psbt) -> AsyncResult<'a, Transaction, ()> { diff --git a/src/cli.rs b/src/cli.rs index 3c02ce49..e5176bae 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -205,7 +205,7 @@ pub(crate) async fn poll_for_user_input( let line = match input.next_line().await { Ok(Some(l)) => l, - Err(e) => { + Err(e) => { println!("ERROR: {}", e); break 'read_command; }, @@ -318,13 +318,16 @@ pub(crate) async fn poll_for_user_input( } } else { match Bolt11Invoice::from_str(invoice_str) { - Ok(invoice) => send_payment( - &channel_manager, - &invoice, - user_provided_amt, - &outbound_payments, - &*fs_store, - ).await, + Ok(invoice) => { + send_payment( + &channel_manager, + &invoice, + user_provided_amt, + &outbound_payments, + &*fs_store, + ) + .await + }, Err(e) => { println!("ERROR: invalid invoice: {:?}", e); }, @@ -366,7 +369,8 @@ pub(crate) async fn poll_for_user_input( &*keys_manager, &outbound_payments, &*fs_store, - ).await; + ) + .await; }, "getoffer" => { let offer_builder = channel_manager.create_offer_builder(); @@ -429,8 +433,7 @@ pub(crate) async fn poll_for_user_input( &channel_manager, expiry_secs.unwrap(), ); - fs_store - .write("", "", INBOUND_PAYMENTS_FNAME, inbound_payments.encode()) + fs_store.write("", "", INBOUND_PAYMENTS_FNAME, inbound_payments.encode()) }; write_future.await.unwrap(); }, @@ -891,7 +894,8 @@ async fn send_payment( print!("> "); let write_future = { let mut outbound_payments = outbound_payments.lock().unwrap(); - outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; + outbound_payments.payments.get_mut(&payment_id).unwrap().status = + HTLCStatus::Failed; fs_store.write("", "", OUTBOUND_PAYMENTS_FNAME, outbound_payments.encode()) }; write_future.await.unwrap(); @@ -940,7 +944,8 @@ async fn keysend( print!("> "); let write_future = { let mut outbound_payments = outbound_payments.lock().unwrap(); - outbound_payments.payments.get_mut(&payment_id).unwrap().status = HTLCStatus::Failed; + 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 8f2b36fd..25a80d71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -308,9 +308,7 @@ fn handle_ldk_events<'a>( } => (payment_preimage, Some(payment_secret)), PaymentPurpose::Bolt12OfferPayment { payment_preimage, payment_secret, .. - } => { - (payment_preimage, Some(payment_secret)) - }, + } => (payment_preimage, Some(payment_secret)), PaymentPurpose::Bolt12RefundPayment { payment_preimage, payment_secret, @@ -341,7 +339,11 @@ fn handle_ldk_events<'a>( write_future.await.unwrap(); }, Event::PaymentSent { - payment_preimage, payment_hash, fee_paid_msat, payment_id, .. + payment_preimage, + payment_hash, + fee_paid_msat, + payment_id, + .. } => { let write_future = { let mut outbound = outbound_payments.lock().unwrap(); @@ -370,7 +372,9 @@ fn handle_ldk_events<'a>( write_future.await.unwrap(); }, Event::OpenChannelRequest { - ref temporary_channel_id, ref counterparty_node_id, .. + 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]); @@ -409,13 +413,21 @@ fn handle_ldk_events<'a>( "\nEVENT: Failed to send payment to payment ID {}, payment hash {}: {:?}", payment_id, hash, - if let Some(r) = reason { r } else { PaymentFailureReason::RetriesExhausted } + 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 } + if let Some(r) = reason { + r + } else { + PaymentFailureReason::RetriesExhausted + } ); } print!("> "); @@ -448,7 +460,8 @@ fn handle_ldk_events<'a>( 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) { + 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)) { @@ -468,8 +481,11 @@ fn handle_ldk_events<'a>( .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 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)); @@ -499,7 +515,11 @@ fn handle_ldk_events<'a>( }, Event::HTLCHandlingFailed { .. } => {}, Event::SpendableOutputs { outputs, channel_id } => { - output_sweeper.0.track_spendable_outputs(outputs, channel_id, false, None).await.unwrap(); + output_sweeper + .0 + .track_spendable_outputs(outputs, channel_id, false, None) + .await + .unwrap(); }, Event::ChannelPending { channel_id, counterparty_node_id, .. } => { println!( @@ -510,11 +530,7 @@ fn handle_ldk_events<'a>( print!("> "); std::io::stdout().flush().unwrap(); }, - Event::ChannelReady { - ref channel_id, - ref counterparty_node_id, - .. - } => { + Event::ChannelReady { ref channel_id, ref counterparty_node_id, .. } => { println!( "\nEVENT: Channel {} with peer {} is ready to be used!", channel_id, @@ -778,11 +794,14 @@ async fn start_ldk() { }; // Step 12: Initialize the OutputSweeper. - let (sweeper_best_block, output_sweeper) = match fs_store.read( - OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, - OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, - OUTPUT_SWEEPER_PERSISTENCE_KEY, - ).await { + let (sweeper_best_block, output_sweeper) = match fs_store + .read( + 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(), From 2ec3d27fa4ec4913425c482e6b5d3f718d08758b Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:27:56 +0000 Subject: [PATCH 05/17] f upstream spawner --- src/main.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 25a80d71..365aaef2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -48,10 +48,8 @@ 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, NO_LIQUIDITY_MANAGER}; -use lightning_block_sync::init; -use lightning_block_sync::poll; -use lightning_block_sync::SpvClient; -use lightning_block_sync::UnboundedCache; +use lightning_block_sync::gossip::TokioSpawner; +use lightning_block_sync::{init, poll, SpvClient, UnboundedCache}; use lightning_dns_resolver::OMDomainResolver; use lightning_net_tokio::SocketDescriptor; use lightning_persister::fs_store::FilesystemStore; @@ -155,7 +153,7 @@ type ChainMonitor = chainmonitor::ChainMonitor< >; pub(crate) type GossipVerifier = lightning_block_sync::gossip::GossipVerifier< - lightning_block_sync::gossip::TokioSpawner, + TokioSpawner, Arc, Arc, >; @@ -925,7 +923,7 @@ async fn start_ldk() { // Install a GossipVerifier in in the P2PGossipSync let utxo_lookup = GossipVerifier::new( Arc::clone(&bitcoind_client.bitcoind_rpc_client), - lightning_block_sync::gossip::TokioSpawner, + TokioSpawner, Arc::clone(&gossip_sync), Arc::clone(&peer_manager), ); From 4c5fb674e5dd815c5247d096b009cb73eb8c80e0 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 30 Sep 2025 09:39:47 +0000 Subject: [PATCH 06/17] 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 365aaef2..2eb9f567 100644 --- a/src/main.rs +++ b/src/main.rs @@ -871,7 +871,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 614cbb772aa214d575c029206a49b976a40fe610 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:54:47 +0000 Subject: [PATCH 07/17] f unused import --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 2eb9f567..9109b4c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ use bitcoin::BlockHash; 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::chain::{BestBlock, Filter}; use lightning::events::bump_transaction::{BumpTransactionEventHandler, Wallet}; use lightning::events::{Event, PaymentFailureReason, PaymentPurpose}; use lightning::ln::channelmanager::{self, RecentPaymentDetails}; From 8c763055d401ef49b5e64a89869bd860de084529 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 07:39:18 +0000 Subject: [PATCH 08/17] 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 9109b4c9..f4196131 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}; @@ -59,6 +60,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; @@ -139,15 +141,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, >; @@ -210,6 +211,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, @@ -679,36 +688,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 @@ -870,6 +880,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 d9c3a349104484eb5faa263edb2e692f3246584c Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:38:06 +0000 Subject: [PATCH 09/17] f upstream spawner --- src/main.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index f4196131..91ad7559 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,7 +40,6 @@ 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, MonitorUpdatingPersisterAsync, OUTPUT_SWEEPER_PERSISTENCE_KEY, OUTPUT_SWEEPER_PERSISTENCE_PRIMARY_NAMESPACE, OUTPUT_SWEEPER_PERSISTENCE_SECONDARY_NAMESPACE, @@ -60,7 +59,6 @@ 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; @@ -211,14 +209,6 @@ 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, From 80c987b2cb07feb4a381ec53d3f746d6151e668f Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 08:07:30 +0000 Subject: [PATCH 10/17] 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 e5176bae..17fa36d2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -756,10 +756,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 06b8b8e550a5525457b70c140f74f3e28ebfcab2 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 08:10:25 +0000 Subject: [PATCH 11/17] 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 17fa36d2..f9633f76 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -845,7 +845,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. @@ -857,8 +857,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( @@ -867,7 +873,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()) @@ -883,7 +889,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 7211ed87ace2b89039d6949e0117e55f75c63b48 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Mon, 29 Sep 2025 08:38:46 +0000 Subject: [PATCH 12/17] 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 f9633f76..dab1bc59 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 91ad7559..7758492b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1083,37 +1083,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; + } + } } } }); @@ -1165,7 +1174,6 @@ async fn start_ldk() { network_graph, inbound_payments, outbound_payments, - ldk_data_dir, cli_persister, ) ); From bea3566653edb5fbdfe200541a899249b3bf2ec9 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:46:12 +0000 Subject: [PATCH 13/17] f fmt --- src/main.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7758492b..d6443484 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1102,13 +1102,12 @@ async fn start_ldk() { 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::>() + let non_onion = |addr| match addr { + &lightning::ln::msgs::SocketAddress::OnionV2(_) => None, + &lightning::ln::msgs::SocketAddress::OnionV3 { .. } => None, + _ => Some(addr.clone()), + }; + ann.addresses().iter().filter_map(non_onion).collect::>() } else { Vec::new() } @@ -1117,7 +1116,9 @@ async fn start_ldk() { }; for addr in addrs { let sockaddrs = addr.to_socket_addrs(); - if sockaddrs.is_err() { continue; } + if sockaddrs.is_err() { + continue; + } for sockaddr in sockaddrs.unwrap() { let _ = cli::do_connect_peer(node_id, sockaddr, Arc::clone(&connect_pm)).await; From dce5a585a6bd2d05033c03b000f698712978e4d7 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 30 Sep 2025 09:38:39 +0000 Subject: [PATCH 14/17] 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 dab1bc59..0dfe5e51 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; @@ -438,12 +438,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 a1ddbb3b21a81b7533083a3eb1229911259a86b7 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:33:13 +0000 Subject: [PATCH 15/17] f fmt --- src/cli.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 0dfe5e51..183eec5b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -111,11 +111,7 @@ pub(crate) async fn poll_for_user_input( }, }; - if connect_peer_if_necessary( - pubkey, - peer_addr, - peer_manager.clone(), - ) + if connect_peer_if_necessary(pubkey, peer_addr, peer_manager.clone()) .await .is_err() { @@ -438,11 +434,7 @@ pub(crate) async fn poll_for_user_input( continue; }, }; - if connect_peer_if_necessary( - pubkey, - peer_addr, - peer_manager.clone(), - ) + if connect_peer_if_necessary(pubkey, peer_addr, peer_manager.clone()) .await .is_ok() { From ca2d2115892e4bf99e47c066c676ac8c62a9d09f Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Wed, 1 Oct 2025 10:19:22 +0000 Subject: [PATCH 16/17] 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 d6443484..bad20687 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1050,13 +1050,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), @@ -1157,14 +1154,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( @@ -1175,7 +1172,7 @@ async fn start_ldk() { network_graph, inbound_payments, outbound_payments, - cli_persister, + cli_fs_store, ) ); @@ -1195,7 +1192,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, From 4961b684036436ded73ddc34179cdbed90dd9a91 Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Tue, 16 Dec 2025 20:43:50 +0000 Subject: [PATCH 17/17] f fmt --- src/main.rs | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index bad20687..1b1a2ac7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1163,18 +1163,16 @@ async fn start_ldk() { let cli_chain_monitor = Arc::clone(&chain_monitor); 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( - cli_peer_manager, - cli_channel_manager, - cli_chain_monitor, - keys_manager, - network_graph, - inbound_payments, - outbound_payments, - cli_fs_store, - ) - ); + let cli_poll = tokio::task::spawn(cli::poll_for_user_input( + cli_peer_manager, + cli_channel_manager, + cli_chain_monitor, + keys_manager, + network_graph, + inbound_payments, + outbound_payments, + cli_fs_store, + )); // Exit if either CLI polling exits or the background processor exits (which shouldn't happen // unless we fail to write to the filesystem).