Skip to content

Commit

Permalink
Merge pull request #270 from tnull/2024-03-payment-api-refactor
Browse files Browse the repository at this point in the history
Refactor and modularize payment APIs
  • Loading branch information
tnull authored May 15, 2024
2 parents e14b70e + 1fab656 commit b7c4862
Show file tree
Hide file tree
Showing 19 changed files with 1,703 additions and 1,093 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A ready-to-go Lightning node library built using [LDK][ldk] and [BDK][bdk].
LDK Node is a self-custodial Lightning node in library form. Its central goal is to provide a small, simple, and straightforward interface that enables users to easily set up and run a Lightning node with an integrated on-chain wallet. While minimalism is at its core, LDK Node aims to be sufficiently modular and configurable to be useful for a variety of use cases.

## Getting Started
The primary abstraction of the library is the [`Node`][api_docs_node], which can be retrieved by setting up and configuring a [`Builder`][api_docs_builder] to your liking and calling one of the `build` methods. `Node` can then be controlled via commands such as `start`, `stop`, `connect_open_channel`, `send_payment`, etc.
The primary abstraction of the library is the [`Node`][api_docs_node], which can be retrieved by setting up and configuring a [`Builder`][api_docs_builder] to your liking and calling one of the `build` methods. `Node` can then be controlled via commands such as `start`, `stop`, `connect_open_channel`, `send`, etc.

