Skip to content

Commit 5135534

Browse files
jkczyzclaude
andcommitted
Cover splice-out classification and funding-payment reorg
splice_channel only checked the splice-out fee; also assert it is recorded as a confirmed interactive-funding payment. Add a test that a confirmed splice payment returns to unconfirmed when its block is reorged out, exercising the unconfirm path for funding payments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent e541265 commit 5135534

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

tests/integration_tests_rust.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,18 @@ async fn splice_channel() {
13061306
let payment =
13071307
payments.into_iter().find(|p| p.id == PaymentId(txo.txid.to_byte_array())).unwrap();
13081308
assert_eq!(payment.fee_paid_msat, Some(expected_splice_out_fee_sat * 1_000));
1309+
// The splice-out graduated to a confirmed interactive-funding payment. Its `direction` is left
1310+
// unasserted on purpose: the destination is our own address, so it is a self-transfer (channel
1311+
// balance -> on-chain wallet) whose inbound/outbound sense is ambiguous.
1312+
assert_eq!(payment.status, PaymentStatus::Succeeded);
1313+
assert!(matches!(
1314+
payment.kind,
1315+
PaymentKind::Onchain {
1316+
status: ConfirmationStatus::Confirmed { .. },
1317+
tx_type: Some(TransactionType::InteractiveFunding { .. }),
1318+
..
1319+
}
1320+
));
13091321

13101322
assert_eq!(
13111323
node_a.list_balances().total_onchain_balance_sats,
@@ -1601,6 +1613,80 @@ async fn funding_payment_graduates_without_channel_ready() {
16011613
node_b.stop().unwrap();
16021614
}
16031615

1616+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
1617+
async fn splice_payment_reorged_to_unconfirmed() {
1618+
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
1619+
let chain_source = random_chain_source(&bitcoind, &electrsd);
1620+
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);
1621+
1622+
let address_a = node_a.onchain_payment().new_address().unwrap();
1623+
let address_b = node_b.onchain_payment().new_address().unwrap();
1624+
let premine_amount_sat = 5_000_000;
1625+
premine_and_distribute_funds(
1626+
&bitcoind.client,
1627+
&electrsd.client,
1628+
vec![address_a, address_b],
1629+
Amount::from_sat(premine_amount_sat),
1630+
)
1631+
.await;
1632+
1633+
node_a.sync_wallets().unwrap();
1634+
node_b.sync_wallets().unwrap();
1635+
1636+
open_channel(&node_a, &node_b, 4_000_000, false, &electrsd).await;
1637+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
1638+
node_a.sync_wallets().unwrap();
1639+
node_b.sync_wallets().unwrap();
1640+
1641+
let _user_channel_id_a = expect_channel_ready_event!(node_a, node_b.node_id());
1642+
let user_channel_id_b = expect_channel_ready_event!(node_b, node_a.node_id());
1643+
1644+
// node_b splices in, recording a funding payment it contributed to.
1645+
node_b.splice_in(&user_channel_id_b, node_a.node_id(), 1_000_000).unwrap();
1646+
let splice_txo = expect_splice_negotiated_event!(node_a, node_b.node_id());
1647+
expect_splice_negotiated_event!(node_b, node_a.node_id());
1648+
wait_for_tx(&electrsd.client, splice_txo.txid).await;
1649+
1650+
// Confirm the splice with a single block — confirmed, but short of `ANTI_REORG_DELAY`, so the
1651+
// payment is `Confirmed`/`Pending` rather than graduated.
1652+
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 1).await;
1653+
node_b.sync_wallets().unwrap();
1654+
1655+
let payment_id = PaymentId(splice_txo.txid.to_byte_array());
1656+
let payment = node_b.payment(&payment_id).expect("splice payment exists");
1657+
assert_eq!(payment.status, PaymentStatus::Pending);
1658+
assert!(matches!(
1659+
payment.kind,
1660+
PaymentKind::Onchain { status: ConfirmationStatus::Confirmed { .. }, .. }
1661+
));
1662+
1663+
// Reorg the splice transaction out by replacing its block with a longer, transaction-free chain.
1664+
let original_height =
1665+
bitcoind.client.get_blockchain_info().expect("failed to get blockchain info").blocks;
1666+
invalidate_blocks(&bitcoind.client, 1);
1667+
let replacement_address = bitcoind.client.new_address().expect("failed to get new address");
1668+
for _ in 0..2 {
1669+
let _res: serde_json::Value = bitcoind
1670+
.client
1671+
.call("generateblock", &[json!(replacement_address.to_string()), json!([])])
1672+
.expect("failed to generate empty block");
1673+
}
1674+
wait_for_block(&electrsd.client, original_height as usize + 1).await;
1675+
node_b.sync_wallets().unwrap();
1676+
1677+
// The funding payment returns to `Unconfirmed` and stays `Pending`, exercising the
1678+
// `TxUnconfirmed` arm for a funding payment.
1679+
let payment = node_b.payment(&payment_id).expect("splice payment still exists");
1680+
assert_eq!(payment.status, PaymentStatus::Pending);
1681+
assert!(matches!(
1682+
payment.kind,
1683+
PaymentKind::Onchain { status: ConfirmationStatus::Unconfirmed, .. }
1684+
));
1685+
1686+
node_a.stop().unwrap();
1687+
node_b.stop().unwrap();
1688+
}
1689+
16041690
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
16051691
async fn simple_bolt12_send_receive() {
16061692
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();

0 commit comments

Comments
 (0)