diff --git a/Cargo.lock b/Cargo.lock index 5a0bcfa4..1997ba0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,8 +188,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", "itoa", "matchit", @@ -199,7 +199,7 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", - "sync_wrapper", + "sync_wrapper 1.0.2", "tower", "tower-layer", "tower-service", @@ -213,13 +213,13 @@ checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite", "rustversion", - "sync_wrapper", + "sync_wrapper 1.0.2", "tower-layer", "tower-service", ] @@ -322,6 +322,22 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +[[package]] +name = "bitcoin-payment-instructions" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f00d509810205bfef492f1d6cefe1e2ac35b5e66675d51642315ddc5cee0e78" +dependencies = [ + "bitcoin", + "dnssec-prover", + "getrandom 0.3.3", + "lightning 0.1.6", + "lightning-invoice 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest", + "serde", + "serde_json", +] + [[package]] name = "bitcoin-units" version = "0.1.2" @@ -537,6 +553,16 @@ dependencies = [ "void", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -682,6 +708,15 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -744,6 +779,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fragile" version = "2.0.0" @@ -868,6 +912,25 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.12" @@ -879,7 +942,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.3.1", "indexmap", "slab", "tokio", @@ -941,6 +1004,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.3.1" @@ -952,6 +1026,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -959,7 +1044,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.3.1", ] [[package]] @@ -970,8 +1055,8 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", - "http-body", + "http 1.3.1", + "http-body 1.0.1", "pin-project-lite", ] @@ -993,6 +1078,30 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.7.0" @@ -1003,9 +1112,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", - "http-body", + "h2 0.4.12", + "http 1.3.1", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -1016,19 +1125,33 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + [[package]] name = "hyper-rustls" version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http", - "hyper", + "http 1.3.1", + "hyper 1.7.0", "hyper-util", "rustls 0.23.31", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.2", "tower-service", ] @@ -1038,7 +1161,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper", + "hyper 1.7.0", "hyper-util", "pin-project-lite", "tokio", @@ -1055,9 +1178,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http", - "http-body", - "hyper", + "http 1.3.1", + "http-body 1.0.1", + "hyper 1.7.0", "libc", "pin-project-lite", "socket2 0.6.0", @@ -1089,6 +1212,113 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -1099,6 +1329,12 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -1158,11 +1394,11 @@ dependencies = [ "bitcoin-bech32", "chrono", "libc", - "lightning", + "lightning 0.1.3", "lightning-background-processor", "lightning-block-sync", "lightning-dns-resolver", - "lightning-invoice", + "lightning-invoice 0.33.2 (git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed)", "lightning-net-tokio", "lightning-persister", "lightning-rapid-gossip-sync", @@ -1195,10 +1431,26 @@ dependencies = [ "dnssec-prover", "hashbrown 0.13.2", "libm", - "lightning-invoice", - "lightning-types", + "lightning-invoice 0.33.2 (git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed)", + "lightning-types 0.2.0 (git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed)", "musig2", - "possiblyrandom", + "possiblyrandom 0.2.0 (git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed)", +] + +[[package]] +name = "lightning" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b60a63d4f4e545a9ce8c92a73a8d0cbc62f5e5e6c029e18e8159cf8e8ce5e0" +dependencies = [ + "bech32 0.11.0", + "bitcoin", + "dnssec-prover", + "hashbrown 0.13.2", + "libm", + "lightning-invoice 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lightning-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "possiblyrandom 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1209,7 +1461,7 @@ dependencies = [ "bitcoin", "bitcoin-io", "bitcoin_hashes", - "lightning", + "lightning 0.1.3", "lightning-rapid-gossip-sync", ] @@ -1220,7 +1472,7 @@ source = "git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pic dependencies = [ "bitcoin", "chunked_transfer", - "lightning", + "lightning 0.1.3", "serde_json", "tokio", ] @@ -1231,11 +1483,22 @@ version = "0.2.0" source = "git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed#878619b2a95af44a5c5ab811805ae41846fb0cc4" dependencies = [ "dnssec-prover", - "lightning", - "lightning-types", + "lightning 0.1.3", + "lightning-types 0.2.0 (git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed)", "tokio", ] +[[package]] +name = "lightning-invoice" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11209f386879b97198b2bfc9e9c1e5d42870825c6bd4376f17f95357244d6600" +dependencies = [ + "bech32 0.11.0", + "bitcoin", + "lightning-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lightning-invoice" version = "0.33.2" @@ -1243,7 +1506,7 @@ source = "git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pic dependencies = [ "bech32 0.11.0", "bitcoin", - "lightning-types", + "lightning-types 0.2.0 (git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed)", ] [[package]] @@ -1252,7 +1515,7 @@ version = "0.1.0" source = "git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed#878619b2a95af44a5c5ab811805ae41846fb0cc4" dependencies = [ "bitcoin", - "lightning", + "lightning 0.1.3", "tokio", ] @@ -1262,7 +1525,7 @@ version = "0.1.0" source = "git+https://github.com/a-mpch/rust-lightning?branch=2025-06-cherry-pick-exposed#878619b2a95af44a5c5ab811805ae41846fb0cc4" dependencies = [ "bitcoin", - "lightning", + "lightning 0.1.3", "windows-sys 0.48.0", ] @@ -1274,7 +1537,16 @@ dependencies = [ "bitcoin", "bitcoin-io", "bitcoin_hashes", - "lightning", + "lightning 0.1.3", +] + +[[package]] +name = "lightning-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cd84d4e71472035903e43caded8ecc123066ce466329ccd5ae537a8d5488c7" +dependencies = [ + "bitcoin", ] [[package]] @@ -1291,6 +1563,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lndk" version = "0.3.0" @@ -1298,6 +1576,7 @@ dependencies = [ "async-trait", "async_fn_traits", "bitcoin", + "bitcoin-payment-instructions", "bitcoincore-rpc", "bytes", "chrono", @@ -1309,7 +1588,7 @@ dependencies = [ "hex", "home", "ldk-sample", - "lightning", + "lightning 0.1.3", "lndk-tonic-lnd", "log", "log4rs", @@ -1318,6 +1597,7 @@ dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", "rcgen", + "reqwest", "tempfile", "tokio", "tonic", @@ -1325,6 +1605,7 @@ dependencies = [ "tonic-prost-build", "tonic-types", "triggered", + "url", ] [[package]] @@ -1333,13 +1614,13 @@ version = "0.1.3" source = "git+https://github.com/lndk-org/tonic_lnd?rev=f89f49aadc439b37d65947cc454eecffdc8c35e0#f89f49aadc439b37d65947cc454eecffdc8c35e0" dependencies = [ "hex", - "http", - "hyper", - "hyper-rustls", + "http 1.3.1", + "hyper 1.7.0", + "hyper-rustls 0.27.7", "hyper-util", "prost", "rustls 0.23.31", - "rustls-pemfile", + "rustls-pemfile 2.2.0", "thiserror 2.0.16", "tokio", "tokio-stream", @@ -1696,6 +1977,15 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "possiblyrandom" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b122a615d72104fb3d8b26523fdf9232cd8ee06949fb37e4ce3ff964d15dffd" +dependencies = [ + "getrandom 0.2.15", +] + [[package]] name = "possiblyrandom" version = "0.2.0" @@ -1704,6 +1994,15 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1970,6 +2269,47 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "reqwest" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-rustls 0.24.2", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls 0.21.12", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-rustls 0.24.1", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + [[package]] name = "ring" version = "0.17.14" @@ -2045,6 +2385,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + [[package]] name = "rustls-pemfile" version = "2.2.0" @@ -2174,6 +2523,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -2237,6 +2598,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "strsim" version = "0.11.1" @@ -2271,6 +2638,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -2288,6 +2661,27 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" version = "0.4.41" @@ -2398,6 +2792,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.38.1" @@ -2427,6 +2831,16 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.2" @@ -2480,19 +2894,19 @@ dependencies = [ "axum", "base64 0.22.1", "bytes", - "h2", - "http", - "http-body", + "h2 0.4.12", + "http 1.3.1", + "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.7.0", "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", "socket2 0.6.0", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", - "tokio-rustls", + "tokio-rustls 0.26.2", "tokio-stream", "tower", "tower-layer", @@ -2561,7 +2975,7 @@ dependencies = [ "indexmap", "pin-project-lite", "slab", - "sync_wrapper", + "sync_wrapper 1.0.2", "tokio", "tokio-util", "tower-layer", @@ -2672,6 +3086,23 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2733,6 +3164,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.92" @@ -2762,6 +3205,16 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.25.4" @@ -2962,12 +3415,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wit-bindgen" version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "x509-parser" version = "0.17.0" @@ -3006,12 +3475,90 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/Cargo.toml b/Cargo.toml index 41ba98b7..39c85bc8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,9 @@ bytes = "1.4.0" triggered = "0.1.2" prost = "0.14.1" async_fn_traits = "0.1.1" +bitcoin-payment-instructions = { version = "0.5", features = ["http"] } +reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"] } +url = "2.5" # Integration test dependencies, only enabled with itest feature bitcoincore-rpc = { version = "0.19.0", optional = true } diff --git a/proto/lndkrpc.proto b/proto/lndkrpc.proto index c8a50552..bf595d5e 100644 --- a/proto/lndkrpc.proto +++ b/proto/lndkrpc.proto @@ -5,6 +5,7 @@ import "external/google/rpc/error_details.proto"; service Offers { rpc PayOffer (PayOfferRequest) returns (PayOfferResponse); + rpc PayHumanReadableAddress (PayHumanReadableAddressRequest) returns (PayOfferResponse); rpc GetInvoice (GetInvoiceRequest) returns (GetInvoiceResponse); rpc DecodeInvoice (DecodeInvoiceRequest) returns (Bolt12InvoiceContents); rpc PayInvoice (PayInvoiceRequest) returns (PayInvoiceResponse); @@ -20,6 +21,15 @@ message PayOfferRequest { optional uint32 fee_limit_percent = 6; } +message PayHumanReadableAddressRequest { + string name = 1; + optional uint64 amount = 2; + optional string payer_note = 3; + optional uint32 response_invoice_timeout = 4; + optional uint32 fee_limit = 5; + optional uint32 fee_limit_percent = 6; +} + message PayOfferResponse { string payment_preimage = 2; } diff --git a/src/cli.rs b/src/cli.rs index 458882ca..8eb86f37 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,10 @@ use clap::{Parser, Subcommand}; use lightning::offers::invoice::Bolt12Invoice; use lndk::lndkrpc::offers_client::OffersClient; -use lndk::lndkrpc::{CreateOfferRequest, GetInvoiceRequest, PayInvoiceRequest, PayOfferRequest}; +use lndk::lndkrpc::{ + CreateOfferRequest, GetInvoiceRequest, PayHumanReadableAddressRequest, PayInvoiceRequest, + PayOfferRequest, +}; use lndk::offers::decode; use lndk::offers::handler::DEFAULT_RESPONSE_INVOICE_TIMEOUT; use lndk::{ @@ -144,6 +147,34 @@ enum Commands { #[arg(long, required = false, conflicts_with = "fee_limit")] fee_limit_percent: Option, }, + /// PayHumanReadableAddress pays a BOLT 12 offer by resolving a human-readable name (BIP-353). + PayHumanReadableAddress { + /// The human-readable name to resolve (e.g., "user@example.com"). + name: String, + + /// Amount the user would like to pay. If this isn't set, we'll assume the user is paying + /// whatever the offer amount is. + #[arg(required = false)] + amount: Option, + + /// A payer-provided note which will be seen by the recipient. + #[arg(required = false)] + payer_note: Option, + + /// The amount of time in seconds that the user would like to wait for an invoice to + /// arrive. If this isn't set, we'll use the default value. + #[arg(long, global = false, required = false, default_value = DEFAULT_RESPONSE_INVOICE_TIMEOUT.to_string())] + response_invoice_timeout: Option, + + /// A fixed fee limit in millisatoshis. + /// Mutually exclusive with fee_limit_percent - only one can be set. + #[arg(long, required = false, conflicts_with = "fee_limit_percent")] + fee_limit: Option, + /// A percentage-based fee limit of the payment amount. + /// Mutually exclusive with fee_limit - only one can be set. + #[arg(long, required = false, conflicts_with = "fee_limit")] + fee_limit_percent: Option, + }, /// GetInvoice fetch a BOLT 12 invoice, which will be returned as a hex-encoded string. It /// fetches the invoice from a BOLT 12 offer, provided as a 'lno'-prefaced offer string. GetInvoice { @@ -261,6 +292,38 @@ async fn main() { Err(err) => err.exit_gracefully(), }; } + Commands::PayHumanReadableAddress { + ref name, + amount, + payer_note, + response_invoice_timeout, + fee_limit, + fee_limit_percent, + } => { + let tls = read_cert_from_args_or_exit(args.cert_pem, args.cert_path); + let channel = create_grpc_channel(args.grpc_host, args.grpc_port, tls).await; + let (mut client, macaroon) = create_authenticated_client( + channel, + args.macaroon_path, + args.macaroon_hex, + &args.network, + ); + + let mut request = Request::new(PayHumanReadableAddressRequest { + name: name.clone(), + amount, + payer_note, + response_invoice_timeout, + fee_limit, + fee_limit_percent, + }); + add_metadata(&mut request, macaroon); + + match client.pay_human_readable_address(request).await { + Ok(_) => println!("Successfully paid for name {}!", name), + Err(err) => err.exit_gracefully(), + }; + } Commands::GetInvoice { ref offer_string, amount, diff --git a/src/dns_resolver.rs b/src/dns_resolver.rs new file mode 100644 index 00000000..dd67157a --- /dev/null +++ b/src/dns_resolver.rs @@ -0,0 +1,111 @@ +use crate::OfferError; +use bitcoin_payment_instructions::hrn_resolution::{HrnResolution, HrnResolver, HumanReadableName}; +use bitcoin_payment_instructions::http_resolver::HTTPHrnResolver; +use std::sync::Arc; +use std::time::Duration; +use url::Url; + +const DEFAULT_DNS_TIMEOUT_SECS: u64 = 20; + +#[derive(Clone)] +pub struct LndkDNSResolverMessageHandler { + resolver: Arc, +} + +impl Default for LndkDNSResolverMessageHandler { + fn default() -> Self { + Self::new() + } +} + +impl LndkDNSResolverMessageHandler { + pub fn new() -> Self { + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(DEFAULT_DNS_TIMEOUT_SECS)) + .build() + .expect("Failed to build HTTP client for DNS resolution"); + + Self::with_resolver(HTTPHrnResolver::with_client(client)) + } + + pub fn with_resolver(resolver: R) -> Self { + Self { + resolver: Arc::new(resolver), + } + } + + pub async fn resolver_hrn_to_offer(&self, name_str: &str) -> Result { + let resolved_uri = self.resolve_locally(name_str.to_string()).await?; + self.extract_offer_from_uri(&resolved_uri) + } + + pub fn extract_offer_from_uri(&self, uri: &str) -> Result { + let url = Url::parse(uri) + .map_err(|_| OfferError::ResolveUriError("Invalid URI format".to_string()))?; + + for (key, value) in url.query_pairs() { + if key.eq_ignore_ascii_case("lno") { + return Ok(value.into_owned()); + } + } + + Err(OfferError::ResolveUriError( + "URI does not contain 'lno' parameter with BOLT12 offer".to_string(), + )) + } + + pub async fn resolve_locally(&self, name: String) -> Result { + let hrn_parsed = HumanReadableName::from_encoded(&name) + .map_err(|_| OfferError::ParseHrnFailure(name.clone()))?; + + let resolution = self + .resolver + .resolve_hrn(&hrn_parsed) + .await + .map_err(|e| OfferError::HrnResolutionFailure(format!("{}: {}", name, e)))?; + + let uri = match resolution { + HrnResolution::DNSSEC { result, .. } => result, + HrnResolution::LNURLPay { .. } => { + return Err(OfferError::ResolveUriError( + "LNURL resolution not supported in this flow".to_string(), + )) + } + }; + + Ok(uri) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_extract_offer_from_simple_uri() { + let handler = LndkDNSResolverMessageHandler::new(); + let uri = "bitcoin:?lno=lno1qgsqvgnwgcg35z"; + let result = handler.extract_offer_from_uri(uri); + assert_eq!(result.unwrap(), "lno1qgsqvgnwgcg35z"); + } + + #[test] + fn test_extract_offer_with_percent_encoding() { + let handler = LndkDNSResolverMessageHandler::new(); + let uri = "bitcoin:?lno=lno1%20test%3Dvalue"; + let result = handler.extract_offer_from_uri(uri); + assert_eq!(result.unwrap(), "lno1 test=value"); + } + + #[test] + fn test_extract_offer_missing_param() { + let handler = LndkDNSResolverMessageHandler::new(); + let uri = "bitcoin:?amount=50&label=test"; + let result = handler.extract_offer_from_uri(uri); + assert!(result.is_err()); + assert!(result + .unwrap_err() + .to_string() + .contains("does not contain 'lno' parameter")); + } +} diff --git a/src/lib.rs b/src/lib.rs index 07c99c4b..ea43513f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ mod clock; +pub mod dns_resolver; mod grpc; #[allow(dead_code)] pub mod lnd; diff --git a/src/offers/handler.rs b/src/offers/handler.rs index b451620b..497414c9 100644 --- a/src/offers/handler.rs +++ b/src/offers/handler.rs @@ -31,7 +31,9 @@ use super::lnd_requests::{ send_invoice_request, LndkBolt12InvoiceInfo, }; use super::OfferError; +use crate::dns_resolver::LndkDNSResolverMessageHandler; use crate::offers::lnd_requests::{send_payment, track_payment, CreateOfferArgs}; +use crate::offers::{get_destination, parse::decode}; use crate::onion_messenger::MessengerUtilities; pub const DEFAULT_RESPONSE_INVOICE_TIMEOUT: u32 = 15; @@ -57,6 +59,7 @@ pub struct OfferHandler { /// an invoice. If not provided, we will use the default value of 15 seconds. pub response_invoice_timeout: u32, client: Option, + dns_resolver: LndkDNSResolverMessageHandler, } #[derive(Clone)] @@ -77,6 +80,18 @@ pub struct PayOfferParams { pub fee_limit: Option, } +#[derive(Clone)] +pub struct PayHumanReadableAddressParams { + pub name: String, + pub amount: Option, + pub payer_note: Option, + pub network: Network, + pub client: Client, + pub reply_path: Option, + pub response_invoice_timeout: Option, + pub fee_limit: Option, +} + #[derive(Clone)] pub struct SendPaymentParams { pub path: BlindedPaymentPath, @@ -113,6 +128,20 @@ impl OfferHandler { response_invoice_timeout: Option, seed: Option<[u8; 32]>, client: Option, + ) -> Self { + Self::with_dns_resolver( + response_invoice_timeout, + seed, + client, + LndkDNSResolverMessageHandler::new(), + ) + } + + pub fn with_dns_resolver( + response_invoice_timeout: Option, + seed: Option<[u8; 32]>, + client: Option, + dns_resolver: LndkDNSResolverMessageHandler, ) -> Self { let messenger_utils = MessengerUtilities::default(); let random_bytes = match seed { @@ -130,6 +159,7 @@ impl OfferHandler { expanded_key, response_invoice_timeout, client, + dns_resolver, } } @@ -150,6 +180,28 @@ impl OfferHandler { .await } + /// Resolves a human-readable name (BIP-353) to an offer and pays it. + pub async fn pay_hrn(&self, cfg: PayHumanReadableAddressParams) -> Result { + let offer_str = self.dns_resolver.resolver_hrn_to_offer(&cfg.name).await?; + + let offer = decode(offer_str)?; + let destination = get_destination(&offer).await?; + + let pay_offer_cfg = PayOfferParams { + offer, + amount: cfg.amount, + payer_note: cfg.payer_note, + network: cfg.network, + client: cfg.client, + destination, + reply_path: cfg.reply_path, + response_invoice_timeout: cfg.response_invoice_timeout, + fee_limit: cfg.fee_limit, + }; + + self.pay_offer(pay_offer_cfg).await + } + /// Sends an invoice request and waits for an invoice to be sent back to us. /// Reminder that if this method returns an error after create_invoice_request is called, we /// *must* remove the payment_id from self.active_payments. diff --git a/src/offers/mod.rs b/src/offers/mod.rs index 81608d79..247b2f59 100644 --- a/src/offers/mod.rs +++ b/src/offers/mod.rs @@ -71,6 +71,12 @@ pub enum OfferError { EncodeInvoiceFailure(BitcoinIoError), /// Cannot list channels. ListChannelsFailure(String), + /// Failed to parse human-readable name. + ParseHrnFailure(String), + /// HRN resolution failed. + HrnResolutionFailure(String), + /// DNS resolution or URI parsing error. + ResolveUriError(String), } impl OfferError { @@ -101,6 +107,9 @@ impl OfferError { OfferError::IntroductionNodeNotFound => "INTRODUCTION_NODE_NOT_FOUND", OfferError::GetChannelInfo(_) => "GET_CHANNEL_INFO", OfferError::ListChannelsFailure(_) => "LIST_CHANNELS_FAILURE", + OfferError::ParseHrnFailure(_) => "PARSE_HRN_FAILURE", + OfferError::HrnResolutionFailure(_) => "HRN_RESOLUTION_FAILURE", + OfferError::ResolveUriError(_) => "RESOLVE_URI_ERROR", } } @@ -110,7 +119,10 @@ impl OfferError { | OfferError::InvalidCurrency | OfferError::ParseOfferFailure(_) | OfferError::ParseInvoiceFailure(_) - | OfferError::EncodeInvoiceFailure(_) => Code::InvalidArgument, + | OfferError::EncodeInvoiceFailure(_) + | OfferError::ParseHrnFailure(_) + | OfferError::HrnResolutionFailure(_) + | OfferError::ResolveUriError(_) => Code::InvalidArgument, _ => Code::Internal, } } @@ -168,6 +180,15 @@ impl Display for OfferError { } OfferError::ListChannelsFailure(e) => write!(f, "Error listing channels: {e:?}"), OfferError::EncodeInvoiceFailure(e) => write!(f, "Failed to encode invoice: {e:?}"), + OfferError::ParseHrnFailure(e) => { + write!(f, "Invalid human-readable name: {}", e) + } + OfferError::HrnResolutionFailure(e) => { + write!(f, "HRN resolution failed: {}", e) + } + OfferError::ResolveUriError(e) => { + write!(f, "Failed to resolve URI: {}", e) + } } } } diff --git a/src/server.rs b/src/server.rs index 14047480..4265ed15 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,7 @@ use crate::lnd::{get_lnd_client, get_network, Creds, LndCfg, LndError}; use crate::lndkrpc::{CreateOfferRequest, CreateOfferResponse}; use crate::offers::get_destination; -use crate::offers::handler::{CreateOfferParams, PayOfferParams}; +use crate::offers::handler::{CreateOfferParams, PayHumanReadableAddressParams, PayOfferParams}; use crate::offers::parse::decode; use crate::offers::validate_amount; use crate::offers::OfferError; @@ -17,8 +17,8 @@ use lightning::util::ser::Writeable; use lndkrpc::offers_server::Offers; use lndkrpc::{ Bolt12InvoiceContents, DecodeInvoiceRequest, FeatureBit, GetInvoiceRequest, GetInvoiceResponse, - PayInvoiceRequest, PayInvoiceResponse, PayOfferRequest, PayOfferResponse, PaymentHash, - PaymentPaths, + PayHumanReadableAddressRequest, PayInvoiceRequest, PayInvoiceResponse, PayOfferRequest, + PayOfferResponse, PaymentHash, PaymentPaths, }; use std::num::NonZeroU64; use std::str::FromStr; @@ -111,6 +111,57 @@ impl Offers for LNDKServer { Ok(Response::new(reply)) } + async fn pay_human_readable_address( + &self, + request: Request, + ) -> Result, Status> { + log::info!("Received a request: {:?}", request.get_ref()); + + let metadata = request.metadata(); + let macaroon = check_auth_metadata(metadata)?; + let creds = Creds::String { + cert: self.lnd_cert.clone(), + macaroon, + }; + let lnd_cfg = LndCfg::new(self.address.clone(), creds); + let mut client = get_lnd_client(lnd_cfg)?; + + let inner_request = request.get_ref(); + let reply_path = None; + let info = client + .lightning() + .get_info(GetInfoRequest {}) + .await + .map_err(|e| LndError::ServiceUnavailable(e.message().to_string()))? + .into_inner(); + let network = get_network(info).await?; + + let fee_limit = create_fee_limit(inner_request.fee_limit, inner_request.fee_limit_percent); + + let cfg = PayHumanReadableAddressParams { + name: inner_request.name.clone(), + amount: inner_request.amount, + payer_note: inner_request.payer_note.clone(), + network, + client, + reply_path, + response_invoice_timeout: inner_request.response_invoice_timeout, + fee_limit, + }; + + let payment = self.offer_handler.pay_hrn(cfg).await?; + log::info!( + "Payment succeeded with preimage: {}", + payment.payment_preimage + ); + + let reply = PayOfferResponse { + payment_preimage: payment.payment_preimage, + }; + + Ok(Response::new(reply)) + } + async fn decode_invoice( &self, request: Request, diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 1fa8e3b3..e353c87b 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -27,9 +27,9 @@ use std::net::SocketAddr; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; -use std::time::SystemTime; +use std::time::{Duration, SystemTime}; use tokio::time; -use tokio::time::Duration; + use tokio::{select, try_join}; use tonic_lnd::Client; @@ -1440,3 +1440,129 @@ async fn test_receive_payment_from_offer_with_multiple_blinded_paths() { } }; } + +struct MockHrnResolver { + response_uri: String, +} + +impl bitcoin_payment_instructions::hrn_resolution::HrnResolver for MockHrnResolver { + fn resolve_hrn<'a>( + &'a self, + _hrn: &'a bitcoin_payment_instructions::hrn_resolution::HumanReadableName, + ) -> bitcoin_payment_instructions::hrn_resolution::HrnResolutionFuture<'a> { + let uri = self.response_uri.clone(); + Box::pin(async move { + Ok( + bitcoin_payment_instructions::hrn_resolution::HrnResolution::DNSSEC { + proof: None, + result: uri, + }, + ) + }) + } + + fn resolve_lnurl<'a>( + &'a self, + _url: &'a str, + ) -> bitcoin_payment_instructions::hrn_resolution::HrnResolutionFuture<'a> { + Box::pin(async { Err("LNURL resolution not supported in mock") }) + } + + fn resolve_lnurl_to_invoice<'a>( + &'a self, + _callback_url: String, + _amount: bitcoin_payment_instructions::amount::Amount, + _expected_description_hash: [u8; 32], + ) -> bitcoin_payment_instructions::hrn_resolution::LNURLResolutionFuture<'a> { + Box::pin(async { Err("LNURL invoice resolution not supported in mock") }) + } +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_pay_offer_with_name_and_dns() { + use lndk::offers::handler::PayHumanReadableAddressParams; + use std::time::{Duration, SystemTime}; + + let test_name = "lndk_pay_offer_with_name"; + let (bitcoind, mut lnd, ldk1, ldk2, lndk_dir, _) = + common::setup_test_infrastructure(test_name).await; + + let (ldk1_pubkey, ldk2_pubkey, _) = + common::connect_network(&ldk1, &ldk2, false, true, &mut lnd, &bitcoind).await; + + let path_pubkeys = vec![ldk2_pubkey, ldk1_pubkey]; + let expiration = SystemTime::now() + Duration::from_secs(24 * 60 * 60); + let offer = ldk1 + .create_offer( + &path_pubkeys, + Network::Regtest, + 20_000, + Quantity::One, + expiration, + ) + .await + .expect("should create offer"); + + let offer_string = offer.to_string(); + let human_readable_name = "satoshi@bip353.test"; + + let mock_resolver = MockHrnResolver { + response_uri: format!("bitcoin:?lno={}", offer_string), + }; + + let dns_resolver = + lndk::dns_resolver::LndkDNSResolverMessageHandler::with_resolver(mock_resolver); + + let resolved_offer = dns_resolver + .resolver_hrn_to_offer(human_readable_name) + .await; + assert!( + resolved_offer.is_ok(), + "DNS resolution failed: {:?}", + resolved_offer.err() + ); + assert_eq!(resolved_offer.unwrap(), offer_string); + + let handler = Arc::new(lndk::offers::handler::OfferHandler::with_dns_resolver( + None, + None, + None, + dns_resolver.clone(), + )); + + let (lndk_cfg, _, messenger, shutdown) = common::setup_lndk( + &lnd.cert_path, + &lnd.macaroon_path, + lnd.address.clone(), + lndk_dir, + ) + .await; + + let client = lnd.client.clone().unwrap(); + + let pay_cfg = PayHumanReadableAddressParams { + name: human_readable_name.to_string(), + amount: Some(20_000), + payer_note: None, + network: Network::Regtest, + client: client.clone(), + reply_path: None, + response_invoice_timeout: None, + fee_limit: None, + }; + + select! { + val = messenger.run(lndk_cfg, Arc::clone(&handler)) => { + panic!("messenger should not complete first {:?}", val); + }, + res = handler.pay_hrn(pay_cfg) => { + assert!(res.is_ok(), "pay_hrn failed: {:?}", res.err()); + let payment = res.unwrap(); + assert!(!payment.payment_preimage.is_empty(), "Payment should have a preimage"); + + shutdown.trigger(); + ldk1.stop().await; + ldk2.stop().await; + } + } +}