Skip to content

Commit 4c92313

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 4c92313

File tree

2 files changed

+266
-5
lines changed

2 files changed

+266
-5
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+
MaybePreimageClaimableHTLC {
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 will be able to claim the balance if we have not
597+
/// yet received the 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::MaybePreimageClaimableHTLC {
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::MaybePreimageClaimableHTLC {
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: 241 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,10 +288,16 @@ fn do_test_claim_value_force_close(prev_commitment_tx: bool) {
288288
claimable_height: htlc_cltv_timeout,
289289
}]),
290290
sorted_vec(nodes[0].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
291-
assert_eq!(vec![Balance::ClaimableOnChannelClose {
291+
assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
292292
claimable_amount_satoshis: 1_000,
293-
}],
294-
nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances());
293+
}, Balance::MaybePreimageClaimableHTLC {
294+
claimable_amount_satoshis: 3_000,
295+
expiry_height: htlc_cltv_timeout,
296+
}, Balance::MaybePreimageClaimableHTLC {
297+
claimable_amount_satoshis: 4_000,
298+
expiry_height: htlc_cltv_timeout,
299+
}]),
300+
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
295301

296302
nodes[1].node.claim_funds(payment_preimage);
297303
check_added_monitors!(nodes[1], 1);
@@ -747,6 +753,238 @@ fn test_balances_on_local_commitment_htlcs() {
747753
test_spendable_output(&nodes[0], &as_txn[1]);
748754
}
749755

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

0 commit comments

Comments
 (0)