```rust
use ldk_node::Builder;
Expand All @@ -31,7 +31,7 @@ fn main() {

node.start().unwrap();

let funding_address = node.new_onchain_address();
let funding_address = node.onchain_payment().new_address();

// .. fund address ..

Expand All @@ -44,7 +44,7 @@ fn main() {
node.event_handled();

let invoice = Bolt11Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(&invoice).unwrap();
node.bolt11_payment().send(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ class AndroidLibTest {
val nodeId2 = node2.nodeId()
println("Node Id 2: $nodeId2")

val address1 = node1.newOnchainAddress()
val address1 = node1.onchain_payment().newOnchainAddress()
println("Funding address 1: $address1")

val address2 = node2.newOnchainAddress()
val address2 = node2.onchain_payment().newOnchainAddress()
println("Funding address 2: $address2")

node1.stop()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,10 @@ class LibraryTest {
val nodeId2 = node2.nodeId()
println("Node Id 2: $nodeId2")

val address1 = node1.newOnchainAddress()
val address1 = node1.onchainPayment().newAddress()
println("Funding address 1: $address1")

val address2 = node2.newOnchainAddress()
val address2 = node2.onchainPayment().newAddress()
println("Funding address 2: $address2")

val txid1 = sendToAddress(address1, 100000u)
Expand Down Expand Up @@ -222,9 +222,9 @@ class LibraryTest {
else -> return
}

val invoice = node2.receivePayment(2500000u, "asdf", 9217u)
val invoice = node2.bolt11Payment().receive(2500000u, "asdf", 9217u)

node1.sendPayment(invoice)
node1.bolt11Payment().send(invoice)

val paymentSuccessfulEvent = node1.waitNextEvent()
println("Got event: $paymentSuccessfulEvent")
Expand Down
84 changes: 53 additions & 31 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,9 @@ interface Node {
void event_handled();
PublicKey node_id();
sequence<SocketAddress>? listening_addresses();
[Throws=NodeError]
Address new_onchain_address();
[Throws=NodeError]
Txid send_to_onchain_address([ByRef]Address address, u64 amount_msat);
[Throws=NodeError]
Txid send_all_to_onchain_address([ByRef]Address address);
Bolt11Payment bolt11_payment();
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
[Throws=NodeError]
void connect(PublicKey node_id, SocketAddress address, boolean persist);
[Throws=NodeError]
Expand All @@ -71,36 +68,51 @@ interface Node {
void update_channel_config([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, ChannelConfig channel_config);
[Throws=NodeError]
void sync_wallets();
PaymentDetails? payment([ByRef]PaymentId payment_id);
[Throws=NodeError]
PaymentHash send_payment([ByRef]Bolt11Invoice invoice);
void remove_payment([ByRef]PaymentId payment_id);
BalanceDetails list_balances();
sequence<PaymentDetails> list_payments();
sequence<PeerDetails> list_peers();
sequence<ChannelDetails> list_channels();
[Throws=NodeError]
PaymentHash send_payment_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat);
string sign_message([ByRef]sequence<u8> msg);
boolean verify_signature([ByRef]sequence<u8> msg, [ByRef]string sig, [ByRef]PublicKey pkey);
};

interface Bolt11Payment {
[Throws=NodeError]
PaymentHash send_spontaneous_payment(u64 amount_msat, PublicKey node_id);
PaymentId send([ByRef]Bolt11Invoice invoice);
[Throws=NodeError]
void send_payment_probes([ByRef]Bolt11Invoice invoice);
PaymentId send_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat);
[Throws=NodeError]
void send_spontaneous_payment_probes(u64 amount_msat, PublicKey node_id);
void send_probes([ByRef]Bolt11Invoice invoice);
[Throws=NodeError]
void send_payment_probes_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat);
void send_probes_using_amount([ByRef]Bolt11Invoice invoice, u64 amount_msat);
[Throws=NodeError]
Bolt11Invoice receive_payment(u64 amount_msat, [ByRef]string description, u32 expiry_secs);
Bolt11Invoice receive(u64 amount_msat, [ByRef]string description, u32 expiry_secs);
[Throws=NodeError]
Bolt11Invoice receive_variable_amount_payment([ByRef]string description, u32 expiry_secs);
Bolt11Invoice receive_variable_amount([ByRef]string description, u32 expiry_secs);
[Throws=NodeError]
Bolt11Invoice receive_payment_via_jit_channel(u64 amount_msat, [ByRef]string description, u32 expiry_secs, u64? max_lsp_fee_limit_msat);
Bolt11Invoice receive_via_jit_channel(u64 amount_msat, [ByRef]string description, u32 expiry_secs, u64? max_lsp_fee_limit_msat);
[Throws=NodeError]
Bolt11Invoice receive_variable_amount_payment_via_jit_channel([ByRef]string description, u32 expiry_secs, u64? max_proportional_lsp_fee_limit_ppm_msat);
PaymentDetails? payment([ByRef]PaymentHash payment_hash);
Bolt11Invoice receive_variable_amount_via_jit_channel([ByRef]string description, u32 expiry_secs, u64? max_proportional_lsp_fee_limit_ppm_msat);
};

interface SpontaneousPayment {
[Throws=NodeError]
void remove_payment([ByRef]PaymentHash payment_hash);
BalanceDetails list_balances();
sequence<PaymentDetails> list_payments();
sequence<PeerDetails> list_peers();
sequence<ChannelDetails> list_channels();
PaymentId send(u64 amount_msat, PublicKey node_id);
[Throws=NodeError]
string sign_message([ByRef]sequence<u8> msg);
boolean verify_signature([ByRef]sequence<u8> msg, [ByRef]string sig, [ByRef]PublicKey pkey);
void send_probes(u64 amount_msat, PublicKey node_id);
};

interface OnchainPayment {
[Throws=NodeError]
Address new_address();
[Throws=NodeError]
Txid send_to_address([ByRef]Address address, u64 amount_msat);
[Throws=NodeError]
Txid send_all_to_address([ByRef]Address address);
};

[Error]
Expand All @@ -127,6 +139,7 @@ enum NodeError {
"InvalidSocketAddress",
"InvalidPublicKey",
"InvalidSecretKey",
"InvalidPaymentId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand Down Expand Up @@ -173,9 +186,9 @@ enum BuildError {

[Enum]
interface Event {
PaymentSuccessful(PaymentHash payment_hash, u64? fee_paid_msat);
PaymentFailed(PaymentHash payment_hash, PaymentFailureReason? reason);
PaymentReceived(PaymentHash payment_hash, u64 amount_msat);
PaymentSuccessful(PaymentId? payment_id, PaymentHash payment_hash, u64? fee_paid_msat);
PaymentFailed(PaymentId? payment_id, PaymentHash payment_hash, PaymentFailureReason? reason);
PaymentReceived(PaymentId? payment_id, PaymentHash payment_hash, u64 amount_msat);
ChannelPending(ChannelId channel_id, UserChannelId user_channel_id, ChannelId former_temporary_channel_id, PublicKey counterparty_node_id, OutPoint funding_txo);
ChannelReady(ChannelId channel_id, UserChannelId user_channel_id, PublicKey? counterparty_node_id);
ChannelClosed(ChannelId channel_id, UserChannelId user_channel_id, PublicKey? counterparty_node_id, ClosureReason? reason);
Expand Down Expand Up @@ -207,6 +220,14 @@ interface ClosureReason {
HTLCsTimedOut();
};

[Enum]
interface PaymentKind {
Onchain();
Bolt11(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret);
Bolt11Jit(PaymentHash hash, PaymentPreimage? preimage, PaymentSecret? secret, LSPFeeLimits lsp_fee_limits);
Spontaneous(PaymentHash hash, PaymentPreimage? preimage);
};

enum PaymentDirection {
"Inbound",
"Outbound",
Expand All @@ -224,13 +245,11 @@ dictionary LSPFeeLimits {
};

dictionary PaymentDetails {
PaymentHash hash;
PaymentPreimage? preimage;
PaymentSecret? secret;
PaymentId id;
PaymentKind kind;
u64? amount_msat;
PaymentDirection direction;
PaymentStatus status;
LSPFeeLimits? lsp_fee_limits;
};

[NonExhaustive]
Expand Down Expand Up @@ -352,6 +371,9 @@ typedef string Address;
[Custom]
typedef string Bolt11Invoice;

[Custom]
typedef string PaymentId;

[Custom]
typedef string PaymentHash;

Expand Down
8 changes: 4 additions & 4 deletions bindings/python/src/ldk_node/test_ldk_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ def test_channel_full_cycle(self):
node_id_2 = node_2.node_id()
print("Node ID 2:", node_id_2)

address_1 = node_1.new_onchain_address()
address_1 = node_1.onchain_payment().new_address()
txid_1 = send_to_address(address_1, 100000)
address_2 = node_2.new_onchain_address()
address_2 = node_2.onchain_payment().new_address()
txid_2 = send_to_address(address_2, 100000)

wait_for_tx(esplora_endpoint, txid_1)
Expand Down Expand Up @@ -185,8 +185,8 @@ def test_channel_full_cycle(self):
print("EVENT:", channel_ready_event_2)
node_2.event_handled()

invoice = node_2.receive_payment(2500000, "asdf", 9217)
node_1.send_payment(invoice)
invoice = node_2.bolt11_payment().receive(2500000, "asdf", 9217)
node_1.bolt11_payment().send(invoice)

payment_successful_event_1 = node_1.wait_next_event()
assert isinstance(payment_successful_event_1, Event.PAYMENT_SUCCESSFUL)
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::io::sqlite_store::SqliteStore;
use crate::liquidity::LiquiditySource;
use crate::logger::{log_error, log_info, FilesystemLogger, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment_store::PaymentStore;
use crate::payment::store::PaymentStore;
use crate::peer_store::PeerStore;
use crate::tx_broadcaster::TransactionBroadcaster;
use crate::types::{
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub enum Error {
InvalidPublicKey,
/// The given secret key is invalid.
InvalidSecretKey,
/// The given payment id is invalid.
InvalidPaymentId,
/// The given payment hash is invalid.
InvalidPaymentHash,
/// The given payment pre-image is invalid.
Expand Down Expand Up @@ -100,6 +102,7 @@ impl fmt::Display for Error {
Self::InvalidSocketAddress => write!(f, "The given network address is invalid."),
Self::InvalidPublicKey => write!(f, "The given public key is invalid."),
Self::InvalidSecretKey => write!(f, "The given secret key is invalid."),
Self::InvalidPaymentId => write!(f, "The given payment id is invalid."),
Self::InvalidPaymentHash => write!(f, "The given payment hash is invalid."),
Self::InvalidPaymentPreimage => write!(f, "The given payment preimage is invalid."),
Self::InvalidPaymentSecret => write!(f, "The given payment secret is invalid."),
Expand Down
Loading

0 comments on commit b7c4862

Please sign in to comment.