Skip to content

Commit c0dc177

Browse files
ddustinrustyrussell
authored andcommitted
Splice: Better balance checking
* Regression test added for Issue ElementsProject#6572 (issuecomment-1730808863) w/stuck HTLC * `check_balance` adjusted to calculate pending HTLCs explicitly * Test confirmed to fail prior to PR ElementsProject#6713 ChangeLog-Fixed: Issue splicing with pending / stuck HTLCs fixed.
1 parent d823eea commit c0dc177

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

channeld/channeld.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2961,13 +2961,35 @@ static struct amount_sat check_balances(struct peer *peer,
29612961
funding_amount_res, min_multiplied;
29622962
struct amount_msat funding_amount,
29632963
initiator_fee, accepter_fee;
2964-
struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES];
2964+
struct amount_msat in[NUM_TX_ROLES], out[NUM_TX_ROLES],
2965+
pending_htlcs[NUM_TX_ROLES];
2966+
struct htlc_map_iter it;
2967+
const struct htlc *htlc;
29652968
bool opener = our_role == TX_INITIATOR;
29662969
u8 *msg;
29672970

2971+
/* The channel funds less any pending htlcs */
29682972
in[TX_INITIATOR] = peer->channel->view->owed[opener ? LOCAL : REMOTE];
29692973
in[TX_ACCEPTER] = peer->channel->view->owed[opener ? REMOTE : LOCAL];
29702974

