Skip to content

Commit 190e6be

Browse files
committed
Expose a Balance for inbound HTLCs even without a preimage
If we don't currently have the preimage for an inbound HTLC, that does not guarantee we can never claim it, but instead only that we cannot claim it unless we receive the preimage from the channel we forwarded the channel out on. Thus, we cannot consider a channel to have no claimable balances if the only remaining output on the commitment ransaction is an inbound HTLC for which we do not have the preimage, as we may be able to claim it in the future. This commit addresses this issue by adding a new `Balance` variant - `MaybePreimageClaimableHTLCAwaitingTimeout`, which is generated until the HTLC output is spent. Fixes #1620
1 parent dc54c58 commit 190e6be

File tree

2 files changed

+258
-2
lines changed

2 files changed

+258
-2
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -579,13 +579,24 @@ pub enum Balance {
579579
/// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
580580
/// likely to be claimed by our counterparty before we do.
581581
MaybeClaimableHTLCAwaitingTimeout {
582-
/// The amount available to claim, in satoshis, excluding the on-chain fees which will be
583-
/// required to do so.
582+
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
583+
/// which will be required to do so.
584584
claimable_amount_satoshis: u64,
585585
/// The height at which we will be able to claim the balance if our counterparty has not
586586
/// done so.
587587
claimable_height: u32,
588588
},
589+
/// HTLCs which we received from our counterparty which are claimable with a preimage which we
590+
/// do not currently have. This will only be claimable if we receive the preimage from the node
591+
/// to which we forwarded this HTLC before the timeout.
592+
MaybePreimageClaimableHTLCAwaitingTimeout {
593+
/// The amount potentially available to claim, in satoshis, excluding the on-chain fees
594+
/// which will be required to do so.
595+
claimable_amount_satoshis: u64,
596+
/// The height at which our counterparty claim the balance if we have not yet received a
597+
/// preimage and claimed it ourselves.
598+
expiry_height: u32,
599+
},
589600
/// The channel has been closed, and our counterparty broadcasted a revoked commitment
590601
/// transaction.
591602
///
@@ -1565,6 +1576,11 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
15651576
timeout_height: htlc.cltv_expiry,
15661577
});
15671578
}
1579+
} else if htlc_resolved.is_none() {
1580+
return Some(Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
1581+
claimable_amount_satoshis: htlc.amount_msat / 1000,
1582+
expiry_height: htlc.cltv_expiry,
1583+
});
15681584
}
15691585
None
15701586
}
@@ -1728,6 +1744,13 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
17281744
});
17291745
} else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
17301746
claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
1747+
} else {
1748+
// As long as the HTLC is still in our latest commitment state, treat
1749+
// it as potentially claimable, even if it has long-since expired.
1750+
res.push(Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
1751+
claimable_amount_satoshis: htlc.amount_msat / 1000,
1752+
expiry_height: htlc.cltv_expiry,
1753+
});
17311754
}
17321755
}
17331756
res.push(Balance::ClaimableOnChannelClose {

lightning/src/ln/monitor_tests.rs

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,239 @@ fn test_balances_on_local_commitment_htlcs() {
747747
test_spendable_output(&nodes[0], &as_txn[1]);
748748
}
749749

750+
#[test]
751+
fn test_no_preimage_inbound_htlc_balances() {
752+
// Tests that MaybePreimageCLaimableHTLCAwaitingTImeouts are generated for inbound HTLCs for
753+
// which we do not have a preimage.
754+
let chanmon_cfgs = create_chanmon_cfgs(2);
755+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
756+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
757+
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
758+
759+
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known());
760+
let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
761+
762+
// Send two HTLCs, one from A to B, and one from B to C.
763+
let to_b_failed_payment_hash = route_payment(&nodes[0], &[&nodes[1]], 10_000_000).1;
764+
let to_a_failed_payment_hash = route_payment(&nodes[1], &[&nodes[0]], 20_000_000).1;
765+
let htlc_cltv_timeout = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 1; // Note ChannelManager adds one to CLTV timeouts for safety
766+
767+
let chan_feerate = get_feerate!(nodes[0], chan_id) as u64;
768+
let opt_anchors = get_opt_anchors!(nodes[0], chan_id);
769+
770+
// Both A and B will have an HTLC that's claimable on timeout and one that's claimable if they
771+
// receive the preimage. These will remain the same through the channel closure and until the
772+
// HTLC output is spent.
773+
774+
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
775+
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
776+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
777+
}, Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
778+
claimable_amount_satoshis: 20_000,
779+
expiry_height: htlc_cltv_timeout,
780+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
781+
claimable_amount_satoshis: 10_000,
782+
claimable_height: htlc_cltv_timeout,
783+
}]),
784+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
785+
786+
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
787+
claimable_amount_satoshis: 500_000 - 20_000,
788+
}, Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
789+
claimable_amount_satoshis: 10_000,
790+
expiry_height: htlc_cltv_timeout,
791+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
792+
claimable_amount_satoshis: 20_000,
793+
claimable_height: htlc_cltv_timeout,
794+
}]),
795+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
796+
797+
// Get nodes[0]'s commitment transaction and HTLC-Timeout transaction
798+
let as_txn = get_local_commitment_txn!(nodes[0], chan_id);
799+
assert_eq!(as_txn.len(), 2);
800+
check_spends!(as_txn[1], as_txn[0]);
801+
check_spends!(as_txn[0], funding_tx);
802+
803+
// Now close the channel by confirming A's commitment transaction on both nodes, checking the
804+
// claimable balances remain the same except for the non-HTLC balance changing variant.
805+
let node_a_commitment_claimable = nodes[0].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
806+
let as_pre_spend_claims = sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
807+
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
808+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
809+
confirmation_height: node_a_commitment_claimable,
810+
}, Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
811+
claimable_amount_satoshis: 20_000,
812+
expiry_height: htlc_cltv_timeout,
813+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
814+
claimable_amount_satoshis: 10_000,
815+
claimable_height: htlc_cltv_timeout,
816+
}]);
817+
818+
mine_transaction(&nodes[0], &as_txn[0]);
819+
nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
820+
check_added_monitors!(nodes[0], 1);
821+
check_closed_broadcast!(nodes[0], true);
822+
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
823+
824+
assert_eq!(as_pre_spend_claims,
825+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
826+
827+
mine_transaction(&nodes[1], &as_txn[0]);
828+
check_added_monitors!(nodes[1], 1);
829+
check_closed_broadcast!(nodes[1], true);
830+
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
831+
832+
let node_b_commitment_claimable = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
833+
let mut bs_pre_spend_claims = sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
834+
claimable_amount_satoshis: 500_000 - 20_000,
835+
confirmation_height: node_b_commitment_claimable,
836+
}, Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
837+
claimable_amount_satoshis: 10_000,
838+
expiry_height: htlc_cltv_timeout,
839+
}, Balance::MaybeClaimableHTLCAwaitingTimeout {
840+
claimable_amount_satoshis: 20_000,
841+
claimable_height: htlc_cltv_timeout,
842+
}]);
843+
assert_eq!(bs_pre_spend_claims,
844+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
845+
846+
// If we get one block prior to the HTLC expiring, we'll broadcast the HTLC-timeout transaction
847+
// (as it is confirmable in the next block), but will still include the same claimable
848+
// balances as no HTLC has been spent, even after the HTLC expires. We'll also fail the inbound
849+
// HTLC, but it won't do anything as the channel is already closed.
850+
851+
connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1);
852+
let as_htlc_timeout_claim = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
853+
assert_eq!(as_htlc_timeout_claim.len(), 1);
854+
check_spends!(as_htlc_timeout_claim[0], as_txn[0]);
855+
expect_pending_htlcs_forwardable_conditions!(nodes[0],
856+
[HTLCDestination::FailedPayment { payment_hash: to_a_failed_payment_hash }]);
857+
858+
assert_eq!(as_pre_spend_claims,
859+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
860+
861+
connect_blocks(&nodes[0], 1);
862+
assert_eq!(as_pre_spend_claims,
863+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
864+
865+
// For node B, we'll get the non-HTLC funds claimable after ANTI_REORG_DELAY confirmations
866+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
867+
test_spendable_output(&nodes[1], &as_txn[0]);
868+
bs_pre_spend_claims.retain(|e| if let Balance::ClaimableAwaitingConfirmations { .. } = e { false } else { true });
869+
870+
// The next few blocks for B look the same as for A, though for the opposite HTLC
871+
nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clear();
872+
connect_blocks(&nodes[1], TEST_FINAL_CLTV - (ANTI_REORG_DELAY - 1) - 1);
873+
expect_pending_htlcs_forwardable_conditions!(nodes[1],
874+
[HTLCDestination::FailedPayment { payment_hash: to_b_failed_payment_hash }]);
875+
let bs_htlc_timeout_claim = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
876+
assert_eq!(bs_htlc_timeout_claim.len(), 1);
877+
check_spends!(bs_htlc_timeout_claim[0], as_txn[0]);
878+
879+
assert_eq!(bs_pre_spend_claims,
880+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
881+
882+
connect_blocks(&nodes[1], 1);
883+
assert_eq!(bs_pre_spend_claims,
884+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
885+
886+
// Now confirm the two HTLC timeout transactions for A, checking that the inbound HTLC resolves
887+
// after ANTI_REORG_DELAY confirmations and the other takes BREAKDOWN_TIMEOUT confirmations.
888+
mine_transaction(&nodes[0], &as_htlc_timeout_claim[0]);
889+
let as_timeout_claimable_height = nodes[0].best_block_info().1 + (BREAKDOWN_TIMEOUT as u32) - 1;
890+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
891+
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
892+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
893+
confirmation_height: node_a_commitment_claimable,
894+
}, Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
895+
claimable_amount_satoshis: 20_000,
896+
expiry_height: htlc_cltv_timeout,
897+
}, Balance::ClaimableAwaitingConfirmations {
898+
claimable_amount_satoshis: 10_000,
899+
confirmation_height: as_timeout_claimable_height,
900+
}]),
901+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
902+
assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
903+
904+
mine_transaction(&nodes[0], &bs_htlc_timeout_claim[0]);
905+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
906+
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
907+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
908+
confirmation_height: node_a_commitment_claimable,
909+
}, Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
910+
claimable_amount_satoshis: 20_000,
911+
expiry_height: htlc_cltv_timeout,
912+
}, Balance::ClaimableAwaitingConfirmations {
913+
claimable_amount_satoshis: 10_000,
914+
confirmation_height: as_timeout_claimable_height,
915+
}]),
916+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
917+
918+
// Once as_htlc_timeout_claim[0] reaches ANTI_REORG_DELAY confirmations, we should get a
919+
// payment failure event.
920+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 2);
921+
expect_payment_failed!(nodes[0], to_b_failed_payment_hash, true);
922+
923+
connect_blocks(&nodes[0], 1);
924+
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
925+
claimable_amount_satoshis: 1_000_000 - 500_000 - 10_000 - chan_feerate *
926+
(channel::commitment_tx_base_weight(opt_anchors) + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
927+
confirmation_height: node_a_commitment_claimable,
928+
}, Balance::ClaimableAwaitingConfirmations {
929+
claimable_amount_satoshis: 10_000,
930+
confirmation_height: core::cmp::max(as_timeout_claimable_height, htlc_cltv_timeout),
931+
}]),
932+
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
933+
934+
connect_blocks(&nodes[0], node_a_commitment_claimable - nodes[0].best_block_info().1);
935+
assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
936+
claimable_amount_satoshis: 10_000,
937+
confirmation_height: core::cmp::max(as_timeout_claimable_height, htlc_cltv_timeout),
938+
}],
939+
nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
940+
test_spendable_output(&nodes[0], &as_txn[0]);
941+
942+
connect_blocks(&nodes[0], as_timeout_claimable_height - nodes[0].best_block_info().1);
943+
assert!(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
944+
test_spendable_output(&nodes[0], &as_htlc_timeout_claim[0]);
945+
946+
// The process for B should be completely identical as well, noting that the non-HTLC-balance
947+
// was already claimed.
948+
mine_transaction(&nodes[1], &bs_htlc_timeout_claim[0]);
949+
let bs_timeout_claimable_height = nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1;
950+
assert_eq!(sorted_vec(vec![Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
951+
claimable_amount_satoshis: 10_000,
952+
expiry_height: htlc_cltv_timeout,
953+
}, Balance::ClaimableAwaitingConfirmations {
954+
claimable_amount_satoshis: 20_000,
955+
confirmation_height: bs_timeout_claimable_height,
956+
}]),
957+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
958+
959+
mine_transaction(&nodes[1], &as_htlc_timeout_claim[0]);
960+
assert_eq!(sorted_vec(vec![Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
961+
claimable_amount_satoshis: 10_000,
962+
expiry_height: htlc_cltv_timeout,
963+
}, Balance::ClaimableAwaitingConfirmations {
964+
claimable_amount_satoshis: 20_000,
965+
confirmation_height: bs_timeout_claimable_height,
966+
}]),
967+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
968+
969+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 2);
970+
expect_payment_failed!(nodes[1], to_a_failed_payment_hash, true);
971+
972+
assert_eq!(vec![Balance::MaybePreimageClaimableHTLCAwaitingTimeout {
973+
claimable_amount_satoshis: 10_000,
974+
expiry_height: htlc_cltv_timeout,
975+
}],
976+
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
977+
test_spendable_output(&nodes[1], &bs_htlc_timeout_claim[0]);
978+
979+
connect_blocks(&nodes[1], 1);
980+
assert!(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances().is_empty());
981+
}
982+
750983
fn sorted_vec_with_additions<T: Ord + Clone>(v_orig: &Vec<T>, extra_ts: &[&T]) -> Vec<T> {
751984
let mut v = v_orig.clone();
752985
for t in extra_ts {

0 commit comments

Comments
 (0)