From 2f71152977d907094baf3c16a3f8c2d7734ac41e Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Tue, 5 Mar 2024 12:18:52 +0100 Subject: [PATCH] Move onchain payments API to `OnchainPaymentsHandler` --- README.md | 2 +- .../lightningdevkit/ldknode/AndroidLibTest.kt | 4 +- .../lightningdevkit/ldknode/LibraryTest.kt | 4 +- bindings/ldk_node.udl | 16 +++-- bindings/python/src/ldk_node/test_ldk_node.py | 4 +- src/lib.rs | 44 +++---------- src/payment/mod.rs | 2 + src/payment/onchain.rs | 63 +++++++++++++++++++ src/uniffi_types.rs | 3 +- tests/common.rs | 4 +- tests/integration_tests_cln.rs | 2 +- tests/integration_tests_rust.rs | 23 ++++--- 12 files changed, 108 insertions(+), 63 deletions(-) create mode 100644 src/payment/onchain.rs diff --git a/README.md b/README.md index eccb85c40..4078ce67b 100644 --- a/README.md +++ b/README.md @@ -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 .. diff --git a/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt b/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt index a5ca6eac0..763862a33 100644 --- a/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt +++ b/bindings/kotlin/ldk-node-android/lib/src/androidTest/kotlin/org/lightningdevkit/ldknode/AndroidLibTest.kt @@ -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() diff --git a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt index c3fec8fb6..dbd8aa229 100644 --- a/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt +++ b/bindings/kotlin/ldk-node-jvm/lib/src/test/kotlin/org/lightningdevkit/ldknode/LibraryTest.kt @@ -146,10 +146,10 @@ class LibraryTest { val nodeId2 = node2.nodeId() println("Node Id 2: $nodeId2") - val address1 = node1.newOnchainAddress() + val address1 = node1.onchain_payment().newAddress() println("Funding address 1: $address1") - val address2 = node2.newOnchainAddress() + val address2 = node2.onchain_payment().newAddress() println("Funding address 2: $address2") val txid1 = sendToAddress(address1, 100000u) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 3f41b9ad1..74ccb1908 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -53,12 +53,7 @@ interface Node { sequence? listening_addresses(); Bolt11PaymentsHandler bolt11_payment(); SpontaneousPaymentsHandler spontaneous_payment(); - [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); + OnchainPaymentsHandler onchain_payment(); [Throws=NodeError] void connect(PublicKey node_id, SocketAddress address, boolean persist); [Throws=NodeError] @@ -110,6 +105,15 @@ interface SpontaneousPaymentsHandler { void send_probes(u64 amount_msat, PublicKey node_id); }; +interface OnchainPaymentsHandler { + [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] enum NodeError { "AlreadyRunning", diff --git a/bindings/python/src/ldk_node/test_ldk_node.py b/bindings/python/src/ldk_node/test_ldk_node.py index 468800efe..a593078c1 100644 --- a/bindings/python/src/ldk_node/test_ldk_node.py +++ b/bindings/python/src/ldk_node/test_ldk_node.py @@ -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) diff --git a/src/lib.rs b/src/lib.rs index 3f83f77c4..9d93c698b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ //! //! node.start().unwrap(); //! -//! let funding_address = node.new_onchain_address(); +//! let funding_address = node.onchain_payment().new_address(); //! //! // .. fund address .. //! @@ -130,7 +130,7 @@ use connection::ConnectionManager; use event::{EventHandler, EventQueue}; use gossip::GossipSource; use liquidity::LiquiditySource; -use payment::{Bolt11PaymentsHandler, SpontaneousPaymentsHandler}; +use payment::{Bolt11PaymentsHandler, OnchainPaymentsHandler, SpontaneousPaymentsHandler}; use payment_store::PaymentStore; pub use payment_store::{LSPFeeLimits, PaymentDetails, PaymentDirection, PaymentStatus}; use peer_store::{PeerInfo, PeerStore}; @@ -154,7 +154,6 @@ use lightning_background_processor::process_events_async; use lightning_transaction_sync::EsploraSyncClient; use bitcoin::secp256k1::PublicKey; -use bitcoin::{Address, Txid}; use rand::Rng; @@ -770,38 +769,13 @@ impl Node { )) } - /// Retrieve a new on-chain/funding address. - pub fn new_onchain_address(&self) -> Result { - let funding_address = self.wallet.get_new_address()?; - log_info!(self.logger, "Generated new funding address: {}", funding_address); - Ok(funding_address) - } - - /// Send an on-chain payment to the given address. - pub fn send_to_onchain_address( - &self, address: &bitcoin::Address, amount_sats: u64, - ) -> Result { - let rt_lock = self.runtime.read().unwrap(); - if rt_lock.is_none() { - return Err(Error::NotRunning); - } - - let cur_balance = self.wallet.get_balance()?; - if cur_balance.get_spendable() < amount_sats { - log_error!(self.logger, "Unable to send payment due to insufficient funds."); - return Err(Error::InsufficientFunds); - } - self.wallet.send_to_address(address, Some(amount_sats)) - } - - /// Send an on-chain payment to the given address, draining all the available funds. - pub fn send_all_to_onchain_address(&self, address: &bitcoin::Address) -> Result { - let rt_lock = self.runtime.read().unwrap(); - if rt_lock.is_none() { - return Err(Error::NotRunning); - } - - self.wallet.send_to_address(address, None) + /// Returns a payment handler allowing to send and receive on-chain payments. + pub fn onchain_payment(&self) -> Arc { + Arc::new(OnchainPaymentsHandler::new( + Arc::clone(&self.runtime), + Arc::clone(&self.wallet), + Arc::clone(&self.logger), + )) } /// Retrieve a list of known channels. diff --git a/src/payment/mod.rs b/src/payment/mod.rs index 8821d7cc5..bc56ccf04 100644 --- a/src/payment/mod.rs +++ b/src/payment/mod.rs @@ -1,7 +1,9 @@ //! Handlers for different types of payments. mod bolt11; +mod onchain; mod spontaneous; pub use bolt11::Bolt11PaymentsHandler; +pub use onchain::OnchainPaymentsHandler; pub use spontaneous::SpontaneousPaymentsHandler; diff --git a/src/payment/onchain.rs b/src/payment/onchain.rs new file mode 100644 index 000000000..76339e401 --- /dev/null +++ b/src/payment/onchain.rs @@ -0,0 +1,63 @@ +//! Holds a payment handler allowing to send and receive on-chain payments. + +use crate::error::Error; +use crate::logger::{log_error, log_info, FilesystemLogger, Logger}; +use crate::types::Wallet; + +use bitcoin::{Address, Txid}; + +use std::sync::{Arc, RwLock}; + +/// A payment handler allowing to send and receive on-chain payments. +/// +/// Should be retrieved by calling [`Node::onchain_payment`]. +/// +/// [`Node::onchain_payment`]: crate::Node::onchain_payment +pub struct OnchainPaymentsHandler { + runtime: Arc>>, + wallet: Arc, + logger: Arc, +} + +impl OnchainPaymentsHandler { + pub(crate) fn new( + runtime: Arc>>, wallet: Arc, + logger: Arc, + ) -> Self { + Self { runtime, wallet, logger } + } + + /// Retrieve a new on-chain/funding address. + pub fn new_address(&self) -> Result { + let funding_address = self.wallet.get_new_address()?; + log_info!(self.logger, "Generated new funding address: {}", funding_address); + Ok(funding_address) + } + + /// Send an on-chain payment to the given address. + pub fn send_to_address( + &self, address: &bitcoin::Address, amount_sats: u64, + ) -> Result { + let rt_lock = self.runtime.read().unwrap(); + if rt_lock.is_none() { + return Err(Error::NotRunning); + } + + let cur_balance = self.wallet.get_balance()?; + if cur_balance.get_spendable() < amount_sats { + log_error!(self.logger, "Unable to send payment due to insufficient funds."); + return Err(Error::InsufficientFunds); + } + self.wallet.send_to_address(address, Some(amount_sats)) + } + + /// Send an on-chain payment to the given address, draining all the available funds. + pub fn send_all_to_address(&self, address: &bitcoin::Address) -> Result { + let rt_lock = self.runtime.read().unwrap(); + if rt_lock.is_none() { + return Err(Error::NotRunning); + } + + self.wallet.send_to_address(address, None) + } +} diff --git a/src/uniffi_types.rs b/src/uniffi_types.rs index 0cef466a1..7204f67f1 100644 --- a/src/uniffi_types.rs +++ b/src/uniffi_types.rs @@ -4,7 +4,7 @@ pub use lightning::util::string::UntrustedString; pub use lightning_invoice::Bolt11Invoice; -pub use bitcoin::{BlockHash, Network, OutPoint}; +pub use bitcoin::{Address, BlockHash, Network, OutPoint, Txid}; pub use bip39::Mnemonic; @@ -17,7 +17,6 @@ use crate::{SocketAddress, UserChannelId}; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; use bitcoin::secp256k1::PublicKey; -use bitcoin::{Address, Txid}; use lightning_invoice::SignedRawBolt11Invoice; use std::convert::TryInto; diff --git a/tests/common.rs b/tests/common.rs index a89fdd837..b4db81808 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -317,8 +317,8 @@ pub fn open_channel( pub(crate) fn do_channel_full_cycle( node_a: TestNode, node_b: TestNode, bitcoind: &BitcoindClient, electrsd: &E, allow_0conf: bool, ) { - let addr_a = node_a.new_onchain_address().unwrap(); - let addr_b = node_b.new_onchain_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().unwrap(); + let addr_b = node_b.onchain_payment().new_address().unwrap(); let premine_amount_sat = 100_000; diff --git a/tests/integration_tests_cln.rs b/tests/integration_tests_cln.rs index 0405b136c..0cc463768 100644 --- a/tests/integration_tests_cln.rs +++ b/tests/integration_tests_cln.rs @@ -44,7 +44,7 @@ fn test_cln() { node.start().unwrap(); // Premine some funds and distribute - let address = node.new_onchain_address().unwrap(); + let address = node.onchain_payment().new_address().unwrap(); let premine_amount = Amount::from_sat(5_000_000); common::premine_and_distribute_funds( &bitcoind_client, diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index ed854e50a..e5d947edd 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -33,8 +33,8 @@ fn channel_open_fails_when_funds_insufficient() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let (node_a, node_b) = setup_two_nodes(&electrsd, false); - let addr_a = node_a.new_onchain_address().unwrap(); - let addr_b = node_b.new_onchain_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().unwrap(); + let addr_b = node_b.onchain_payment().new_address().unwrap(); let premine_amount_sat = 100_000; @@ -79,7 +79,7 @@ fn multi_hop_sending() { nodes.push(node); } - let addresses = nodes.iter().map(|n| n.new_onchain_address().unwrap()).collect(); + let addresses = nodes.iter().map(|n| n.onchain_payment().new_address().unwrap()).collect(); let premine_amount_sat = 5_000_000; premine_and_distribute_funds( &bitcoind.client, @@ -169,7 +169,7 @@ fn start_stop_reinit() { let expected_node_id = node.node_id(); assert_eq!(node.start(), Err(NodeError::AlreadyRunning)); - let funding_address = node.new_onchain_address().unwrap(); + let funding_address = node.onchain_payment().new_address().unwrap(); assert_eq!(node.list_balances().total_onchain_balance_sats, 0); @@ -223,8 +223,8 @@ fn onchain_spend_receive() { let (bitcoind, electrsd) = setup_bitcoind_and_electrsd(); let (node_a, node_b) = setup_two_nodes(&electrsd, false); - let addr_a = node_a.new_onchain_address().unwrap(); - let addr_b = node_b.new_onchain_address().unwrap(); + let addr_a = node_a.onchain_payment().new_address().unwrap(); + let addr_b = node_b.onchain_payment().new_address().unwrap(); premine_and_distribute_funds( &bitcoind.client, @@ -237,9 +237,12 @@ fn onchain_spend_receive() { node_b.sync_wallets().unwrap(); assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, 100000); - assert_eq!(Err(NodeError::InsufficientFunds), node_a.send_to_onchain_address(&addr_b, 1000)); + assert_eq!( + Err(NodeError::InsufficientFunds), + node_a.onchain_payment().send_to_address(&addr_b, 1000) + ); - let txid = node_b.send_to_onchain_address(&addr_a, 1000).unwrap(); + let txid = node_b.onchain_payment().send_to_address(&addr_a, 1000).unwrap(); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6); wait_for_tx(&electrsd.client, txid); @@ -250,8 +253,8 @@ fn onchain_spend_receive() { assert!(node_b.list_balances().spendable_onchain_balance_sats > 98000); assert!(node_b.list_balances().spendable_onchain_balance_sats < 100000); - let addr_b = node_b.new_onchain_address().unwrap(); - let txid = node_a.send_all_to_onchain_address(&addr_b).unwrap(); + let addr_b = node_b.onchain_payment().new_address().unwrap(); + let txid = node_a.onchain_payment().send_all_to_address(&addr_b).unwrap(); generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6); wait_for_tx(&electrsd.client, txid);