2975+
/* pending_htlcs holds the value of all pending htlcs for each side */
2976+
pending_htlcs[TX_INITIATOR] = AMOUNT_MSAT(0);
2977+
pending_htlcs[TX_ACCEPTER] = AMOUNT_MSAT(0);
2978+
for (htlc = htlc_map_first(peer->channel->htlcs, &it);
2979+
htlc;
2980+
htlc = htlc_map_next(peer->channel->htlcs, &it)) {
2981+
struct amount_msat *itr;
2982+
2983+
if (htlc_owner(htlc) == opener ? LOCAL : REMOTE)
2984+
itr = &pending_htlcs[TX_INITIATOR];
2985+
else
2986+
itr = &pending_htlcs[TX_ACCEPTER];
2987+
2988+
if (!amount_msat_add(itr, *itr, htlc->amount))
2989+
peer_failed_warn(peer->pps, &peer->channel_id,
2990+
"Unable to add HTLC balance");
2991+
}
2992+
29712993
for (size_t i = 0; i < psbt->num_inputs; i++)
29722994
if (i != chan_input_index)
29732995
add_amount_to_side(peer, in,
@@ -2985,12 +3007,22 @@ static struct amount_sat check_balances(struct peer *peer,
29853007
psbt_output_get_amount(psbt, i),
29863008
&psbt->outputs[i].unknowns);
29873009

2988-
/* Calculate total channel output amount */
3010+
/* Calculate original channel output amount */
29893011
if (!amount_msat_add(&funding_amount,
29903012
peer->channel->view->owed[LOCAL],
29913013
peer->channel->view->owed[REMOTE]))
29923014
peer_failed_warn(peer->pps, &peer->channel_id,
29933015
"Unable to calculate starting channel amount");
3016+
if (!amount_msat_add(&funding_amount,
3017+
funding_amount,
3018+
pending_htlcs[TX_INITIATOR]))
3019+
peer_failed_warn(peer->pps, &peer->channel_id,
3020+
"Unable to calculate starting channel amount");
3021+
if (!amount_msat_add(&funding_amount,
3022+
funding_amount,
3023+
pending_htlcs[TX_ACCEPTER]))
3024+
peer_failed_warn(peer->pps, &peer->channel_id,
3025+
"Unable to calculate starting channel amount");
29943026

29953027
/* Tasks:
29963028
* Add up total funding_amount
@@ -3033,9 +3065,13 @@ static struct amount_sat check_balances(struct peer *peer,
30333065
peer_failed_warn(peer->pps, &peer->channel_id,
30343066
"Initiator funding is less than commited"
30353067
" amount. Initiator contributing %s but they"
3036-
" committed to %s.",
3068+
" committed to %s. Pending offered HTLC"
3069+
" balance of %s is not available for this"
3070+
" operation.",
30373071
fmt_amount_msat(tmpctx, in[TX_INITIATOR]),
3038-
fmt_amount_msat(tmpctx, out[TX_INITIATOR]));
3072+
fmt_amount_msat(tmpctx, out[TX_INITIATOR]),
3073+
fmt_amount_msat(tmpctx,
3074+
pending_htlcs[TX_INITIATOR]));
30393075
}
30403076

30413077
if (!amount_msat_sub(&initiator_fee, in[TX_INITIATOR], out[TX_INITIATOR]))
@@ -3051,9 +3087,13 @@ static struct amount_sat check_balances(struct peer *peer,
30513087
peer_failed_warn(peer->pps, &peer->channel_id,
30523088
"Accepter funding is less than commited"
30533089
" amount. Accepter contributing %s but they"
3054-
" committed to %s.",
3090+
" committed to %s. Pending offered HTLC"
3091+
" balance of %s is not available for this"
3092+
" operation.",
30553093
fmt_amount_msat(tmpctx, in[TX_INITIATOR]),
3056-
fmt_amount_msat(tmpctx, out[TX_INITIATOR]));
3094+
fmt_amount_msat(tmpctx, out[TX_INITIATOR]),
3095+
fmt_amount_msat(tmpctx,
3096+
pending_htlcs[TX_INITIATOR]));
30573097
}
30583098

30593099
if (!amount_msat_sub(&accepter_fee, in[TX_ACCEPTER], out[TX_ACCEPTER]))

tests/test_splicing.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,3 +276,44 @@ def test_commit_crash_splice(node_factory, bitcoind):
276276
# Check that the splice doesn't generate a unilateral close transaction
277277
time.sleep(5)
278278
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0
279+
280+
281+
@pytest.mark.openchannel('v1')
282+
@pytest.mark.openchannel('v2')
283+
@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need')
284+
def test_splice_stuck_htlc(node_factory, bitcoind, executor):
285+
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, opts={'experimental-splicing': None})
286+
287+
l3.rpc.dev_ignore_htlcs(id=l2.info['id'], ignore=True)
288+
289+
inv = l3.rpc.invoice(10000000, '1', 'no_1')
290+
executor.submit(l1.rpc.pay, inv['bolt11'])
291+
l3.daemon.wait_for_log('their htlc 0 dev_ignore_htlcs')
292+
293+
# Now we should have a stuck invoice between l1 -> l2
294+
295+
chan_id = l1.get_channel_id(l2)
296+
297+
# add extra sats to pay fee
298+
funds_result = l1.rpc.fundpsbt("109000sat", "slow", 166, excess_as_change=True)
299+
300+
result = l1.rpc.splice_init(chan_id, 100000, funds_result['psbt'])
301+
result = l1.rpc.splice_update(chan_id, result['psbt'])
302+
result = l1.rpc.signpsbt(result['psbt'])
303+
result = l1.rpc.splice_signed(chan_id, result['signed_psbt'])
304+
305+
l2.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
306+
l1.daemon.wait_for_log(r'CHANNELD_NORMAL to CHANNELD_AWAITING_SPLICE')
307+
308+
mempool = bitcoind.rpc.getrawmempool(True)
309+
assert len(list(mempool.keys())) == 1
310+
assert result['txid'] in list(mempool.keys())
311+
312+
bitcoind.generate_block(6, wait_for_mempool=1)
313+
314+
l2.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
315+
l1.daemon.wait_for_log(r'CHANNELD_AWAITING_SPLICE to CHANNELD_NORMAL')
316+
317+
# Check that the splice doesn't generate a unilateral close transaction
318+
time.sleep(5)
319+
assert l1.db_query("SELECT count(*) as c FROM channeltxs;")[0]['c'] == 0

0 commit comments

Comments
 (0)