Skip to content

Commit 41b4b0d

Browse files
committed
sim-node/test: add testing for successful dispatch payment
1 parent 6b768d6 commit 41b4b0d

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

sim-lib/src/sim_node.rs

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,9 @@ mod tests {
10461046
use bitcoin::secp256k1::PublicKey;
10471047
use lightning::routing::router::Route;
10481048
use mockall::mock;
1049+
use std::time::Duration;
1050+
use tokio::sync::oneshot;
1051+
use tokio::time::timeout;
10491052

10501053
/// Creates a test channel policy with its maximum HTLC size set to half of the in flight limit of the channel.
10511054
/// The minimum HTLC size is hardcoded to 2 so that we can fall beneath this value with a 1 msat htlc.
@@ -1506,4 +1509,268 @@ mod tests {
15061509
PaymentOutcome::InsufficientBalance
15071510
));
15081511
}
1512+
1513+
/// Contains elements required to test dispatch_payment functionality.
1514+
struct DispatchPaymentTestKit<'a> {
1515+
graph: SimGraph,
1516+
nodes: Vec<PublicKey>,
1517+
routing_graph: NetworkGraph<&'a WrappedLog>,
1518+
shutdown: triggered::Trigger,
1519+
}
1520+
1521+
impl<'a> DispatchPaymentTestKit<'a> {
1522+
/// Creates a test graph with a set of nodes connected by three channels, with all the capacity of the channel
1523+
/// on the side of the first node. For example, if called with capacity = 100 it will set up the following
1524+
/// network:
1525+
/// Alice (100) --- (0) Bob (100) --- (0) Carol (100) --- (0) Dave
1526+
///
1527+
/// The nodes pubkeys in this chain of channels are provided in-order for easy access.
1528+
async fn new(capacity: u64) -> Self {
1529+
let (shutdown, _listener) = triggered::trigger();
1530+
let channels = create_simulated_channels(3, capacity);
1531+
1532+
// Collect pubkeys in-order, pushing the last node on separately because they don't have an outgoing
1533+
// channel (they are not node_1 in any channel, only node_2).
1534+
let mut nodes = channels
1535+
.iter()
1536+
.map(|c| c.node_1.policy.pubkey)
1537+
.collect::<Vec<PublicKey>>();
1538+
nodes.push(channels.last().unwrap().node_2.policy.pubkey);
1539+
1540+
let kit = DispatchPaymentTestKit {
1541+
graph: SimGraph::new(channels.clone(), shutdown.clone())
1542+
.expect("could not create test graph"),
1543+
nodes,
1544+
routing_graph: populate_network_graph(channels).unwrap(),
1545+
shutdown,
1546+
};
1547+
1548+
// Assert that our channel balance is all on the side of the channel opener when we start up.
1549+
assert_eq!(
1550+
kit.channel_balances().await,
1551+
vec![(capacity, 0), (capacity, 0), (capacity, 0)]
1552+
);
1553+
1554+
kit
1555+
}
1556+
1557+
/// Returns a vector of local/remote channel balances for channels in the network.
1558+
async fn channel_balances(&self) -> Vec<(u64, u64)> {
1559+
let mut balances = vec![];
1560+
1561+
// We can't iterate through our hashmap of channels in-order, so we take advantage of our short channel id
1562+
// being the index in our chain of channels. This allows us to look up channels in-order.
1563+
let chan_count = self.graph.channels.lock().await.len();
1564+
1565+
for i in 0..chan_count {
1566+
let chan_lock = self.graph.channels.lock().await;
1567+
let channel = chan_lock.get(&ShortChannelID::from(i as u64)).unwrap();
1568+
1569+
// Take advantage of our test setup, which always makes node_1 the channel initiator to get our
1570+
// "in order" balances for the chain of channels.
1571+
balances.push((
1572+
channel.node_1.local_balance_msat,
1573+
channel.node_2.local_balance_msat,
1574+
));
1575+
}
1576+
1577+
balances
1578+
}
1579+
1580+
// Sends a test payment from source to destination and waits for the payment to complete, returning the route
1581+
// used.
1582+
async fn send_test_payemnt(
1583+
&mut self,
1584+
source: PublicKey,
1585+
dest: PublicKey,
1586+
amt: u64,
1587+
) -> Route {
1588+
let route = find_payment_route(&source, dest, amt, &self.routing_graph).unwrap();
1589+
1590+
let (sender, receiver) = oneshot::channel();
1591+
self.graph
1592+
.dispatch_payment(source, route.clone(), PaymentHash { 0: [1; 32] }, sender);
1593+
1594+
// Assert that we receive from the channel or fail.
1595+
assert!(timeout(Duration::from_millis(10), receiver).await.is_ok());
1596+
1597+
route
1598+
}
1599+
1600+
// Sets the balance on the channel to the tuple provided, used to arrange liquidity for testing.
1601+
async fn set_channel_balance(&mut self, scid: &ShortChannelID, balance: (u64, u64)) {
1602+
let mut channels_lock = self.graph.channels.lock().await;
1603+
let channel = channels_lock.get_mut(scid).unwrap();
1604+
1605+
channel.node_1.local_balance_msat = balance.0;
1606+
channel.node_2.local_balance_msat = balance.1;
1607+
1608+
assert!(channel.sanity_check().is_ok());
1609+
}
1610+
}
1611+
1612+
/// Tests dispatch of a successfully settled payment across a test network of simulated channels:
1613+
/// Alice --- Bob --- Carol --- Dave
1614+
#[tokio::test]
1615+
async fn test_successful_dispatch() {
1616+
let chan_capacity = 500_000_000;
1617+
let mut test_kit = DispatchPaymentTestKit::new(chan_capacity).await;
1618+
1619+
// Send a payment that should succeed from Alice -> Dave.
1620+
let mut amt = 20_000;
1621+
let route = test_kit
1622+
.send_test_payemnt(test_kit.nodes[0], test_kit.nodes[3], amt)
1623+
.await;
1624+
1625+
let route_total = amt + route.get_total_fees();
1626+
let hop_1_amt = amt + route.paths[0].hops[1].fee_msat;
1627+
1628+
// The sending node should have pushed the amount + total fee to the intermediary.
1629+
let alice_to_bob = (chan_capacity - route_total, route_total);
1630+
// The middle hop should include fees for the outgoing link.
1631+
let mut bob_to_carol = (chan_capacity - hop_1_amt, hop_1_amt);
1632+
// The receiving node should have the payment amount pushed to them.
1633+
let carol_to_dave = (chan_capacity - amt, amt);
1634+
1635+
let mut expected_balances = vec![alice_to_bob, bob_to_carol, carol_to_dave];
1636+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1637+
1638+
// Next, we'll test the case where a payment fails on the first hop. This is an edge case in our state
1639+
// machine, so we want to specifically hit it. To do this, we'll try to send double the amount that we just
1640+
// pushed to Dave back to Bob, expecting a failure on Dave's outgoing link due to insufficient liquidity.
1641+
let _ = test_kit
1642+
.send_test_payemnt(test_kit.nodes[3], test_kit.nodes[1], amt * 2)
1643+
.await;
1644+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1645+
1646+
// Now, test a successful single-hop payment from Bob -> Carol. We'll do this twice, so that we can drain all
1647+
// the liquidity on Bob's side (to prepare for a multi-hop failure test). Our pathfinding only allows us to
1648+
// use 50% of the channel's capacity, so we need to do two payments.
1649+
amt = bob_to_carol.0 / 2;
1650+
let _ = test_kit
1651+
.send_test_payemnt(test_kit.nodes[1], test_kit.nodes[2], amt)
1652+
.await;
1653+
1654+
bob_to_carol = (bob_to_carol.0 / 2, bob_to_carol.1 + amt);
1655+
expected_balances = vec![alice_to_bob, bob_to_carol, carol_to_dave];
1656+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1657+
1658+
// When we push this amount a second time, all the liquidity should be moved to Carol's end.
1659+
let _ = test_kit
1660+
.send_test_payemnt(test_kit.nodes[1], test_kit.nodes[2], amt)
1661+
.await;
1662+
bob_to_carol = (0, chan_capacity);
1663+
expected_balances = vec![alice_to_bob, bob_to_carol, carol_to_dave];
1664+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1665+
1666+
// Finally, we'll test a multi-hop failure by trying to send from Alice -> Dave. Since Bob's liquidity is
1667+
// drained, we expect a failure and unchanged balances along the route.
1668+
let _ = test_kit
1669+
.send_test_payemnt(test_kit.nodes[0], test_kit.nodes[3], 20_000)
1670+
.await;
1671+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1672+
1673+
test_kit.shutdown.trigger();
1674+
test_kit.graph.wait_for_shutdown().await;
1675+
}
1676+
1677+
/// Tests successful dispatch of a multi-hop payment.
1678+
#[tokio::test]
1679+
async fn test_successful_multi_hop() {
1680+
let chan_capacity = 500_000_000;
1681+
let mut test_kit = DispatchPaymentTestKit::new(chan_capacity).await;
1682+
1683+
// Send a payment that should succeed from Alice -> Dave.
1684+
let amt = 20_000;
1685+
let route = test_kit
1686+
.send_test_payemnt(test_kit.nodes[0], test_kit.nodes[3], amt)
1687+
.await;
1688+
1689+
let route_total = amt + route.get_total_fees();
1690+
let hop_1_amt = amt + route.paths[0].hops[1].fee_msat;
1691+
1692+
let expected_balances = vec![
1693+
// The sending node should have pushed the amount + total fee to the intermediary.
1694+
(chan_capacity - route_total, route_total),
1695+
// The middle hop should include fees for the outgoing link.
1696+
(chan_capacity - hop_1_amt, hop_1_amt),
1697+
// The receiving node should have the payment amount pushed to them.
1698+
(chan_capacity - amt, amt),
1699+
];
1700+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1701+
1702+
test_kit.shutdown.trigger();
1703+
test_kit.graph.wait_for_shutdown().await;
1704+
}
1705+
1706+
/// Tests success and failure for single hop payments, which are an edge case in our state machine.
1707+
#[tokio::test]
1708+
async fn test_single_hop_payments() {
1709+
let chan_capacity = 500_000_000;
1710+
let mut test_kit = DispatchPaymentTestKit::new(chan_capacity).await;
1711+
1712+
// Send a single hop payment from Alice -> Bob, it will succeed because Alice has all the liquidity.
1713+
let amt = 150_000;
1714+
let _ = test_kit
1715+
.send_test_payemnt(test_kit.nodes[0], test_kit.nodes[1], amt)
1716+
.await;
1717+
1718+
let expected_balances = vec![
1719+
(chan_capacity - amt, amt),
1720+
(chan_capacity, 0),
1721+
(chan_capacity, 0),
1722+
];
1723+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1724+
1725+
// Send a single hop payment from Dave -> Carol that will fail due to lack of liquidity, balances should be
1726+
// unchanged.
1727+
let _ = test_kit
1728+
.send_test_payemnt(test_kit.nodes[3], test_kit.nodes[2], amt)
1729+
.await;
1730+
1731+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1732+
1733+
test_kit.shutdown.trigger();
1734+
test_kit.graph.wait_for_shutdown().await;
1735+
}
1736+
1737+
/// Tests failing back of multi-hop payments at various failure indexes.
1738+
#[tokio::test]
1739+
async fn test_multi_hop_faiulre() {
1740+
let chan_capacity = 500_000_000;
1741+
let mut test_kit = DispatchPaymentTestKit::new(chan_capacity).await;
1742+
1743+
// Drain liquidity between Bob and Carol to force failures on Bob's outgoing linke.
1744+
test_kit
1745+
.set_channel_balance(&ShortChannelID::from(1), (0, chan_capacity))
1746+
.await;
1747+
1748+
let mut expected_balances =
1749+
vec![(chan_capacity, 0), (0, chan_capacity), (chan_capacity, 0)];
1750+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1751+
1752+
// Send a payment from Alice -> Dave which we expect to fail leaving balances unaffected.
1753+
let amt = 150_000;
1754+
let _ = test_kit
1755+
.send_test_payemnt(test_kit.nodes[0], test_kit.nodes[3], amt)
1756+
.await;
1757+
1758+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1759+
1760+
// Push liquidity to Dave so that we can send a payment which will fail on Bob's outgoing link, leaving
1761+
// balances unaffected.
1762+
expected_balances[2] = (0, chan_capacity);
1763+
test_kit
1764+
.set_channel_balance(&ShortChannelID::from(2), (0, chan_capacity))
1765+
.await;
1766+
1767+
let _ = test_kit
1768+
.send_test_payemnt(test_kit.nodes[3], test_kit.nodes[0], amt)
1769+
.await;
1770+
1771+
assert_eq!(test_kit.channel_balances().await, expected_balances);
1772+
1773+
test_kit.shutdown.trigger();
1774+
test_kit.graph.wait_for_shutdown().await;
1775+
}
15091776
}

0 commit comments

Comments
 (0)