Skip to content

Commit 8689530

Browse files
committed
add test for handling failed RGB forward swaps
1 parent 858c61c commit 8689530

4 files changed

Lines changed: 127 additions & 3 deletions

File tree

src/ldk.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ pub(crate) const FEE_RATE: u64 = 7;
111111
pub(crate) const UTXO_SIZE_SAT: u32 = 32000;
112112
pub(crate) const MIN_CHANNEL_CONFIRMATIONS: u8 = 6;
113113

114+
#[cfg(test)]
115+
pub(crate) static FORCE_NEXT_INTERCEPTED_SWAP_RGB_FORWARD_FAILURE: AtomicBool =
116+
AtomicBool::new(false);
117+
118+
#[cfg(test)]
119+
const TEST_RGB_FORWARD_FAILURE_EXCESS_AMOUNT: u64 = 1;
120+
114121
pub(crate) struct LdkBackgroundServices {
115122
stop_processing: Arc<AtomicBool>,
116123
peer_manager: Arc<PeerManager>,
@@ -1276,14 +1283,31 @@ async fn handle_ldk_events(
12761283
tracing::debug!("Swap is whitelisted, forwarding the htlc...");
12771284
unlocked_state.update_taker_swap_status(&payment_hash, SwapStatus::Pending);
12781285

1286+
let outbound_rgb_payment = expected_outbound_rgb_payment;
1287+
#[cfg(test)]
1288+
let outbound_rgb_payment = {
1289+
let mut outbound_rgb_payment = outbound_rgb_payment;
1290+
if FORCE_NEXT_INTERCEPTED_SWAP_RGB_FORWARD_FAILURE.swap(false, Ordering::SeqCst) {
1291+
if let (Some((contract_id, _)), Some((_, local_rgb_amount, _))) =
1292+
(outbound_rgb_payment, outbound_rgb_info)
1293+
{
1294+
outbound_rgb_payment = Some((
1295+
contract_id,
1296+
local_rgb_amount.saturating_add(TEST_RGB_FORWARD_FAILURE_EXCESS_AMOUNT),
1297+
));
1298+
}
1299+
}
1300+
outbound_rgb_payment
1301+
};
1302+
12791303
unlocked_state
12801304
.channel_manager
12811305
.forward_intercepted_htlc(
12821306
intercept_id,
12831307
channelmanager::NextHopForward::ShortChannelId(requested_next_hop_scid),
12841308
outbound_channel.counterparty.node_id,
12851309
expected_outbound_amount_msat,
1286-
expected_outbound_rgb_payment,
1310+
outbound_rgb_payment,
12871311
)
12881312
.expect("Forward should be valid");
12891313
}

src/routes.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3092,9 +3092,9 @@ pub(crate) async fn node_info(
30923092
let pending_payments_map = |b| match b {
30933093
&Balance::MaybeTimeoutClaimableHTLC {
30943094
amount_satoshis,
3095-
outbound_payment,
3095+
outbound_payment: true,
30963096
..
3097-
} if outbound_payment => amount_satoshis,
3097+
} => amount_satoshis,
30983098
_ => 0,
30993099
};
31003100
let pending_outbound_payments_sat = balances.iter().map(pending_payments_map).sum::<u64>();

src/test/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2029,6 +2029,7 @@ mod swap_roundtrip_buy_same_channel;
20292029
mod swap_roundtrip_fail_amount_maker;
20302030
mod swap_roundtrip_fail_amount_taker;
20312031
mod swap_roundtrip_fail_btc2btc;
2032+
mod swap_roundtrip_fail_forward;
20322033
mod swap_roundtrip_fail_invalid_asset_from;
20332034
mod swap_roundtrip_fail_invalid_asset_to;
20342035
mod swap_roundtrip_fail_same_asset;
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use super::*;
2+
3+
const TEST_DIR_BASE: &str = "tmp/swap_roundtrip_fail_forward/";
4+
5+
#[serial_test::serial]
6+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
7+
#[traced_test]
8+
async fn swap_fail_forward_marks_taker_failed() {
9+
initialize();
10+
11+
let test_dir_node1 = format!("{TEST_DIR_BASE}node1");
12+
let test_dir_node2 = format!("{TEST_DIR_BASE}node2");
13+
let (node1_addr, _) = start_node(&test_dir_node1, NODE1_PEER_PORT, false).await;
14+
let (node2_addr, _) = start_node(&test_dir_node2, NODE2_PEER_PORT, false).await;
15+
16+
fund_and_create_utxos(node1_addr, None).await;
17+
fund_and_create_utxos(node2_addr, None).await;
18+
19+
let asset_id = issue_asset_nia(node2_addr).await.asset_id;
20+
21+
let node1_pubkey = node_info(node1_addr).await.pubkey;
22+
let node2_pubkey = node_info(node2_addr).await.pubkey;
23+
24+
open_channel(
25+
node1_addr,
26+
&node2_pubkey,
27+
Some(NODE2_PEER_PORT),
28+
Some(5000000),
29+
Some(546000),
30+
None,
31+
None,
32+
)
33+
.await;
34+
open_channel(
35+
node2_addr,
36+
&node1_pubkey,
37+
Some(NODE1_PEER_PORT),
38+
None,
39+
None,
40+
Some(600),
41+
Some(&asset_id),
42+
)
43+
.await;
44+
wait_for_usable_channels(node1_addr, 2).await;
45+
wait_for_usable_channels(node2_addr, 2).await;
46+
47+
println!("\nsetup swap");
48+
let maker_addr = node1_addr;
49+
let taker_addr = node2_addr;
50+
let qty_from = 10;
51+
let qty_to = 50000;
52+
let maker_init_response =
53+
maker_init(maker_addr, qty_from, Some(&asset_id), qty_to, None, 3600).await;
54+
taker(taker_addr, maker_init_response.swapstring.clone()).await;
55+
56+
let swaps_maker = list_swaps(maker_addr).await;
57+
assert!(swaps_maker.taker.is_empty());
58+
assert_eq!(swaps_maker.maker.len(), 1);
59+
assert_eq!(
60+
swaps_maker.maker.first().unwrap().status,
61+
SwapStatus::Waiting
62+
);
63+
64+
let swaps_taker = list_swaps(taker_addr).await;
65+
assert!(swaps_taker.maker.is_empty());
66+
assert_eq!(swaps_taker.taker.len(), 1);
67+
assert_eq!(
68+
swaps_taker.taker.first().unwrap().status,
69+
SwapStatus::Waiting
70+
);
71+
72+
crate::ldk::FORCE_NEXT_INTERCEPTED_SWAP_RGB_FORWARD_FAILURE
73+
.store(true, std::sync::atomic::Ordering::SeqCst);
74+
75+
println!("\nexecute swap");
76+
maker_execute(
77+
maker_addr,
78+
maker_init_response.swapstring,
79+
maker_init_response.payment_secret,
80+
node2_pubkey,
81+
)
82+
.await;
83+
84+
wait_for_swap_status(
85+
maker_addr,
86+
&maker_init_response.payment_hash,
87+
SwapStatus::Failed,
88+
)
89+
.await;
90+
91+
// This assertion documents the expected behavior and currently fails because the
92+
// HTLCHandlingFailed event is ignored, leaving the taker swap stuck as Pending.
93+
wait_for_swap_status(
94+
taker_addr,
95+
&maker_init_response.payment_hash,
96+
SwapStatus::Failed,
97+
)
98+
.await;
99+
}

0 commit comments

Comments
 (0)