From 8495bc7ca8efebd713b5003d6f30f2fb1d622403 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Thu, 15 Feb 2024 11:28:56 +0100 Subject: [PATCH] Expose onchain balances via `BalanceDetails` --- .../lightningdevkit/ldknode/LibraryTest.kt | 16 +++++------ bindings/ldk_node.udl | 6 ++-- bindings/python/src/ldk_node/test_ldk_node.py | 12 ++++---- src/balance.rs | 4 +++ src/lib.rs | 23 +++++++-------- tests/common.rs | 19 +++++++------ tests/integration_tests_rust.rs | 28 +++++++++---------- 7 files changed, 57 insertions(+), 51 deletions(-) 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 936fa558e..0bf36de00 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 @@ -162,10 +162,10 @@ class LibraryTest { node1.syncWallets() node2.syncWallets() - val spendableBalance1 = node1.spendableOnchainBalanceSats() - val spendableBalance2 = node2.spendableOnchainBalanceSats() - val totalBalance1 = node1.totalOnchainBalanceSats() - val totalBalance2 = node2.totalOnchainBalanceSats() + val spendableBalance1 = node1.listBalances().spendableOnchainBalanceSats + val spendableBalance2 = node2.listBalances().spendableOnchainBalanceSats + val totalBalance1 = node1.listBalances().totalOnchainBalanceSats + val totalBalance2 = node2.listBalances().totalOnchainBalanceSats println("Spendable balance 1: $spendableBalance1") println("Spendable balance 2: $spendableBalance1") println("Total balance 1: $totalBalance1") @@ -199,8 +199,8 @@ class LibraryTest { node1.syncWallets() node2.syncWallets() - val spendableBalance1AfterOpen = node1.spendableOnchainBalanceSats() - val spendableBalance2AfterOpen = node2.spendableOnchainBalanceSats() + val spendableBalance1AfterOpen = node1.listBalances().spendableOnchainBalanceSats + val spendableBalance2AfterOpen = node2.listBalances().spendableOnchainBalanceSats println("Spendable balance 1 after open: $spendableBalance1AfterOpen") println("Spendable balance 2 after open: $spendableBalance2AfterOpen") assert(spendableBalance1AfterOpen > 49000u) @@ -256,8 +256,8 @@ class LibraryTest { node1.syncWallets() node2.syncWallets() - val spendableBalance1AfterClose = node1.spendableOnchainBalanceSats() - val spendableBalance2AfterClose = node2.spendableOnchainBalanceSats() + val spendableBalance1AfterClose = node1.listBalances().spendableOnchainBalanceSats + val spendableBalance2AfterClose = node2.listBalances().spendableOnchainBalanceSats println("Spendable balance 1 after close: $spendableBalance1AfterClose") println("Spendable balance 2 after close: $spendableBalance2AfterClose") assert(spendableBalance1AfterClose > 95000u) diff --git a/bindings/ldk_node.udl b/bindings/ldk_node.udl index 594443ac5..e39357001 100644 --- a/bindings/ldk_node.udl +++ b/bindings/ldk_node.udl @@ -53,10 +53,6 @@ interface LDKNode { [Throws=NodeError] Txid send_all_to_onchain_address([ByRef]Address address); [Throws=NodeError] - u64 spendable_onchain_balance_sats(); - [Throws=NodeError] - u64 total_onchain_balance_sats(); - [Throws=NodeError] void connect(PublicKey node_id, SocketAddress address, boolean persist); [Throws=NodeError] void disconnect(PublicKey node_id); @@ -239,6 +235,8 @@ interface LightningBalance { }; dictionary BalanceDetails { + u64 total_onchain_balance_sats; + u64 spendable_onchain_balance_sats; u64 total_lightning_balance_sats; sequence lightning_balances; }; diff --git a/bindings/python/src/ldk_node/test_ldk_node.py b/bindings/python/src/ldk_node/test_ldk_node.py index 7b8ea7239..a5de42d90 100644 --- a/bindings/python/src/ldk_node/test_ldk_node.py +++ b/bindings/python/src/ldk_node/test_ldk_node.py @@ -138,10 +138,10 @@ def test_channel_full_cycle(self): node_1.sync_wallets() node_2.sync_wallets() - spendable_balance_1 = node_1.spendable_onchain_balance_sats() - spendable_balance_2 = node_2.spendable_onchain_balance_sats() - total_balance_1 = node_1.total_onchain_balance_sats() - total_balance_2 = node_2.total_onchain_balance_sats() + spendable_balance_1 = node_1.list_balances().spendable_onchain_balance_sats + spendable_balance_2 = node_2.list_balances().spendable_onchain_balance_sats + total_balance_1 = node_1.list_balances().total_onchain_balance_sats + total_balance_2 = node_2.list_balances().total_onchain_balance_sats print("SPENDABLE 1:", spendable_balance_1) self.assertEqual(spendable_balance_1, 100000) @@ -215,10 +215,10 @@ def test_channel_full_cycle(self): node_1.sync_wallets() node_2.sync_wallets() - spendable_balance_after_close_1 = node_1.spendable_onchain_balance_sats() + spendable_balance_after_close_1 = node_1.list_balances().spendable_onchain_balance_sats assert spendable_balance_after_close_1 > 95000 assert spendable_balance_after_close_1 < 100000 - spendable_balance_after_close_2 = node_2.spendable_onchain_balance_sats() + spendable_balance_after_close_2 = node_2.list_balances().spendable_onchain_balance_sats self.assertEqual(spendable_balance_after_close_2, 102500) # Stop nodes diff --git a/src/balance.rs b/src/balance.rs index 5a1183207..474ef3e64 100644 --- a/src/balance.rs +++ b/src/balance.rs @@ -7,6 +7,10 @@ use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage}; /// [`Node::list_balances`]: crate::Node::list_balances #[derive(Debug, Clone)] pub struct BalanceDetails { + /// The total balance of our on-chain wallet. + pub total_onchain_balance_sats: u64, + /// The currently spendable balance of our on-chain wallet. + pub spendable_onchain_balance_sats: u64, /// The total balance that we would be able to claim across all our Lightning channels. pub total_lightning_balance_sats: u64, /// A detailed list of all known Lightning balances. diff --git a/src/lib.rs b/src/lib.rs index eb413c164..f74867877 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -853,16 +853,6 @@ impl Node { Ok(funding_address) } - /// Retrieve the currently spendable on-chain balance in satoshis. - pub fn spendable_onchain_balance_sats(&self) -> Result { - Ok(self.wallet.get_balance().map(|bal| bal.get_spendable())?) - } - - /// Retrieve the current total on-chain balance in satoshis. - pub fn total_onchain_balance_sats(&self) -> Result { - Ok(self.wallet.get_balance().map(|bal| bal.get_total())?) - } - /// Send an on-chain payment to the given address. pub fn send_to_onchain_address( &self, address: &bitcoin::Address, amount_sats: u64, @@ -1558,6 +1548,12 @@ impl Node { /// Retrieves an overview of all known balances. pub fn list_balances(&self) -> BalanceDetails { + let (total_onchain_balance_sats, spendable_onchain_balance_sats) = self + .wallet + .get_balance() + .map(|bal| (bal.get_total(), bal.get_spendable())) + .unwrap_or((0, 0)); + let mut total_lightning_balance_sats = 0; let mut lightning_balances = Vec::new(); for funding_txo in self.chain_monitor.list_monitors() { @@ -1583,7 +1579,12 @@ impl Node { } } - BalanceDetails { total_lightning_balance_sats, lightning_balances } + BalanceDetails { + total_onchain_balance_sats, + spendable_onchain_balance_sats, + total_lightning_balance_sats, + lightning_balances, + } } /// Retrieves all payments that match the given predicate. diff --git a/tests/common.rs b/tests/common.rs index a1e1e993c..85fdaac56 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -333,8 +333,8 @@ pub(crate) fn do_channel_full_cycle( ); node_a.sync_wallets().unwrap(); node_b.sync_wallets().unwrap(); - assert_eq!(node_a.spendable_onchain_balance_sats().unwrap(), premine_amount_sat); - assert_eq!(node_b.spendable_onchain_balance_sats().unwrap(), premine_amount_sat); + assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); + assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); // Check we haven't got any events yet assert_eq!(node_a.next_event(), None); @@ -371,9 +371,9 @@ pub(crate) fn do_channel_full_cycle( let onchain_fee_buffer_sat = 1500; let node_a_upper_bound_sat = premine_amount_sat - funding_amount_sat; let node_a_lower_bound_sat = premine_amount_sat - funding_amount_sat - onchain_fee_buffer_sat; - assert!(node_a.spendable_onchain_balance_sats().unwrap() < node_a_upper_bound_sat); - assert!(node_a.spendable_onchain_balance_sats().unwrap() > node_a_lower_bound_sat); - assert_eq!(node_b.spendable_onchain_balance_sats().unwrap(), premine_amount_sat); + assert!(node_a.list_balances().spendable_onchain_balance_sats < node_a_upper_bound_sat); + assert!(node_a.list_balances().spendable_onchain_balance_sats > node_a_lower_bound_sat); + assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); expect_channel_ready_event!(node_a, node_b.node_id()); @@ -539,10 +539,13 @@ pub(crate) fn do_channel_full_cycle( let node_a_upper_bound_sat = (premine_amount_sat - funding_amount_sat) + (funding_amount_sat - sum_of_all_payments_sat); let node_a_lower_bound_sat = node_a_upper_bound_sat - onchain_fee_buffer_sat; - assert!(node_a.spendable_onchain_balance_sats().unwrap() > node_a_lower_bound_sat); - assert!(node_a.spendable_onchain_balance_sats().unwrap() < node_a_upper_bound_sat); + assert!(node_a.list_balances().spendable_onchain_balance_sats > node_a_lower_bound_sat); + assert!(node_a.list_balances().spendable_onchain_balance_sats < node_a_upper_bound_sat); let expected_final_amount_node_b_sat = premine_amount_sat + sum_of_all_payments_sat; - assert_eq!(node_b.spendable_onchain_balance_sats().unwrap(), expected_final_amount_node_b_sat); + assert_eq!( + node_b.list_balances().spendable_onchain_balance_sats, + expected_final_amount_node_b_sat + ); // Check we handled all events assert_eq!(node_a.next_event(), None); diff --git a/tests/integration_tests_rust.rs b/tests/integration_tests_rust.rs index a5b5102b0..55e3dc553 100644 --- a/tests/integration_tests_rust.rs +++ b/tests/integration_tests_rust.rs @@ -44,8 +44,8 @@ fn channel_open_fails_when_funds_insufficient() { ); node_a.sync_wallets().unwrap(); node_b.sync_wallets().unwrap(); - assert_eq!(node_a.spendable_onchain_balance_sats().unwrap(), premine_amount_sat); - assert_eq!(node_b.spendable_onchain_balance_sats().unwrap(), premine_amount_sat); + assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, premine_amount_sat); + assert_eq!(node_b.list_balances().spendable_onchain_balance_sats, premine_amount_sat); println!("\nA -- connect_open_channel -> B"); assert_eq!( @@ -88,7 +88,7 @@ fn multi_hop_sending() { for n in &nodes { n.sync_wallets().unwrap(); - assert_eq!(n.spendable_onchain_balance_sats().unwrap(), premine_amount_sat); + assert_eq!(n.list_balances().spendable_onchain_balance_sats, premine_amount_sat); assert_eq!(n.next_event(), None); } @@ -168,7 +168,7 @@ fn start_stop_reinit() { let funding_address = node.new_onchain_address().unwrap(); - assert_eq!(node.total_onchain_balance_sats().unwrap(), 0); + assert_eq!(node.list_balances().total_onchain_balance_sats, 0); let expected_amount = Amount::from_sat(100000); premine_and_distribute_funds( @@ -179,7 +179,7 @@ fn start_stop_reinit() { ); node.sync_wallets().unwrap(); - assert_eq!(node.spendable_onchain_balance_sats().unwrap(), expected_amount.to_sat()); + assert_eq!(node.list_balances().spendable_onchain_balance_sats, expected_amount.to_sat()); let log_file_symlink = format!("{}/logs/ldk_node_latest.log", config.clone().storage_dir_path); assert!(std::path::Path::new(&log_file_symlink).is_symlink()); @@ -202,13 +202,13 @@ fn start_stop_reinit() { assert_eq!(reinitialized_node.node_id(), expected_node_id); assert_eq!( - reinitialized_node.spendable_onchain_balance_sats().unwrap(), + reinitialized_node.list_balances().spendable_onchain_balance_sats, expected_amount.to_sat() ); reinitialized_node.sync_wallets().unwrap(); assert_eq!( - reinitialized_node.spendable_onchain_balance_sats().unwrap(), + reinitialized_node.list_balances().spendable_onchain_balance_sats, expected_amount.to_sat() ); @@ -232,7 +232,7 @@ fn onchain_spend_receive() { node_a.sync_wallets().unwrap(); node_b.sync_wallets().unwrap(); - assert_eq!(node_b.spendable_onchain_balance_sats().unwrap(), 100000); + 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)); @@ -243,9 +243,9 @@ fn onchain_spend_receive() { node_a.sync_wallets().unwrap(); node_b.sync_wallets().unwrap(); - assert_eq!(node_a.spendable_onchain_balance_sats().unwrap(), 1000); - assert!(node_b.spendable_onchain_balance_sats().unwrap() > 98000); - assert!(node_b.spendable_onchain_balance_sats().unwrap() < 100000); + assert_eq!(node_a.list_balances().spendable_onchain_balance_sats, 1000); + 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(); @@ -255,9 +255,9 @@ fn onchain_spend_receive() { node_a.sync_wallets().unwrap(); node_b.sync_wallets().unwrap(); - assert_eq!(node_a.total_onchain_balance_sats().unwrap(), 0); - assert!(node_b.spendable_onchain_balance_sats().unwrap() > 99000); - assert!(node_b.spendable_onchain_balance_sats().unwrap() < 100000); + assert_eq!(node_a.list_balances().total_onchain_balance_sats, 0); + assert!(node_b.list_balances().spendable_onchain_balance_sats > 99000); + assert!(node_b.list_balances().spendable_onchain_balance_sats < 100000); } #[test]