diff --git a/Cargo.lock b/Cargo.lock index 9fbb7373..767d2221 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -203,7 +203,7 @@ checksum = "83986307ea92997c3d051e8c306af8115a05add601e22acb7c1903008e6b614e" dependencies = [ "async-trait", "bdk_core", - "esplora-client", + "esplora-client 0.12.3", "futures", ] @@ -302,7 +302,7 @@ checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" [[package]] name = "bitcoin-payment-instructions" version = "0.6.0" -source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=0e430be98c09540624a68a68022ee0551e86d1be#0e430be98c09540624a68a68022ee0551e86d1be" +source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=1d6ffaa8962391ddc84aeef98bd7439e55ccae9d#1d6ffaa8962391ddc84aeef98bd7439e55ccae9d" dependencies = [ "bitcoin", "dnssec-prover", @@ -350,6 +350,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b65c2e1ab98050c93cc9ada070d5070e014329c65196d03f6f8f1f75c2666f37" dependencies = [ + "base64 0.22.1", "rustls 0.23.42", "rustls-webpki 0.103.13", "serde", @@ -573,7 +574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -591,6 +592,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "esplora-client" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647f9c89c653d69cce478202b7377c03737e33fc6bc5ec044187bf83e5597ea2" +dependencies = [ + "bitcoin", + "bitreq", + "hex-conservative 0.2.2", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1054,7 +1069,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -1266,7 +1281,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "ldk-node" version = "0.8.0+git" -source = "git+https://github.com/lightningdevkit/ldk-node?rev=056447c28221be02c3d39f8c6ae430a67ebbd850#056447c28221be02c3d39f8c6ae430a67ebbd850" +source = "git+https://github.com/lightningdevkit/ldk-node?rev=5e4197b0ac135b92874b7ee43a9b2e2a9f0a6c57#5e4197b0ac135b92874b7ee43a9b2e2a9f0a6c57" dependencies = [ "async-trait", "base64 0.22.1", @@ -1281,7 +1296,8 @@ dependencies = [ "bitreq", "chrono", "electrum-client", - "esplora-client", + "esplora-client 0.12.3", + "esplora-client 0.13.0", "getrandom 0.3.4", "libc", "lightning", @@ -1419,8 +1435,8 @@ dependencies = [ [[package]] name = "lightning" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bech32", "bitcoin", @@ -1436,8 +1452,8 @@ dependencies = [ [[package]] name = "lightning-background-processor" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitcoin-io", @@ -1450,8 +1466,8 @@ dependencies = [ [[package]] name = "lightning-block-sync" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitreq", @@ -1462,8 +1478,8 @@ dependencies = [ [[package]] name = "lightning-dns-resolver" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "dnssec-prover", "lightning", @@ -1473,8 +1489,8 @@ dependencies = [ [[package]] name = "lightning-invoice" -version = "0.35.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.35.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bech32", "bitcoin", @@ -1484,8 +1500,8 @@ dependencies = [ [[package]] name = "lightning-liquidity" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitreq", @@ -1501,7 +1517,7 @@ dependencies = [ [[package]] name = "lightning-macros" version = "0.2.2+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "proc-macro2", "quote", @@ -1510,8 +1526,8 @@ dependencies = [ [[package]] name = "lightning-net-tokio" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "lightning", @@ -1520,8 +1536,8 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "lightning", @@ -1531,8 +1547,8 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitcoin-io", @@ -1542,12 +1558,12 @@ dependencies = [ [[package]] name = "lightning-transaction-sync" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "electrum-client", - "esplora-client", + "esplora-client 0.13.0", "futures", "lightning", "lightning-macros", @@ -1555,8 +1571,8 @@ dependencies = [ [[package]] name = "lightning-types" -version = "0.4.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.4.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", ] @@ -1715,7 +1731,7 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "possiblyrandom" version = "0.2.0" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "getrandom 0.2.16", ] @@ -1824,7 +1840,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.42", - "socket2 0.5.10", + "socket2 0.6.1", "thiserror", "tokio", "tracing", @@ -1861,9 +1877,9 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.1", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2105,7 +2121,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2428,7 +2444,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/docs/api-guide.md b/docs/api-guide.md index eb182a0b..2e854644 100644 --- a/docs/api-guide.md +++ b/docs/api-guide.md @@ -113,8 +113,8 @@ These RPCs support a manual claim/fail workflow for held payments. See | RPC | Description | |------------------------|--------------------------------------------------------------------| | `Bolt11ReceiveForHash` | Create an invoice for a given payment hash (manual claim required) | -| `Bolt11ClaimForHash` | Claim a held payment by providing the preimage | -| `Bolt11FailForHash` | Reject a held payment | +| `Bolt11ClaimForId` | Claim a held payment by its payment ID and preimage | +| `Bolt11FailForId` | Reject a held payment by its payment ID | ### BOLT11 JIT Channels (LSPS2) @@ -130,8 +130,9 @@ when the invoice is paid. | RPC | Description | |-----------------|-------------------------------------------------------------------------| -| `Bolt12Receive` | Create a BOLT12 offer (fixed or variable amount) | -| `Bolt12Send` | Pay a BOLT12 offer (with optional quantity, payer note, routing config) | +| `Bolt12Receive` | Create a BOLT12 offer (fixed or variable amount) | +| `Bolt12Send` | Pay a BOLT12 offer (with optional quantity, payer note, routing config) | +| `Bolt12CreatePayerProof` | Create a BOLT 12 payer proof from a successful payment | ### Spontaneous and Unified Send @@ -230,11 +231,11 @@ optional Basic Auth. See [Configuration](configuration.md#metrics) for setup. Hodl invoices allow you to inspect and conditionally accept incoming payments: 1. **Create the invoice:** Call `Bolt11ReceiveForHash` with a payment hash you control. -2. **Wait for payment:** Subscribe to events via `SubscribeEvents` and watch for a - `PaymentClaimable` event matching your payment hash. +2. **Wait for payment:** Subscribe via `SubscribeEvents`. Save the payment ID from the matching + `PaymentClaimable` event. 3. **Decide:** - - **Accept:** Call `Bolt11ClaimForHash` with the preimage corresponding to the payment hash. - - **Reject:** Call `Bolt11FailForHash` with the payment hash. + - **Accept:** Call `Bolt11ClaimForId` with the payment ID and corresponding preimage. + - **Reject:** Call `Bolt11FailForId` with the payment ID. The payment is held in a pending state until you explicitly claim or fail it. **You must always call one of these.** If you do neither, the HTLC will eventually time out, which diff --git a/e2e-tests/Cargo.lock b/e2e-tests/Cargo.lock index 310f66d6..aa715ba9 100644 --- a/e2e-tests/Cargo.lock +++ b/e2e-tests/Cargo.lock @@ -165,7 +165,7 @@ checksum = "83986307ea92997c3d051e8c306af8115a05add601e22acb7c1903008e6b614e" dependencies = [ "async-trait", "bdk_core", - "esplora-client", + "esplora-client 0.12.3", "futures", ] @@ -250,7 +250,7 @@ checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin-payment-instructions" version = "0.6.0" -source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=0e430be98c09540624a68a68022ee0551e86d1be#0e430be98c09540624a68a68022ee0551e86d1be" +source = "git+https://github.com/tnull/bitcoin-payment-instructions?rev=1d6ffaa8962391ddc84aeef98bd7439e55ccae9d#1d6ffaa8962391ddc84aeef98bd7439e55ccae9d" dependencies = [ "bitcoin", "dnssec-prover", @@ -298,6 +298,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b65c2e1ab98050c93cc9ada070d5070e014329c65196d03f6f8f1f75c2666f37" dependencies = [ + "base64 0.22.1", "rustls 0.23.42", "rustls-webpki 0.103.13", "serde", @@ -566,7 +567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -584,6 +585,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "esplora-client" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647f9c89c653d69cce478202b7377c03737e33fc6bc5ec044187bf83e5597ea2" +dependencies = [ + "bitcoin", + "bitreq", + "hex-conservative", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1044,7 +1059,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.10", + "socket2 0.6.3", "tokio", "tower-service", "tracing", @@ -1269,7 +1284,7 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "ldk-node" version = "0.8.0+git" -source = "git+https://github.com/lightningdevkit/ldk-node?rev=056447c28221be02c3d39f8c6ae430a67ebbd850#056447c28221be02c3d39f8c6ae430a67ebbd850" +source = "git+https://github.com/lightningdevkit/ldk-node?rev=5e4197b0ac135b92874b7ee43a9b2e2a9f0a6c57#5e4197b0ac135b92874b7ee43a9b2e2a9f0a6c57" dependencies = [ "async-trait", "base64 0.22.1", @@ -1284,7 +1299,8 @@ dependencies = [ "bitreq", "chrono", "electrum-client", - "esplora-client", + "esplora-client 0.12.3", + "esplora-client 0.13.0", "getrandom 0.3.4", "libc", "lightning", @@ -1381,8 +1397,8 @@ dependencies = [ [[package]] name = "lightning" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bech32", "bitcoin", @@ -1398,8 +1414,8 @@ dependencies = [ [[package]] name = "lightning-background-processor" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitcoin-io", @@ -1412,8 +1428,8 @@ dependencies = [ [[package]] name = "lightning-block-sync" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitreq", @@ -1424,8 +1440,8 @@ dependencies = [ [[package]] name = "lightning-dns-resolver" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "dnssec-prover", "lightning", @@ -1435,8 +1451,8 @@ dependencies = [ [[package]] name = "lightning-invoice" -version = "0.35.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.35.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bech32", "bitcoin", @@ -1446,8 +1462,8 @@ dependencies = [ [[package]] name = "lightning-liquidity" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitreq", @@ -1463,7 +1479,7 @@ dependencies = [ [[package]] name = "lightning-macros" version = "0.2.2+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "proc-macro2", "quote", @@ -1472,8 +1488,8 @@ dependencies = [ [[package]] name = "lightning-net-tokio" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "lightning", @@ -1482,8 +1498,8 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "lightning", @@ -1493,8 +1509,8 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "bitcoin-io", @@ -1504,12 +1520,12 @@ dependencies = [ [[package]] name = "lightning-transaction-sync" -version = "0.3.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.3.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", "electrum-client", - "esplora-client", + "esplora-client 0.13.0", "futures", "lightning", "lightning-macros", @@ -1517,8 +1533,8 @@ dependencies = [ [[package]] name = "lightning-types" -version = "0.4.0+git" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +version = "0.4.0-beta1" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "bitcoin", ] @@ -1674,7 +1690,7 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "possiblyrandom" version = "0.2.0" -source = "git+https://github.com/lightningdevkit/rust-lightning?rev=506cb91f2e0fb87906188b79777bcf42595d3623#506cb91f2e0fb87906188b79777bcf42595d3623" +source = "git+https://github.com/lightningdevkit/rust-lightning?rev=9174965af9437196c527a9aa0df36bbcf050c8bb#9174965af9437196c527a9aa0df36bbcf050c8bb" dependencies = [ "getrandom 0.2.17", ] @@ -1793,7 +1809,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls 0.23.42", - "socket2 0.5.10", + "socket2 0.6.3", "thiserror", "tokio", "tracing", @@ -1830,7 +1846,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] @@ -2089,7 +2105,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2306,7 +2322,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -2408,10 +2424,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/e2e-tests/Cargo.toml b/e2e-tests/Cargo.toml index 461ba244..efba99a4 100644 --- a/e2e-tests/Cargo.toml +++ b/e2e-tests/Cargo.toml @@ -21,4 +21,4 @@ ldk-server-client = { path = "../ldk-server-client" } ldk-server-grpc = { path = "../ldk-server-grpc", features = ["serde"] } serde_json = "1.0" hex-conservative = { version = "0.2", features = ["std"] } -ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "056447c28221be02c3d39f8c6ae430a67ebbd850" } +ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "5e4197b0ac135b92874b7ee43a9b2e2a9f0a6c57" } diff --git a/e2e-tests/tests/e2e.rs b/e2e-tests/tests/e2e.rs index f0ed65ec..6790e6a9 100644 --- a/e2e-tests/tests/e2e.rs +++ b/e2e-tests/tests/e2e.rs @@ -845,14 +845,21 @@ async fn test_cli_bolt11_send() { // Pay via CLI from A let output = run_cli(&server_a, &["bolt11-send", &invoice_resp.invoice]); - assert!(!output["payment_id"].as_str().unwrap().is_empty()); + let send_payment_id = output["payment_id"].as_str().unwrap(); + assert!(!send_payment_id.is_empty()); // Verify events let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; - assert!(matches!(&event_a.event, Some(Event::PaymentSuccessful(_)))); + let Some(Event::PaymentSuccessful(successful)) = &event_a.event else { + panic!("expected PaymentSuccessful"); + }; + assert_eq!(successful.payment.as_ref().unwrap().payment_id, send_payment_id); let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentReceived(_))).await; - assert!(matches!(&event_b.event, Some(Event::PaymentReceived(_)))); + let Some(Event::PaymentReceived(received)) = &event_b.event else { + panic!("expected PaymentReceived"); + }; + assert!(!received.payment.as_ref().unwrap().payment_id.is_empty()); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -968,6 +975,55 @@ async fn test_cli_bolt12_send() { assert!(!output["payment_id"].as_str().unwrap().is_empty()); } +#[tokio::test(flavor = "multi_thread", worker_threads = 1)] +async fn test_cli_bolt12_create_payer_proof() { + let bitcoind = TestBitcoind::new(); + let server_a = LdkServerHandle::start(&bitcoind).await; + let server_b = LdkServerHandle::start(&bitcoind).await; + + let mut events_a = server_a.client().subscribe_events().await.unwrap(); + + setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; + + let offer_resp = server_b + .client() + .bolt12_receive(Bolt12ReceiveRequest { + description: "payer proof offer".to_string(), + amount_msat: Some(10_000_000), + expiry_secs: None, + quantity: None, + }) + .await + .unwrap(); + + let send_output = run_cli(&server_a, &["bolt12-send", &offer_resp.offer]); + let send_payment_id = send_output["payment_id"].as_str().unwrap(); + assert!(!send_payment_id.is_empty()); + + let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; + let Some(Event::PaymentSuccessful(successful)) = &event_a.event else { + panic!("expected PaymentSuccessful"); + }; + assert_eq!(successful.payment_id, send_payment_id); + let payment_preimage = successful.payment_preimage.as_ref().expect("preimage"); + let invoice = successful.bolt12_invoice.as_ref().expect("bolt12 invoice"); + + let proof_output = run_cli( + &server_a, + &[ + "bolt12-create-payer-proof", + send_payment_id, + payment_preimage, + invoice, + "--include-offer-description", + "--include-invoice-amount", + "--note", + "Paid in full", + ], + ); + assert!(!proof_output["payer_proof"].as_str().unwrap().is_empty()); +} + #[tokio::test(flavor = "multi_thread", worker_threads = 1)] async fn test_cli_spontaneous_send() { let bitcoind = TestBitcoind::new(); @@ -980,14 +1036,21 @@ async fn test_cli_spontaneous_send() { setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; let output = run_cli(&server_a, &["spontaneous-send", server_b.node_id(), "10000sat"]); - assert!(!output["payment_id"].as_str().unwrap().is_empty()); + let send_payment_id = output["payment_id"].as_str().unwrap(); + assert!(!send_payment_id.is_empty()); // Verify events let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; - assert!(matches!(&event_a.event, Some(Event::PaymentSuccessful(_)))); + let Some(Event::PaymentSuccessful(successful)) = &event_a.event else { + panic!("expected PaymentSuccessful"); + }; + assert_eq!(successful.payment.as_ref().unwrap().payment_id, send_payment_id); let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentReceived(_))).await; - assert!(matches!(&event_b.event, Some(Event::PaymentReceived(_)))); + let Some(Event::PaymentReceived(received)) = &event_b.event else { + panic!("expected PaymentReceived"); + }; + assert!(!received.payment.as_ref().unwrap().payment_id.is_empty()); } #[tokio::test(flavor = "multi_thread", worker_threads = 1)] @@ -1020,6 +1083,7 @@ async fn test_cli_spontaneous_send_with_custom_tlvs() { let Some(Event::PaymentReceived(pr)) = event_b.event else { panic!("expected PaymentReceived"); }; + assert!(!pr.payment.as_ref().unwrap().payment_id.is_empty()); assert_eq!(pr.custom_records.len(), 2); let by_type: HashMap> = pr.custom_records.into_iter().map(|r| (r.type_num, r.value.to_vec())).collect(); @@ -1055,7 +1119,7 @@ async fn test_cli_get_payment_details() { let output = run_cli(&server_a, &["get-payment-details", payment_id]); assert!(output.get("payment").is_some()); - assert_eq!(output["payment"]["id"], payment_id); + assert_eq!(output["payment"]["payment_id"], payment_id); } #[tokio::test] @@ -1354,15 +1418,11 @@ async fn test_hodl_invoice_claim() { setup_funded_channel(&bitcoind, &server_a, &server_b, 100_000).await; - // Test three claim variants: (preimage, amount, hash) - let test_cases: Vec<([u8; 32], Option<&str>, bool)> = vec![ - ([42u8; 32], Some("10000000msat"), true), // all args - ([44u8; 32], Some("10000000msat"), false), // preimage + amount - ([45u8; 32], None, true), // preimage + hash - ([46u8; 32], None, false), // preimage only - ]; + // Test claiming with and without amount verification. + let test_cases: Vec<([u8; 32], Option<&str>)> = + vec![([42u8; 32], Some("10000000msat")), ([46u8; 32], None)]; - for (preimage_bytes, amount, include_hash) in &test_cases { + for (preimage_bytes, amount) in &test_cases { let preimage_hex = preimage_bytes.to_lower_hex_string(); let payment_hash_hex = sha256::Hash::hash(preimage_bytes).to_byte_array().to_lower_hex_string(); @@ -1388,25 +1448,27 @@ async fn test_hodl_invoice_claim() { // Wait for PaymentClaimable event on B (drain other events) let claimable = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentClaimable(_))).await; - assert!(matches!( - &claimable.event, - Some(Event::PaymentClaimable(event)) if event.claim_deadline.is_some() - )); + let Some(Event::PaymentClaimable(claimable_event)) = &claimable.event else { + panic!("expected PaymentClaimable"); + }; + assert!(claimable_event.claim_deadline.is_some()); + assert!(!claimable_event.payment_id.is_empty()); // Claim the payment on B - let mut args: Vec<&str> = vec!["bolt11-claim-for-hash", &preimage_hex]; + let mut args: Vec<&str> = + vec!["bolt11-claim-for-id", &claimable_event.payment_id, &preimage_hex]; if let Some(amt) = amount { args.extend(["-c", amt]); } - if *include_hash { - args.extend(["-p", &payment_hash_hex]); - } run_cli(&server_b, &args); // Wait for PaymentSuccessful on A after claim (drain other events) let successful = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentSuccessful(_))).await; - assert!(matches!(&successful.event, Some(Event::PaymentSuccessful(_)))); + let Some(Event::PaymentSuccessful(event)) = &successful.event else { + panic!("expected PaymentSuccessful"); + }; + assert!(!event.payment.as_ref().unwrap().payment_id.is_empty()); } } @@ -1446,14 +1508,20 @@ async fn test_hodl_invoice_fail() { // Verify PaymentClaimable event on B let event_b = wait_for_event(&mut events_b, |e| matches!(e, Event::PaymentClaimable(_))).await; - assert!(matches!(&event_b.event, Some(Event::PaymentClaimable(_)))); + let Some(Event::PaymentClaimable(claimable)) = &event_b.event else { + panic!("expected PaymentClaimable"); + }; + assert!(!claimable.payment_id.is_empty()); // Fail the payment on B using CLI - run_cli(&server_b, &["bolt11-fail-for-hash", &payment_hash_hex]); + run_cli(&server_b, &["bolt11-fail-for-id", &claimable.payment_id]); // Verify PaymentFailed on A let event_a = wait_for_event(&mut events_a, |e| matches!(e, Event::PaymentFailed(_))).await; - assert!(matches!(&event_a.event, Some(Event::PaymentFailed(_)))); + let Some(Event::PaymentFailed(failed)) = &event_a.event else { + panic!("expected PaymentFailed"); + }; + assert!(!failed.payment.as_ref().unwrap().payment_id.is_empty()); } #[tokio::test] @@ -1613,8 +1681,8 @@ async fn test_cli_spontaneous_send_with_preimage() { let Some(Event::PaymentReceived(pr)) = event_b.event else { panic!("expected PaymentReceived"); }; - let payment = pr.payment.unwrap(); + assert!(!payment.payment_id.is_empty()); let Some(payment_kind::Kind::Spontaneous(spont)) = payment.kind.unwrap().kind else { panic!("expected spontaneous kind"); diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index 29e3779e..848ffb0d 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -23,31 +23,32 @@ use ldk_server_client::error::LdkServerErrorCode::{ AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError, }; use ldk_server_client::ldk_server_grpc::api::{ - Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, - Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, + Bolt11ClaimForIdRequest, Bolt11ClaimForIdResponse, Bolt11FailForIdRequest, + Bolt11FailForIdResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest, Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse, - Bolt11SendUnderpayingRequest, Bolt11SendUnderpayingResponse, Bolt12ReceiveRequest, - Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse, CloseChannelRequest, - CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse, DecodeInvoiceRequest, - DecodeInvoiceResponse, DecodeOfferRequest, DecodeOfferResponse, DisconnectPeerRequest, - DisconnectPeerResponse, ExportPathfindingScoresRequest, ForceCloseChannelRequest, - ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, - GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse, - GraphGetChannelRequest, GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse, - GraphListChannelsRequest, GraphListChannelsResponse, GraphListNodesRequest, - GraphListNodesResponse, ListChannelsRequest, ListChannelsResponse, - ListForwardedPaymentsRequest, ListPaymentsRequest, ListPeersRequest, ListPeersResponse, - OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, - OpenChannelRequest, OpenChannelResponse, SignMessageRequest, SignMessageResponse, - SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse, SpontaneousSendRequest, - SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, UpdateChannelConfigRequest, - UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse, + Bolt11SendUnderpayingRequest, Bolt11SendUnderpayingResponse, Bolt12CreatePayerProofRequest, + Bolt12CreatePayerProofResponse, Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, + Bolt12SendResponse, CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, + ConnectPeerResponse, DecodeInvoiceRequest, DecodeInvoiceResponse, DecodeOfferRequest, + DecodeOfferResponse, DisconnectPeerRequest, DisconnectPeerResponse, + ExportPathfindingScoresRequest, ForceCloseChannelRequest, ForceCloseChannelResponse, + GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse, + GetPaymentDetailsRequest, GetPaymentDetailsResponse, GraphGetChannelRequest, + GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse, GraphListChannelsRequest, + GraphListChannelsResponse, GraphListNodesRequest, GraphListNodesResponse, ListChannelsRequest, + ListChannelsResponse, ListForwardedPaymentsRequest, ListPaymentsRequest, ListPeersRequest, + ListPeersResponse, OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, + OnchainSendResponse, OpenChannelRequest, OpenChannelResponse, SignMessageRequest, + SignMessageResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse, + SpontaneousSendRequest, SpontaneousSendResponse, UnifiedSendRequest, UnifiedSendResponse, + UpdateChannelConfigRequest, UpdateChannelConfigResponse, VerifySignatureRequest, + VerifySignatureResponse, }; use ldk_server_client::ldk_server_grpc::types::{ bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, CustomTlvRecord, - PageToken, RouteParametersConfig, + PageToken, PayerProofOptions, RouteParametersConfig, }; use ldk_server_client::{ DEFAULT_EXPIRY_SECS, DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF, DEFAULT_MAX_PATH_COUNT, @@ -166,7 +167,9 @@ enum Commands { expiry_secs: Option, }, #[command(about = "Claim a held payment by providing the preimage")] - Bolt11ClaimForHash { + Bolt11ClaimForId { + #[arg(help = "The hex-encoded 32-byte payment ID from PaymentClaimable")] + payment_id: String, #[arg(help = "The hex-encoded 32-byte payment preimage")] preimage: String, #[arg( @@ -175,17 +178,11 @@ enum Commands { help = "The claimable amount, e.g. 50sat or 50000msat, only used for verifying we are claiming the expected amount" )] claimable_amount: Option, - #[arg( - short, - long, - help = "The hex-encoded 32-byte payment hash, used to verify the preimage matches" - )] - payment_hash: Option, }, #[command(about = "Fail/reject a held payment")] - Bolt11FailForHash { - #[arg(help = "The hex-encoded 32-byte payment hash")] - payment_hash: String, + Bolt11FailForId { + #[arg(help = "The hex-encoded 32-byte payment ID from PaymentClaimable")] + payment_id: String, }, #[command(about = "Create a fixed-amount BOLT11 invoice to receive via an LSPS2 JIT channel")] Bolt11ReceiveViaJitChannel { @@ -323,6 +320,27 @@ enum Commands { )] max_channel_saturation_power_of_half: Option, }, + #[command(about = "Create a BOLT 12 payer proof for a payment this node made")] + Bolt12CreatePayerProof { + #[arg(help = "The hex-encoded payment id from PaymentSuccessful")] + payment_id: String, + #[arg(help = "The hex-encoded 32-byte payment preimage from PaymentSuccessful")] + payment_preimage: String, + #[arg(help = "The hex-encoded BOLT 12 invoice from PaymentSuccessful")] + invoice: String, + #[arg(long, help = "Optional note to attach to the payer proof")] + note: Option, + #[arg(long, help = "Disclose the offer description in the proof")] + include_offer_description: bool, + #[arg(long, help = "Disclose the offer issuer in the proof")] + include_offer_issuer: bool, + #[arg(long, help = "Disclose the invoice amount in the proof")] + include_invoice_amount: bool, + #[arg(long, help = "Disclose the invoice creation timestamp in the proof")] + include_invoice_created_at: bool, + #[arg(long, help = "Additional TLV types to disclose")] + extra_tlv_types: Vec, + }, #[command(about = "Send a spontaneous payment (keysend) to a node")] SpontaneousSend { #[arg(help = "The hex-encoded public key of the node to send the payment to")] @@ -722,20 +740,20 @@ async fn main() { client.bolt11_receive_for_hash(request).await, ); }, - Commands::Bolt11ClaimForHash { preimage, claimable_amount, payment_hash } => { - handle_response_result::<_, Bolt11ClaimForHashResponse>( + Commands::Bolt11ClaimForId { payment_id, preimage, claimable_amount } => { + handle_response_result::<_, Bolt11ClaimForIdResponse>( client - .bolt11_claim_for_hash(Bolt11ClaimForHashRequest { - payment_hash, + .bolt11_claim_for_id(Bolt11ClaimForIdRequest { + payment_id, claimable_amount_msat: claimable_amount.map(|a| a.to_msat()), preimage, }) .await, ); }, - Commands::Bolt11FailForHash { payment_hash } => { - handle_response_result::<_, Bolt11FailForHashResponse>( - client.bolt11_fail_for_hash(Bolt11FailForHashRequest { payment_hash }).await, + Commands::Bolt11FailForId { payment_id } => { + handle_response_result::<_, Bolt11FailForIdResponse>( + client.bolt11_fail_for_id(Bolt11FailForIdRequest { payment_id }).await, ); }, Commands::Bolt11ReceiveViaJitChannel { @@ -874,6 +892,36 @@ async fn main() { .await, ); }, + Commands::Bolt12CreatePayerProof { + payment_id, + payment_preimage, + invoice, + note, + include_offer_description, + include_offer_issuer, + include_invoice_amount, + include_invoice_created_at, + extra_tlv_types, + } => { + let options = PayerProofOptions { + note, + include_offer_description, + include_offer_issuer, + include_invoice_amount, + include_invoice_created_at, + extra_tlv_types, + }; + handle_response_result::<_, Bolt12CreatePayerProofResponse>( + client + .bolt12_create_payer_proof(Bolt12CreatePayerProofRequest { + payment_id, + payment_preimage, + invoice, + options: Some(options), + }) + .await, + ); + }, Commands::SpontaneousSend { node_id, amount, diff --git a/ldk-server-client/src/client.rs b/ldk-server-client/src/client.rs index 6d0d50d0..afa1b253 100644 --- a/ldk-server-client/src/client.rs +++ b/ldk-server-client/src/client.rs @@ -16,18 +16,19 @@ use hyper::body::HttpBody as _; use hyper::{Body as HyperBody, Client as HyperClient, Request as HyperRequest, Version}; use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; use ldk_server_grpc::api::{ - Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse, Bolt11FailForHashRequest, - Bolt11FailForHashResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, + Bolt11ClaimForIdRequest, Bolt11ClaimForIdResponse, Bolt11FailForIdRequest, + Bolt11FailForIdResponse, Bolt11ReceiveForHashRequest, Bolt11ReceiveForHashResponse, Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest, Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest, Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse, - Bolt11SendUnderpayingRequest, Bolt11SendUnderpayingResponse, Bolt12ReceiveRequest, - Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse, CloseChannelRequest, - CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse, DecodeInvoiceRequest, - DecodeInvoiceResponse, DecodeOfferRequest, DecodeOfferResponse, DisconnectPeerRequest, - DisconnectPeerResponse, ExportPathfindingScoresRequest, ExportPathfindingScoresResponse, - ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse, - GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse, + Bolt11SendUnderpayingRequest, Bolt11SendUnderpayingResponse, Bolt12CreatePayerProofRequest, + Bolt12CreatePayerProofResponse, Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, + Bolt12SendResponse, CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, + ConnectPeerResponse, DecodeInvoiceRequest, DecodeInvoiceResponse, DecodeOfferRequest, + DecodeOfferResponse, DisconnectPeerRequest, DisconnectPeerResponse, + ExportPathfindingScoresRequest, ExportPathfindingScoresResponse, ForceCloseChannelRequest, + ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, + GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse, GraphGetChannelRequest, GraphGetChannelResponse, GraphGetNodeRequest, GraphGetNodeResponse, GraphListChannelsRequest, GraphListChannelsResponse, GraphListNodesRequest, GraphListNodesResponse, ListChannelsRequest, ListChannelsResponse, @@ -41,13 +42,13 @@ use ldk_server_grpc::api::{ VerifySignatureResponse, }; use ldk_server_grpc::endpoints::{ - BOLT11_CLAIM_FOR_HASH_PATH, BOLT11_FAIL_FOR_HASH_PATH, BOLT11_RECEIVE_FOR_HASH_PATH, + BOLT11_CLAIM_FOR_ID_PATH, BOLT11_FAIL_FOR_ID_PATH, BOLT11_RECEIVE_FOR_HASH_PATH, BOLT11_RECEIVE_PATH, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH, BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT11_SEND_UNDERPAYING_PATH, - BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, - DECODE_INVOICE_PATH, DECODE_OFFER_PATH, DISCONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH, - FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_METRICS_PATH, GET_NODE_INFO_PATH, - GET_PAYMENT_DETAILS_PATH, GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, + BOLT12_CREATE_PAYER_PROOF_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, CLOSE_CHANNEL_PATH, + CONNECT_PEER_PATH, DECODE_INVOICE_PATH, DECODE_OFFER_PATH, DISCONNECT_PEER_PATH, + EXPORT_PATHFINDING_SCORES_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_METRICS_PATH, + GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, GRPC_SERVICE_PREFIX, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH, @@ -209,18 +210,18 @@ impl LdkServerClient { self.grpc_unary(&request, BOLT11_RECEIVE_FOR_HASH_PATH).await } - /// Manually claim a payment for a given payment hash. - pub async fn bolt11_claim_for_hash( - &self, request: Bolt11ClaimForHashRequest, - ) -> Result { - self.grpc_unary(&request, BOLT11_CLAIM_FOR_HASH_PATH).await + /// Manually claim a payment for a given payment ID. + pub async fn bolt11_claim_for_id( + &self, request: Bolt11ClaimForIdRequest, + ) -> Result { + self.grpc_unary(&request, BOLT11_CLAIM_FOR_ID_PATH).await } - /// Manually fail a payment for a given payment hash. - pub async fn bolt11_fail_for_hash( - &self, request: Bolt11FailForHashRequest, - ) -> Result { - self.grpc_unary(&request, BOLT11_FAIL_FOR_HASH_PATH).await + /// Manually fail a payment for a given payment ID. + pub async fn bolt11_fail_for_id( + &self, request: Bolt11FailForIdRequest, + ) -> Result { + self.grpc_unary(&request, BOLT11_FAIL_FOR_ID_PATH).await } /// Retrieve a new fixed-amount BOLT11 invoice for receiving via an LSPS2 JIT channel. @@ -269,6 +270,13 @@ impl LdkServerClient { self.grpc_unary(&request, BOLT12_SEND_PATH).await } + /// Create a BOLT 12 payer proof for a payment this node made. + pub async fn bolt12_create_payer_proof( + &self, request: Bolt12CreatePayerProofRequest, + ) -> Result { + self.grpc_unary(&request, BOLT12_CREATE_PAYER_PROOF_PATH).await + } + /// Creates a new outbound channel. pub async fn open_channel( &self, request: OpenChannelRequest, diff --git a/ldk-server-grpc/src/api.rs b/ldk-server-grpc/src/api.rs index ade887f4..8f026fef 100644 --- a/ldk-server-grpc/src/api.rs +++ b/ldk-server-grpc/src/api.rs @@ -191,8 +191,8 @@ pub struct Bolt11ReceiveResponse { } /// Return a BOLT11 payable invoice for a given payment hash. /// The inbound payment will NOT be automatically claimed upon arrival. -/// Instead, the payment will need to be manually claimed by calling `Bolt11ClaimForHash` -/// or manually failed by calling `Bolt11FailForHash`. +/// Instead, the payment will need to be manually claimed by calling `Bolt11ClaimForId` +/// or manually failed by calling `Bolt11FailForId`. /// See more: /// - /// - @@ -229,19 +229,18 @@ pub struct Bolt11ReceiveForHashResponse { #[prost(string, tag = "1")] pub invoice: ::prost::alloc::string::String, } -/// Manually claim a payment for a given payment hash with the corresponding preimage. +/// Manually claim a payment for a given payment ID with the corresponding preimage. /// This should be used to claim payments created via `Bolt11ReceiveForHash`. -/// See more: +/// See more: #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(default))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11ClaimForHashRequest { - /// The hex-encoded 32-byte payment hash. - /// If provided, it will be used to verify that the preimage matches. - #[prost(string, optional, tag = "1")] - pub payment_hash: ::core::option::Option<::prost::alloc::string::String>, +pub struct Bolt11ClaimForIdRequest { + /// The hex-encoded 32-byte payment ID from `PaymentClaimable`. + #[prost(string, tag = "1")] + pub payment_id: ::prost::alloc::string::String, /// The amount in millisatoshi that is claimable. /// If not provided, skips amount verification. #[prost(uint64, optional, tag = "2")] @@ -250,33 +249,33 @@ pub struct Bolt11ClaimForHashRequest { #[prost(string, tag = "3")] pub preimage: ::prost::alloc::string::String, } -/// The response for the `Bolt11ClaimForHash` RPC. On failure, a gRPC error status is returned. +/// The response for the `Bolt11ClaimForId` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(default))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11ClaimForHashResponse {} -/// Manually fail a payment for a given payment hash. +pub struct Bolt11ClaimForIdResponse {} +/// Manually fail a payment for a given payment ID. /// This should be used to reject payments created via `Bolt11ReceiveForHash`. -/// See more: +/// See more: #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(default))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11FailForHashRequest { - /// The hex-encoded 32-byte payment hash. +pub struct Bolt11FailForIdRequest { + /// The hex-encoded 32-byte payment ID from `PaymentClaimable`. #[prost(string, tag = "1")] - pub payment_hash: ::prost::alloc::string::String, + pub payment_id: ::prost::alloc::string::String, } -/// The response for the `Bolt11FailForHash` RPC. On failure, a gRPC error status is returned. +/// The response for the `Bolt11FailForId` RPC. On failure, a gRPC error status is returned. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(default))] #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct Bolt11FailForHashResponse {} +pub struct Bolt11FailForIdResponse {} /// Return a BOLT11 payable invoice that can be used to request and receive a payment via an /// LSPS2 just-in-time channel. /// See more: @@ -485,6 +484,40 @@ pub struct Bolt12SendResponse { #[prost(string, tag = "1")] pub payment_id: ::prost::alloc::string::String, } +/// Create a BOLT 12 payer proof for a payment this node made. +/// Inputs come from `PaymentSuccessful`: `payment_id`, `payment_preimage`, and `bolt12_invoice`. +/// See more: +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[cfg_attr(feature = "serde", serde(default))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Bolt12CreatePayerProofRequest { + /// The local identifier used to track the payment, in hex-encoded form. + #[prost(string, tag = "1")] + pub payment_id: ::prost::alloc::string::String, + /// The hex-encoded 32-byte payment preimage from `PaymentSuccessful`. + #[prost(string, tag = "2")] + pub payment_preimage: ::prost::alloc::string::String, + /// The hex-encoded BOLT 12 invoice from `PaymentSuccessful.bolt12_invoice`. + /// Static invoices used for async payments cannot be proven. + #[prost(string, tag = "3")] + pub invoice: ::prost::alloc::string::String, + /// Controls which optional invoice fields the proof discloses. + #[prost(message, optional, tag = "4")] + pub options: ::core::option::Option, +} +/// The response for the `Bolt12CreatePayerProof` RPC. On failure, a gRPC error status is returned. +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[cfg_attr(feature = "serde", serde(default))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Bolt12CreatePayerProofResponse { + /// The bech32-encoded payer proof. + #[prost(string, tag = "1")] + pub payer_proof: ::prost::alloc::string::String, +} /// Send a spontaneous payment, also known as "keysend", to a node. /// See more: #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] diff --git a/ldk-server-grpc/src/endpoints.rs b/ldk-server-grpc/src/endpoints.rs index cc4492be..b482cbcf 100644 --- a/ldk-server-grpc/src/endpoints.rs +++ b/ldk-server-grpc/src/endpoints.rs @@ -16,8 +16,8 @@ pub const ONCHAIN_RECEIVE_PATH: &str = "OnchainReceive"; pub const ONCHAIN_SEND_PATH: &str = "OnchainSend"; pub const BOLT11_RECEIVE_PATH: &str = "Bolt11Receive"; pub const BOLT11_RECEIVE_FOR_HASH_PATH: &str = "Bolt11ReceiveForHash"; -pub const BOLT11_CLAIM_FOR_HASH_PATH: &str = "Bolt11ClaimForHash"; -pub const BOLT11_FAIL_FOR_HASH_PATH: &str = "Bolt11FailForHash"; +pub const BOLT11_CLAIM_FOR_ID_PATH: &str = "Bolt11ClaimForId"; +pub const BOLT11_FAIL_FOR_ID_PATH: &str = "Bolt11FailForId"; pub const BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH: &str = "Bolt11ReceiveViaJitChannel"; pub const BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH: &str = "Bolt11ReceiveVariableAmountViaJitChannel"; @@ -25,6 +25,7 @@ pub const BOLT11_SEND_PATH: &str = "Bolt11Send"; pub const BOLT11_SEND_UNDERPAYING_PATH: &str = "Bolt11SendUnderpaying"; pub const BOLT12_RECEIVE_PATH: &str = "Bolt12Receive"; pub const BOLT12_SEND_PATH: &str = "Bolt12Send"; +pub const BOLT12_CREATE_PAYER_PROOF_PATH: &str = "Bolt12CreatePayerProof"; pub const OPEN_CHANNEL_PATH: &str = "OpenChannel"; pub const SPLICE_IN_PATH: &str = "SpliceIn"; pub const SPLICE_OUT_PATH: &str = "SpliceOut"; diff --git a/ldk-server-grpc/src/events.rs b/ldk-server-grpc/src/events.rs index f13a3186..7fba64e7 100644 --- a/ldk-server-grpc/src/events.rs +++ b/ldk-server-grpc/src/events.rs @@ -147,11 +147,14 @@ pub struct ChannelStateChanged { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaymentReceived { + /// The local identifier used to track the payment, in hex-encoded form. + #[prost(string, tag = "1")] + pub payment_id: ::prost::alloc::string::String, /// The payment details for the payment in event. - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag = "2")] pub payment: ::core::option::Option, /// Custom TLV records attached to the incoming payment, if any. - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag = "3")] pub custom_records: ::prost::alloc::vec::Vec, } /// PaymentSuccessful indicates a sent payment was successful. @@ -161,9 +164,19 @@ pub struct PaymentReceived { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaymentSuccessful { + /// The local identifier used to track the payment, in hex-encoded form. + #[prost(string, tag = "1")] + pub payment_id: ::prost::alloc::string::String, /// The payment details for the payment in event. - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag = "2")] pub payment: ::core::option::Option, + /// The hex-encoded payment preimage. Needed to build a BOLT 12 payer proof. + #[prost(string, optional, tag = "3")] + pub payment_preimage: ::core::option::Option<::prost::alloc::string::String>, + /// The hex-encoded paid BOLT 12 invoice, when the payment was for a standard BOLT 12 invoice. + /// Unset for non-BOLT12 payments and for static invoices used in async payments. + #[prost(string, optional, tag = "4")] + pub bolt12_invoice: ::core::option::Option<::prost::alloc::string::String>, } /// PaymentFailed indicates a sent payment has failed. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] @@ -172,8 +185,11 @@ pub struct PaymentSuccessful { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaymentFailed { + /// The local identifier used to track the payment, in hex-encoded form. + #[prost(string, tag = "1")] + pub payment_id: ::prost::alloc::string::String, /// The payment details for the payment in event. - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag = "2")] pub payment: ::core::option::Option, } /// PaymentClaimable indicates a payment has arrived and is waiting to be manually claimed or failed. @@ -184,14 +200,17 @@ pub struct PaymentFailed { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PaymentClaimable { + /// The local identifier used to track the payment, in hex-encoded form. + #[prost(string, tag = "1")] + pub payment_id: ::prost::alloc::string::String, /// The payment details for the claimable payment. - #[prost(message, optional, tag = "1")] + #[prost(message, optional, tag = "2")] pub payment: ::core::option::Option, /// Custom TLV records attached to the claimable payment, if any. - #[prost(message, repeated, tag = "2")] + #[prost(message, repeated, tag = "3")] pub custom_records: ::prost::alloc::vec::Vec, /// The block height by which this payment must be claimed before it is failed back. - #[prost(uint32, optional, tag = "3")] + #[prost(uint32, optional, tag = "4")] pub claim_deadline: ::core::option::Option, } /// PaymentForwarded indicates a payment was forwarded through the node. diff --git a/ldk-server-grpc/src/proto/api.proto b/ldk-server-grpc/src/proto/api.proto index df5f22cc..35f4d9ff 100644 --- a/ldk-server-grpc/src/proto/api.proto +++ b/ldk-server-grpc/src/proto/api.proto @@ -157,8 +157,8 @@ message Bolt11ReceiveResponse { // Return a BOLT11 payable invoice for a given payment hash. // The inbound payment will NOT be automatically claimed upon arrival. -// Instead, the payment will need to be manually claimed by calling `Bolt11ClaimForHash` -// or manually failed by calling `Bolt11FailForHash`. +// Instead, the payment will need to be manually claimed by calling `Bolt11ClaimForId` +// or manually failed by calling `Bolt11FailForId`. // See more: // - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_for_hash // - https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.receive_variable_amount_for_hash @@ -187,14 +187,13 @@ message Bolt11ReceiveForHashResponse { string invoice = 1; } -// Manually claim a payment for a given payment hash with the corresponding preimage. +// Manually claim a payment for a given payment ID with the corresponding preimage. // This should be used to claim payments created via `Bolt11ReceiveForHash`. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.claim_for_hash -message Bolt11ClaimForHashRequest { +// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.claim_for_id +message Bolt11ClaimForIdRequest { - // The hex-encoded 32-byte payment hash. - // If provided, it will be used to verify that the preimage matches. - optional string payment_hash = 1; + // The hex-encoded 32-byte payment ID from `PaymentClaimable`. + string payment_id = 1; // The amount in millisatoshi that is claimable. // If not provided, skips amount verification. @@ -204,20 +203,20 @@ message Bolt11ClaimForHashRequest { string preimage = 3; } -// The response for the `Bolt11ClaimForHash` RPC. On failure, a gRPC error status is returned. -message Bolt11ClaimForHashResponse {} +// The response for the `Bolt11ClaimForId` RPC. On failure, a gRPC error status is returned. +message Bolt11ClaimForIdResponse {} -// Manually fail a payment for a given payment hash. +// Manually fail a payment for a given payment ID. // This should be used to reject payments created via `Bolt11ReceiveForHash`. -// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.fail_for_hash -message Bolt11FailForHashRequest { +// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt11Payment.html#method.fail_for_id +message Bolt11FailForIdRequest { - // The hex-encoded 32-byte payment hash. - string payment_hash = 1; + // The hex-encoded 32-byte payment ID from `PaymentClaimable`. + string payment_id = 1; } -// The response for the `Bolt11FailForHash` RPC. On failure, a gRPC error status is returned. -message Bolt11FailForHashResponse {} +// The response for the `Bolt11FailForId` RPC. On failure, a gRPC error status is returned. +message Bolt11FailForIdResponse {} // Return a BOLT11 payable invoice that can be used to request and receive a payment via an // LSPS2 just-in-time channel. @@ -382,6 +381,30 @@ message Bolt12SendResponse { string payment_id = 1; } +// Create a BOLT 12 payer proof for a payment this node made. +// Inputs come from `PaymentSuccessful`: `payment_id`, `payment_preimage`, and `bolt12_invoice`. +// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.Bolt12Payment.html#method.create_payer_proof +message Bolt12CreatePayerProofRequest { + // The local identifier used to track the payment, in hex-encoded form. + string payment_id = 1; + + // The hex-encoded 32-byte payment preimage from `PaymentSuccessful`. + string payment_preimage = 2; + + // The hex-encoded BOLT 12 invoice from `PaymentSuccessful.bolt12_invoice`. + // Static invoices used for async payments cannot be proven. + string invoice = 3; + + // Controls which optional invoice fields the proof discloses. + optional types.PayerProofOptions options = 4; +} + +// The response for the `Bolt12CreatePayerProof` RPC. On failure, a gRPC error status is returned. +message Bolt12CreatePayerProofResponse { + // The bech32-encoded payer proof. + string payer_proof = 1; +} + // Send a spontaneous payment, also known as "keysend", to a node. // See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.SpontaneousPayment.html#method.send message SpontaneousSendRequest { @@ -944,10 +967,10 @@ service LightningNode { rpc Bolt11Receive(Bolt11ReceiveRequest) returns (Bolt11ReceiveResponse); // Return a BOLT11 payable invoice for a given payment hash. rpc Bolt11ReceiveForHash(Bolt11ReceiveForHashRequest) returns (Bolt11ReceiveForHashResponse); - // Manually claim a payment for a given payment hash. - rpc Bolt11ClaimForHash(Bolt11ClaimForHashRequest) returns (Bolt11ClaimForHashResponse); - // Manually fail a payment for a given payment hash. - rpc Bolt11FailForHash(Bolt11FailForHashRequest) returns (Bolt11FailForHashResponse); + // Manually claim a payment for a given payment ID. + rpc Bolt11ClaimForId(Bolt11ClaimForIdRequest) returns (Bolt11ClaimForIdResponse); + // Manually fail a payment for a given payment ID. + rpc Bolt11FailForId(Bolt11FailForIdRequest) returns (Bolt11FailForIdResponse); // Return a BOLT11 invoice for receiving via a JIT channel. rpc Bolt11ReceiveViaJitChannel(Bolt11ReceiveViaJitChannelRequest) returns (Bolt11ReceiveViaJitChannelResponse); // Return a variable-amount BOLT11 invoice for receiving via a JIT channel. @@ -961,6 +984,8 @@ service LightningNode { rpc Bolt12Receive(Bolt12ReceiveRequest) returns (Bolt12ReceiveResponse); // Send a payment for a BOLT12 offer. rpc Bolt12Send(Bolt12SendRequest) returns (Bolt12SendResponse); + // Create a BOLT 12 payer proof for a payment this node made. + rpc Bolt12CreatePayerProof(Bolt12CreatePayerProofRequest) returns (Bolt12CreatePayerProofResponse); // Send a spontaneous payment (keysend). rpc SpontaneousSend(SpontaneousSendRequest) returns (SpontaneousSendResponse); // Create a new outbound channel. diff --git a/ldk-server-grpc/src/proto/events.proto b/ldk-server-grpc/src/proto/events.proto index 6f593088..bcd589de 100644 --- a/ldk-server-grpc/src/proto/events.proto +++ b/ldk-server-grpc/src/proto/events.proto @@ -94,33 +94,46 @@ message ChannelStateChanged { // PaymentReceived indicates a payment has been received. message PaymentReceived { + // The local identifier used to track the payment, in hex-encoded form. + string payment_id = 1; // The payment details for the payment in event. - types.Payment payment = 1; + types.Payment payment = 2; // Custom TLV records attached to the incoming payment, if any. - repeated types.CustomTlvRecord custom_records = 2; + repeated types.CustomTlvRecord custom_records = 3; } // PaymentSuccessful indicates a sent payment was successful. message PaymentSuccessful { + // The local identifier used to track the payment, in hex-encoded form. + string payment_id = 1; // The payment details for the payment in event. - types.Payment payment = 1; + types.Payment payment = 2; + // The hex-encoded payment preimage. Needed to build a BOLT 12 payer proof. + optional string payment_preimage = 3; + // The hex-encoded paid BOLT 12 invoice, when the payment was for a standard BOLT 12 invoice. + // Unset for non-BOLT12 payments and for static invoices used in async payments. + optional string bolt12_invoice = 4; } // PaymentFailed indicates a sent payment has failed. message PaymentFailed { + // The local identifier used to track the payment, in hex-encoded form. + string payment_id = 1; // The payment details for the payment in event. - types.Payment payment = 1; + types.Payment payment = 2; } // PaymentClaimable indicates a payment has arrived and is waiting to be manually claimed or failed. // This event is only emitted for payments created via `Bolt11ReceiveForHash`. message PaymentClaimable { + // The local identifier used to track the payment, in hex-encoded form. + string payment_id = 1; // The payment details for the claimable payment. - types.Payment payment = 1; + types.Payment payment = 2; // Custom TLV records attached to the claimable payment, if any. - repeated types.CustomTlvRecord custom_records = 2; + repeated types.CustomTlvRecord custom_records = 3; // The block height by which this payment must be claimed before it is failed back. - optional uint32 claim_deadline = 3; + optional uint32 claim_deadline = 4; } // PaymentForwarded indicates a payment was forwarded through the node. diff --git a/ldk-server-grpc/src/proto/types.proto b/ldk-server-grpc/src/proto/types.proto index 654a3a80..3106c2b6 100644 --- a/ldk-server-grpc/src/proto/types.proto +++ b/ldk-server-grpc/src/proto/types.proto @@ -5,7 +5,7 @@ package types; // See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.PaymentDetails.html message Payment { // An identifier used to uniquely identify a payment in hex-encoded form. - string id = 1; + string payment_id = 1; // The kind of the payment. PaymentKind kind = 2; @@ -29,6 +29,28 @@ message Payment { uint64 latest_update_timestamp = 6; } +// Options that control which BOLT 12 invoice fields a payer proof discloses. +// See more: https://docs.rs/ldk-node/latest/ldk_node/payment/struct.PayerProofOptions.html +message PayerProofOptions { + // An optional note to attach to the payer proof itself. + optional string note = 1; + + // Whether to disclose the offer description. + bool include_offer_description = 2; + + // Whether to disclose the offer issuer. + bool include_offer_issuer = 3; + + // Whether to disclose the invoice amount. + bool include_invoice_amount = 4; + + // Whether to disclose the invoice creation timestamp. + bool include_invoice_created_at = 5; + + // Additional TLV types to disclose, for fields not covered by the flags above. + repeated uint64 extra_tlv_types = 6; +} + message PaymentKind { oneof kind { Onchain onchain = 1; diff --git a/ldk-server-grpc/src/types.rs b/ldk-server-grpc/src/types.rs index 1240cb46..b8c93a1a 100644 --- a/ldk-server-grpc/src/types.rs +++ b/ldk-server-grpc/src/types.rs @@ -17,7 +17,7 @@ pub struct Payment { /// An identifier used to uniquely identify a payment in hex-encoded form. #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, + pub payment_id: ::prost::alloc::string::String, /// The kind of the payment. #[prost(message, optional, tag = "2")] pub kind: ::core::option::Option, @@ -48,6 +48,33 @@ pub struct Payment { #[prost(uint64, tag = "6")] pub latest_update_timestamp: u64, } +/// Options that control which BOLT 12 invoice fields a payer proof discloses. +/// See more: +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] +#[cfg_attr(feature = "serde", serde(default))] +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PayerProofOptions { + /// An optional note to attach to the payer proof itself. + #[prost(string, optional, tag = "1")] + pub note: ::core::option::Option<::prost::alloc::string::String>, + /// Whether to disclose the offer description. + #[prost(bool, tag = "2")] + pub include_offer_description: bool, + /// Whether to disclose the offer issuer. + #[prost(bool, tag = "3")] + pub include_offer_issuer: bool, + /// Whether to disclose the invoice amount. + #[prost(bool, tag = "4")] + pub include_invoice_amount: bool, + /// Whether to disclose the invoice creation timestamp. + #[prost(bool, tag = "5")] + pub include_invoice_created_at: bool, + /// Additional TLV types to disclose, for fields not covered by the flags above. + #[prost(uint64, repeated, tag = "6")] + pub extra_tlv_types: ::prost::alloc::vec::Vec, +} #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] #[cfg_attr(feature = "serde", serde(default))] diff --git a/ldk-server-mcp/src/tools/handlers.rs b/ldk-server-mcp/src/tools/handlers.rs index 6557fbdf..75b66842 100644 --- a/ldk-server-mcp/src/tools/handlers.rs +++ b/ldk-server-mcp/src/tools/handlers.rs @@ -10,11 +10,11 @@ use hex_conservative::DisplayHex; use ldk_server_client::client::LdkServerClient; use ldk_server_client::ldk_server_grpc::api::{ - Bolt11ClaimForHashRequest, Bolt11FailForHashRequest, Bolt11ReceiveForHashRequest, + Bolt11ClaimForIdRequest, Bolt11FailForIdRequest, Bolt11ReceiveForHashRequest, Bolt11ReceiveRequest, Bolt11ReceiveVariableAmountViaJitChannelRequest, Bolt11ReceiveViaJitChannelRequest, Bolt11SendRequest, Bolt11SendUnderpayingRequest, - Bolt12ReceiveRequest, Bolt12SendRequest, CloseChannelRequest, ConnectPeerRequest, - DecodeInvoiceRequest, DecodeOfferRequest, DisconnectPeerRequest, + Bolt12CreatePayerProofRequest, Bolt12ReceiveRequest, Bolt12SendRequest, CloseChannelRequest, + ConnectPeerRequest, DecodeInvoiceRequest, DecodeOfferRequest, DisconnectPeerRequest, ExportPathfindingScoresRequest, ForceCloseChannelRequest, GetBalancesRequest, GetNodeInfoRequest, GetPaymentDetailsRequest, GraphGetChannelRequest, GraphGetNodeRequest, GraphListChannelsRequest, GraphListNodesRequest, ListChannelsRequest, @@ -142,19 +142,19 @@ pub async fn handle_bolt11_receive_for_hash( serialize_response(response) } -pub async fn handle_bolt11_claim_for_hash( +pub async fn handle_bolt11_claim_for_id( client: &LdkServerClient, args: Value, ) -> Result { - let request: Bolt11ClaimForHashRequest = parse_request(args)?; - let response = client.bolt11_claim_for_hash(request).await.map_err(McpError::from)?; + let request: Bolt11ClaimForIdRequest = parse_request(args)?; + let response = client.bolt11_claim_for_id(request).await.map_err(McpError::from)?; serialize_response(response) } -pub async fn handle_bolt11_fail_for_hash( +pub async fn handle_bolt11_fail_for_id( client: &LdkServerClient, args: Value, ) -> Result { - let request: Bolt11FailForHashRequest = parse_request(args)?; - let response = client.bolt11_fail_for_hash(request).await.map_err(McpError::from)?; + let request: Bolt11FailForIdRequest = parse_request(args)?; + let response = client.bolt11_fail_for_id(request).await.map_err(McpError::from)?; serialize_response(response) } @@ -220,6 +220,14 @@ pub async fn handle_bolt12_send(client: &LdkServerClient, args: Value) -> Result serialize_response(response) } +pub async fn handle_bolt12_create_payer_proof( + client: &LdkServerClient, args: Value, +) -> Result { + let request: Bolt12CreatePayerProofRequest = parse_request(args)?; + let response = client.bolt12_create_payer_proof(request).await.map_err(McpError::from)?; + serialize_response(response) +} + pub async fn handle_spontaneous_send( client: &LdkServerClient, args: Value, ) -> Result { diff --git a/ldk-server-mcp/src/tools/mod.rs b/ldk-server-mcp/src/tools/mod.rs index b86b159d..a675df72 100644 --- a/ldk-server-mcp/src/tools/mod.rs +++ b/ldk-server-mcp/src/tools/mod.rs @@ -106,16 +106,16 @@ pub fn build_tool_registry() -> ToolRegistry { |client, args| Box::pin(handlers::handle_bolt11_receive_for_hash(client, args)), ), tool_spec( - "bolt11_claim_for_hash", - "Manually claim a BOLT11 payment for a specific payment hash", - schema::bolt11_claim_for_hash_schema, - |client, args| Box::pin(handlers::handle_bolt11_claim_for_hash(client, args)), + "bolt11_claim_for_id", + "Manually claim a BOLT11 payment for a specific payment ID", + schema::bolt11_claim_for_id_schema, + |client, args| Box::pin(handlers::handle_bolt11_claim_for_id(client, args)), ), tool_spec( - "bolt11_fail_for_hash", - "Manually fail a BOLT11 payment for a specific payment hash", - schema::bolt11_fail_for_hash_schema, - |client, args| Box::pin(handlers::handle_bolt11_fail_for_hash(client, args)), + "bolt11_fail_for_id", + "Manually fail a BOLT11 payment for a specific payment ID", + schema::bolt11_fail_for_id_schema, + |client, args| Box::pin(handlers::handle_bolt11_fail_for_id(client, args)), ), tool_spec( "bolt11_receive_via_jit_channel", @@ -157,6 +157,12 @@ pub fn build_tool_registry() -> ToolRegistry { schema::bolt12_send_schema, |client, args| Box::pin(handlers::handle_bolt12_send(client, args)), ), + tool_spec( + "bolt12_create_payer_proof", + "Create a BOLT12 payer proof for a payment this node made", + schema::bolt12_create_payer_proof_schema, + |client, args| Box::pin(handlers::handle_bolt12_create_payer_proof(client, args)), + ), tool_spec( "spontaneous_send", "Send a spontaneous (keysend) payment to a Lightning node", diff --git a/ldk-server-mcp/src/tools/schema.rs b/ldk-server-mcp/src/tools/schema.rs index 46c372f9..461c174d 100644 --- a/ldk-server-mcp/src/tools/schema.rs +++ b/ldk-server-mcp/src/tools/schema.rs @@ -219,13 +219,13 @@ pub fn bolt11_receive_for_hash_schema() -> Value { }) } -pub fn bolt11_claim_for_hash_schema() -> Value { +pub fn bolt11_claim_for_id_schema() -> Value { json!({ "type": "object", "properties": { - "payment_hash": { + "payment_id": { "type": "string", - "description": "The hex-encoded 32-byte payment hash. If provided, verifies that the preimage matches" + "description": "The hex-encoded 32-byte payment ID from PaymentClaimable" }, "claimable_amount_msat": { "type": "integer", @@ -236,20 +236,20 @@ pub fn bolt11_claim_for_hash_schema() -> Value { "description": "The hex-encoded 32-byte payment preimage" } }, - "required": ["preimage"] + "required": ["payment_id", "preimage"] }) } -pub fn bolt11_fail_for_hash_schema() -> Value { +pub fn bolt11_fail_for_id_schema() -> Value { json!({ "type": "object", "properties": { - "payment_hash": { + "payment_id": { "type": "string", - "description": "The hex-encoded 32-byte payment hash" + "description": "The hex-encoded 32-byte payment ID from PaymentClaimable" } }, - "required": ["payment_hash"] + "required": ["payment_id"] }) } @@ -380,6 +380,58 @@ pub fn bolt12_send_schema() -> Value { }) } +pub fn bolt12_create_payer_proof_schema() -> Value { + json!({ + "type": "object", + "properties": { + "payment_id": { + "type": "string", + "description": "The hex-encoded payment id from PaymentSuccessful" + }, + "payment_preimage": { + "type": "string", + "description": "The hex-encoded 32-byte payment preimage from PaymentSuccessful" + }, + "invoice": { + "type": "string", + "description": "The hex-encoded BOLT 12 invoice from PaymentSuccessful" + }, + "options": { + "type": "object", + "description": "Controls which optional invoice fields the proof discloses", + "properties": { + "note": { + "type": "string", + "description": "Optional note to attach to the payer proof" + }, + "include_offer_description": { + "type": "boolean", + "description": "Disclose the offer description" + }, + "include_offer_issuer": { + "type": "boolean", + "description": "Disclose the offer issuer" + }, + "include_invoice_amount": { + "type": "boolean", + "description": "Disclose the invoice amount" + }, + "include_invoice_created_at": { + "type": "boolean", + "description": "Disclose the invoice creation timestamp" + }, + "extra_tlv_types": { + "type": "array", + "items": { "type": "integer" }, + "description": "Additional TLV types to disclose" + } + } + } + }, + "required": ["payment_id", "payment_preimage", "invoice"] + }) +} + pub fn spontaneous_send_schema() -> Value { json!({ "type": "object", diff --git a/ldk-server-mcp/tests/integration.rs b/ldk-server-mcp/tests/integration.rs index 5b636285..f093f98c 100644 --- a/ldk-server-mcp/tests/integration.rs +++ b/ldk-server-mcp/tests/integration.rs @@ -11,16 +11,17 @@ use std::io::{BufRead, BufReader, Write}; use serde_json::{json, Value}; -const NUM_TOOLS: usize = 38; +const NUM_TOOLS: usize = 39; const EXPECTED_TOOLS: [&str; NUM_TOOLS] = [ - "bolt11_claim_for_hash", - "bolt11_fail_for_hash", + "bolt11_claim_for_id", + "bolt11_fail_for_id", "bolt11_receive", "bolt11_receive_for_hash", "bolt11_receive_variable_amount_via_jit_channel", "bolt11_receive_via_jit_channel", "bolt11_send", "bolt11_send_underpaying", + "bolt12_create_payer_proof", "bolt12_receive", "bolt12_send", "close_channel", @@ -294,19 +295,19 @@ fn test_bolt11_receive_for_hash_unreachable() { } #[test] -fn test_bolt11_claim_for_hash_unreachable() { +fn test_bolt11_claim_for_id_unreachable() { assert_unreachable_tool( - "bolt11_claim_for_hash", + "bolt11_claim_for_id", json!({ - "payment_hash": "11".repeat(32), + "payment_id": "11".repeat(32), "preimage": "22".repeat(32) }), ); } #[test] -fn test_bolt11_fail_for_hash_unreachable() { - assert_unreachable_tool("bolt11_fail_for_hash", json!({ "payment_hash": "33".repeat(32) })); +fn test_bolt11_fail_for_id_unreachable() { + assert_unreachable_tool("bolt11_fail_for_id", json!({ "payment_id": "33".repeat(32) })); } #[test] diff --git a/ldk-server/Cargo.toml b/ldk-server/Cargo.toml index 2a9783d0..7096b985 100644 --- a/ldk-server/Cargo.toml +++ b/ldk-server/Cargo.toml @@ -13,7 +13,7 @@ keywords = ["bitcoin", "lightning", "ldk", "server"] categories = ["cryptography::cryptocurrencies"] [dependencies] -ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "056447c28221be02c3d39f8c6ae430a67ebbd850" } +ldk-node = { git = "https://github.com/lightningdevkit/ldk-node", rev = "5e4197b0ac135b92874b7ee43a9b2e2a9f0a6c57" } serde = { version = "1.0.203", default-features = false, features = ["derive"] } hyper = { version = "1", default-features = false, features = ["server", "http2"] } http-body-util = { version = "0.1", default-features = false } diff --git a/ldk-server/src/api/bolt11_claim_for_hash.rs b/ldk-server/src/api/bolt11_claim_for_id.rs similarity index 54% rename from ldk-server/src/api/bolt11_claim_for_hash.rs rename to ldk-server/src/api/bolt11_claim_for_id.rs index d68cfb18..cbf07a48 100644 --- a/ldk-server/src/api/bolt11_claim_for_hash.rs +++ b/ldk-server/src/api/bolt11_claim_for_id.rs @@ -10,17 +10,26 @@ use std::sync::Arc; use hex::FromHex; -use ldk_node::bitcoin::hashes::{sha256, Hash}; -use ldk_node::lightning_types::payment::{PaymentHash, PaymentPreimage}; -use ldk_server_grpc::api::{Bolt11ClaimForHashRequest, Bolt11ClaimForHashResponse}; +use ldk_node::lightning::ln::channelmanager::PaymentId; +use ldk_node::lightning_types::payment::PaymentPreimage; +use ldk_server_grpc::api::{Bolt11ClaimForIdRequest, Bolt11ClaimForIdResponse}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; -pub(crate) async fn handle_bolt11_claim_for_hash_request( - context: Arc, request: Bolt11ClaimForHashRequest, -) -> Result { +pub(crate) async fn handle_bolt11_claim_for_id_request( + context: Arc, request: Bolt11ClaimForIdRequest, +) -> Result { + let payment_id_bytes = + <[u8; PaymentId::LENGTH]>::from_hex(&request.payment_id).map_err(|_| { + LdkServerError::new( + InvalidRequestError, + format!("Invalid payment_id, must be a {}-byte hex string.", PaymentId::LENGTH), + ) + })?; + let payment_id = PaymentId(payment_id_bytes); + let preimage_bytes = <[u8; 32]>::from_hex(&request.preimage).map_err(|_| { LdkServerError::new( InvalidRequestError, @@ -29,21 +38,8 @@ pub(crate) async fn handle_bolt11_claim_for_hash_request( })?; let preimage = PaymentPreimage(preimage_bytes); - let payment_hash = if let Some(hash_hex) = &request.payment_hash { - let hash_bytes = <[u8; 32]>::from_hex(hash_hex).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - "Invalid payment_hash, must be a 32-byte hex string.".to_string(), - ) - })?; - PaymentHash(hash_bytes) - } else { - PaymentHash(sha256::Hash::hash(&preimage.0).to_byte_array()) - }; - let claimable_amount_msat = request.claimable_amount_msat.unwrap_or(u64::MAX); + context.node.bolt11_payment().claim_for_id(payment_id, claimable_amount_msat, preimage)?; - context.node.bolt11_payment().claim_for_hash(payment_hash, claimable_amount_msat, preimage)?; - - Ok(Bolt11ClaimForHashResponse {}) + Ok(Bolt11ClaimForIdResponse {}) } diff --git a/ldk-server/src/api/bolt11_fail_for_hash.rs b/ldk-server/src/api/bolt11_fail_for_hash.rs deleted file mode 100644 index e65527fe..00000000 --- a/ldk-server/src/api/bolt11_fail_for_hash.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file is Copyright its original authors, visible in version control -// history. -// -// This file is licensed under the Apache License, Version 2.0 or the MIT license -// , at your option. -// You may not use this file except in accordance with one or both of these -// licenses. - -use std::sync::Arc; - -use hex::FromHex; -use ldk_node::lightning_types::payment::PaymentHash; -use ldk_server_grpc::api::{Bolt11FailForHashRequest, Bolt11FailForHashResponse}; - -use crate::api::error::LdkServerError; -use crate::api::error::LdkServerErrorCode::InvalidRequestError; -use crate::service::Context; - -pub(crate) async fn handle_bolt11_fail_for_hash_request( - context: Arc, request: Bolt11FailForHashRequest, -) -> Result { - let hash_bytes = <[u8; 32]>::from_hex(&request.payment_hash).map_err(|_| { - LdkServerError::new( - InvalidRequestError, - "Invalid payment_hash, must be a 32-byte hex string.".to_string(), - ) - })?; - let payment_hash = PaymentHash(hash_bytes); - - context.node.bolt11_payment().fail_for_hash(payment_hash)?; - - Ok(Bolt11FailForHashResponse {}) -} diff --git a/ldk-server/src/api/bolt11_fail_for_id.rs b/ldk-server/src/api/bolt11_fail_for_id.rs new file mode 100644 index 00000000..5d86883c --- /dev/null +++ b/ldk-server/src/api/bolt11_fail_for_id.rs @@ -0,0 +1,35 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use std::sync::Arc; + +use hex::FromHex; +use ldk_node::lightning::ln::channelmanager::PaymentId; +use ldk_server_grpc::api::{Bolt11FailForIdRequest, Bolt11FailForIdResponse}; + +use crate::api::error::LdkServerError; +use crate::api::error::LdkServerErrorCode::InvalidRequestError; +use crate::service::Context; + +pub(crate) async fn handle_bolt11_fail_for_id_request( + context: Arc, request: Bolt11FailForIdRequest, +) -> Result { + let payment_id_bytes = + <[u8; PaymentId::LENGTH]>::from_hex(&request.payment_id).map_err(|_| { + LdkServerError::new( + InvalidRequestError, + format!("Invalid payment_id, must be a {}-byte hex string.", PaymentId::LENGTH), + ) + })?; + let payment_id = PaymentId(payment_id_bytes); + + context.node.bolt11_payment().fail_for_id(payment_id)?; + + Ok(Bolt11FailForIdResponse {}) +} diff --git a/ldk-server/src/api/bolt12_create_payer_proof.rs b/ldk-server/src/api/bolt12_create_payer_proof.rs new file mode 100644 index 00000000..23bc77ba --- /dev/null +++ b/ldk-server/src/api/bolt12_create_payer_proof.rs @@ -0,0 +1,69 @@ +// This file is Copyright its original authors, visible in version control +// history. +// +// This file is licensed under the Apache License, Version 2.0 or the MIT license +// , at your option. +// You may not use this file except in accordance with one or both of these +// licenses. + +use std::sync::Arc; + +use hex::FromHex; +use ldk_node::lightning::ln::channelmanager::PaymentId; +use ldk_node::lightning::offers::invoice::Bolt12Invoice; +use ldk_node::lightning_types::payment::PaymentPreimage; +use ldk_node::payment::PayerProofOptions; +use ldk_server_grpc::api::{Bolt12CreatePayerProofRequest, Bolt12CreatePayerProofResponse}; + +use crate::api::error::LdkServerError; +use crate::api::error::LdkServerErrorCode::InvalidRequestError; +use crate::service::Context; + +pub(crate) async fn handle_bolt12_create_payer_proof_request( + context: Arc, request: Bolt12CreatePayerProofRequest, +) -> Result { + let payment_id_bytes = + <[u8; PaymentId::LENGTH]>::from_hex(&request.payment_id).map_err(|_| { + LdkServerError::new( + InvalidRequestError, + format!("Invalid payment_id, must be a {}-byte hex-string.", PaymentId::LENGTH), + ) + })?; + let payment_id = PaymentId(payment_id_bytes); + + let preimage_bytes = <[u8; 32]>::from_hex(&request.payment_preimage).map_err(|_| { + LdkServerError::new( + InvalidRequestError, + "Invalid payment_preimage, must be a 32-byte hex string.".to_string(), + ) + })?; + let payment_preimage = PaymentPreimage(preimage_bytes); + + let invoice_bytes = Vec::::from_hex(&request.invoice).map_err(|_| { + LdkServerError::new( + InvalidRequestError, + "Invalid invoice, must be a hex-encoded BOLT 12 invoice.".to_string(), + ) + })?; + let invoice = + Bolt12Invoice::try_from(invoice_bytes).map_err(|_| ldk_node::NodeError::InvalidInvoice)?; + + let options = request.options.map(|options| PayerProofOptions { + note: options.note, + include_offer_description: options.include_offer_description, + include_offer_issuer: options.include_offer_issuer, + include_invoice_amount: options.include_invoice_amount, + include_invoice_created_at: options.include_invoice_created_at, + extra_tlv_types: options.extra_tlv_types, + }); + + let payer_proof = context.node.bolt12_payment().create_payer_proof( + payment_id, + payment_preimage, + &invoice, + options, + )?; + + Ok(Bolt12CreatePayerProofResponse { payer_proof: payer_proof.to_string() }) +} diff --git a/ldk-server/src/api/error.rs b/ldk-server/src/api/error.rs index eef727f2..ecebec47 100644 --- a/ldk-server/src/api/error.rs +++ b/ldk-server/src/api/error.rs @@ -91,6 +91,7 @@ impl From for LdkServerError { | NodeError::InvalidFeeRate | NodeError::UriParameterParsingFailed | NodeError::InvalidBlindedPaths + | NodeError::InvalidPayerProof | NodeError::AsyncPaymentServicesDisabled => { (error.to_string(), LdkServerErrorCode::InvalidRequestError) }, @@ -122,6 +123,7 @@ impl From for LdkServerError { | NodeError::GossipUpdateTimeout | NodeError::LiquiditySourceUnavailable | NodeError::LiquidityRequestFailed + | NodeError::PayerProofCreationFailed | NodeError::OnchainTxCreationFailed | NodeError::OnchainTxSigningFailed | NodeError::TxSyncFailed diff --git a/ldk-server/src/api/mod.rs b/ldk-server/src/api/mod.rs index 2947257d..4694fcc3 100644 --- a/ldk-server/src/api/mod.rs +++ b/ldk-server/src/api/mod.rs @@ -16,12 +16,13 @@ use ldk_server_grpc::types::CustomTlvRecord as ProtoCustomTlvRecord; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; -pub(crate) mod bolt11_claim_for_hash; -pub(crate) mod bolt11_fail_for_hash; +pub(crate) mod bolt11_claim_for_id; +pub(crate) mod bolt11_fail_for_id; pub(crate) mod bolt11_receive; pub(crate) mod bolt11_receive_for_hash; pub(crate) mod bolt11_receive_via_jit_channel; pub(crate) mod bolt11_send; +pub(crate) mod bolt12_create_payer_proof; pub(crate) mod bolt12_receive; pub(crate) mod bolt12_send; pub(crate) mod close_channel; diff --git a/ldk-server/src/main.rs b/ldk-server/src/main.rs index 28ea10de..5babb5bc 100644 --- a/ldk-server/src/main.rs +++ b/ldk-server/src/main.rs @@ -29,6 +29,7 @@ use ldk_node::config::{Config, ElectrumSyncConfig, EsploraSyncConfig}; use ldk_node::lightning::events::ClosureReason; use ldk_node::lightning::ln::channelmanager::PaymentId; use ldk_node::lightning::ln::types::ChannelId; +use ldk_node::lightning::util::ser::Writeable; use ldk_node::{Builder, CustomTlvRecord, Event, Node}; use ldk_server_grpc::events; use ldk_server_grpc::events::{event_envelope, EventEnvelope}; @@ -158,6 +159,9 @@ fn main() { ldk_node_config.hrn_config = config_file.hrn_config; ldk_node_config.anchor_channels_config.enable_zero_fee_commitments = config_file.enable_zero_fee_commitments; + // The server exposes receive-for-hash APIs, so unknown inbound BOLT11 HTLCs + // must emit PaymentClaimable instead of being failed back. + ldk_node_config.manually_handle_unknown_bolt11_payments = true; let mut builder = Builder::from_config(ldk_node_config); builder.set_log_facade_logger(); @@ -491,10 +495,9 @@ fn main() { .. } => { info!( - "PAYMENT_RECEIVED: with id {:?}, hash {}, amount_msat {}", + "PAYMENT_RECEIVED: with id {}, hash {}, amount_msat {}", payment_id, payment_hash, amount_msat ); - let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); let proto_custom_records: Vec<_> = custom_records .iter() @@ -505,6 +508,7 @@ fn main() { &payment_id, move |payment_ref| { event_envelope::Event::PaymentReceived(events::PaymentReceived { + payment_id: payment_id.to_string(), payment: Some(payment_ref.clone()), custom_records: proto_custom_records, }) @@ -518,12 +522,17 @@ fn main() { metrics.update_all_balances(&event_node); } }, - Event::PaymentSuccessful {payment_id, ..} => { - let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); - + Event::PaymentSuccessful { payment_id, payment_preimage, bolt12_invoice, .. } => { + let payment_preimage = payment_preimage.map(|p| p.to_string()); + let bolt12_invoice = bolt12_invoice.as_ref().and_then(|invoice| { + invoice.bolt12_invoice().map(|i| i.encode().to_lower_hex_string()) + }); send_event_and_upsert_payment(&payment_id, |payment_ref| event_envelope::Event::PaymentSuccessful(events::PaymentSuccessful { + payment_id: payment_id.to_string(), payment: Some(payment_ref.clone()), + payment_preimage, + bolt12_invoice, }), &event_node, &event_sender, @@ -535,10 +544,9 @@ fn main() { } }, Event::PaymentFailed {payment_id, ..} => { - let payment_id = payment_id.expect("PaymentId expected for ldk-server >=0.1"); - send_event_and_upsert_payment(&payment_id, |payment_ref| event_envelope::Event::PaymentFailed(events::PaymentFailed { + payment_id: payment_id.to_string(), payment: Some(payment_ref.clone()), }), &event_node, @@ -554,7 +562,12 @@ fn main() { &payment_id, |payment_ref| { event_envelope::Event::PaymentClaimable( - build_payment_claimable_proto(payment_ref, &custom_records, claim_deadline), + build_payment_claimable_proto( + payment_ref, + &custom_records, + claim_deadline, + payment_id.to_string(), + ), ) }, &event_node, @@ -572,7 +585,7 @@ fn main() { } => { info!( "PAYMENT_FORWARDED: outbound_amount_forwarded_msat {}, total_fee_earned_msat: {}, inbound HTLCs: {}, outbound HTLCs: {}", - outbound_amount_forwarded_msat.unwrap_or(0), + outbound_amount_forwarded_msat, total_fee_earned_msat.unwrap_or(0), prev_htlcs.len(), next_htlcs.len(), @@ -601,7 +614,7 @@ fn main() { total_fee_earned_msat, skimmed_fee_msat, claim_from_onchain_tx, - outbound_amount_forwarded_msat + Some(outbound_amount_forwarded_msat), ); let mut forwarded_payment_id = [0u8; 32]; @@ -873,7 +886,7 @@ fn upsert_payment_details( match paginated_store.write( PAYMENTS_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENTS_PERSISTENCE_SECONDARY_NAMESPACE, - &payment.id, + &payment.payment_id, time, &payment.encode_to_vec(), ) { @@ -913,10 +926,12 @@ fn load_or_generate_api_key(storage_dir: &Path) -> std::io::Result { fn build_payment_claimable_proto( payment_ref: &Payment, custom_records: &[CustomTlvRecord], claim_deadline: Option, + payment_id: String, ) -> events::PaymentClaimable { let proto_custom_records: Vec<_> = custom_records.iter().map(node_to_proto_custom_tlv).collect(); events::PaymentClaimable { + payment_id, payment: Some(payment_ref.clone()), custom_records: proto_custom_records, claim_deadline, @@ -1031,7 +1046,8 @@ mod tests { CustomTlvRecord { type_num: 65537, value: vec![1, 2, 3] }, CustomTlvRecord { type_num: 65538, value: Vec::new() }, ]; - let proto = build_payment_claimable_proto(&payment, &records, None); + let proto = build_payment_claimable_proto(&payment, &records, None, "abc123".to_string()); + assert_eq!(proto.payment_id, "abc123"); assert_eq!(proto.custom_records.len(), 2); assert_eq!(proto.custom_records[0].type_num, 65537); assert_eq!(proto.custom_records[0].value.to_vec(), vec![1, 2, 3]); diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 83c6fb7e..f53216cc 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -19,13 +19,13 @@ use ldk_node::bitcoin::hashes::hmac::{Hmac, HmacEngine}; use ldk_node::bitcoin::hashes::{sha256, Hash, HashEngine}; use ldk_node::Node; use ldk_server_grpc::endpoints::{ - BOLT11_CLAIM_FOR_HASH_PATH, BOLT11_FAIL_FOR_HASH_PATH, BOLT11_RECEIVE_FOR_HASH_PATH, + BOLT11_CLAIM_FOR_ID_PATH, BOLT11_FAIL_FOR_ID_PATH, BOLT11_RECEIVE_FOR_HASH_PATH, BOLT11_RECEIVE_PATH, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH, BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT11_SEND_UNDERPAYING_PATH, - BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, - DECODE_INVOICE_PATH, DECODE_OFFER_PATH, DISCONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH, - FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_METRICS_PATH, GET_NODE_INFO_PATH, - GET_PAYMENT_DETAILS_PATH, GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, + BOLT12_CREATE_PAYER_PROOF_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH, CLOSE_CHANNEL_PATH, + CONNECT_PEER_PATH, DECODE_INVOICE_PATH, DECODE_OFFER_PATH, DISCONNECT_PEER_PATH, + EXPORT_PATHFINDING_SCORES_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_METRICS_PATH, + GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, LIST_PEERS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH, @@ -42,8 +42,8 @@ use ldk_server_grpc::grpc::{ use prost::Message; use tokio::sync::{broadcast, mpsc}; -use crate::api::bolt11_claim_for_hash::handle_bolt11_claim_for_hash_request; -use crate::api::bolt11_fail_for_hash::handle_bolt11_fail_for_hash_request; +use crate::api::bolt11_claim_for_id::handle_bolt11_claim_for_id_request; +use crate::api::bolt11_fail_for_id::handle_bolt11_fail_for_id_request; use crate::api::bolt11_receive::handle_bolt11_receive_request; use crate::api::bolt11_receive_for_hash::handle_bolt11_receive_for_hash_request; use crate::api::bolt11_receive_via_jit_channel::{ @@ -51,6 +51,7 @@ use crate::api::bolt11_receive_via_jit_channel::{ handle_bolt11_receive_via_jit_channel_request, }; use crate::api::bolt11_send::{handle_bolt11_send_request, handle_bolt11_send_underpaying_request}; +use crate::api::bolt12_create_payer_proof::handle_bolt12_create_payer_proof_request; use crate::api::bolt12_receive::handle_bolt12_receive_request; use crate::api::bolt12_send::handle_bolt12_send_request; use crate::api::close_channel::{handle_close_channel_request, handle_force_close_channel_request}; @@ -292,13 +293,11 @@ impl Service> for NodeService { handle_grpc_unary(context, body_bytes, handle_bolt11_receive_for_hash_request) .await }, - BOLT11_CLAIM_FOR_HASH_PATH => { - handle_grpc_unary(context, body_bytes, handle_bolt11_claim_for_hash_request) - .await + BOLT11_CLAIM_FOR_ID_PATH => { + handle_grpc_unary(context, body_bytes, handle_bolt11_claim_for_id_request).await }, - BOLT11_FAIL_FOR_HASH_PATH => { - handle_grpc_unary(context, body_bytes, handle_bolt11_fail_for_hash_request) - .await + BOLT11_FAIL_FOR_ID_PATH => { + handle_grpc_unary(context, body_bytes, handle_bolt11_fail_for_id_request).await }, BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH => { handle_grpc_unary( @@ -329,6 +328,10 @@ impl Service> for NodeService { BOLT12_SEND_PATH => { handle_grpc_unary(context, body_bytes, handle_bolt12_send_request).await }, + BOLT12_CREATE_PAYER_PROOF_PATH => { + handle_grpc_unary(context, body_bytes, handle_bolt12_create_payer_proof_request) + .await + }, OPEN_CHANNEL_PATH => { handle_grpc_unary(context, body_bytes, handle_open_channel).await }, diff --git a/ldk-server/src/util/proto_adapter.rs b/ldk-server/src/util/proto_adapter.rs index 42c3738c..4bf65efc 100644 --- a/ldk-server/src/util/proto_adapter.rs +++ b/ldk-server/src/util/proto_adapter.rs @@ -135,7 +135,7 @@ pub(crate) fn payment_to_proto(payment: PaymentDetails) -> Payment { } = payment; Payment { - id: id.to_string(), + payment_id: id.to_string(), kind: Some(payment_kind_to_proto(kind)), amount_msat, fee_paid_msat, @@ -162,7 +162,7 @@ pub(crate) fn payment_kind_to_proto( status: Some(confirmation_status_to_proto(status)), })), }, - PaymentKind::Bolt11 { hash, preimage, secret, counterparty_skimmed_fee_msat } => { + PaymentKind::Bolt11 { hash, preimage, secret, counterparty_skimmed_fee_msat, .. } => { ldk_server_grpc::types::PaymentKind { kind: Some(Bolt11(ldk_server_grpc::types::Bolt11 { hash: hash.to_string(), @@ -172,19 +172,19 @@ pub(crate) fn payment_kind_to_proto( })), } }, - PaymentKind::Bolt12Offer { hash, preimage, secret, offer_id, payer_note, quantity } => { - ldk_server_grpc::types::PaymentKind { - kind: Some(Bolt12Offer(ldk_server_grpc::types::Bolt12Offer { - hash: hash.map(|h| h.to_string()), - preimage: preimage.map(|p| p.to_string()), - secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), - offer_id: offer_id.0.to_lower_hex_string(), - payer_note: payer_note.map(|s| s.to_string()), - quantity, - })), - } + PaymentKind::Bolt12Offer { + hash, preimage, secret, offer_id, payer_note, quantity, .. + } => ldk_server_grpc::types::PaymentKind { + kind: Some(Bolt12Offer(ldk_server_grpc::types::Bolt12Offer { + hash: hash.map(|h| h.to_string()), + preimage: preimage.map(|p| p.to_string()), + secret: secret.map(|s| Bytes::copy_from_slice(&s.0)), + offer_id: offer_id.0.to_lower_hex_string(), + payer_note: payer_note.map(|s| s.to_string()), + quantity, + })), }, - PaymentKind::Bolt12Refund { hash, preimage, secret, payer_note, quantity } => { + PaymentKind::Bolt12Refund { hash, preimage, secret, payer_note, quantity, .. } => { ldk_server_grpc::types::PaymentKind { kind: Some(Bolt12Refund(ldk_server_grpc::types::Bolt12Refund { hash: hash.map(|h| h.to_string()), @@ -195,7 +195,7 @@ pub(crate) fn payment_kind_to_proto( })), } }, - PaymentKind::Spontaneous { hash, preimage } => ldk_server_grpc::types::PaymentKind { + PaymentKind::Spontaneous { hash, preimage, .. } => ldk_server_grpc::types::PaymentKind { kind: Some(Spontaneous(ldk_server_grpc::types::Spontaneous { hash: hash.to_string(), preimage: preimage.map(|p| p.to_string()),