+
+ let events_2 = node_a.get_and_clear_pending_events();
+ assert_eq!(events_2.len(), 1);
+ let as_update = match events_2[0] {
+ Event::BroadcastChannelUpdate { ref msg } => {
+ msg.clone()
+ },
+ _ => panic!("Unexpected event"),
+ };
+
+ let events_3 = node_b.get_and_clear_pending_events();
+ assert_eq!(events_3.len(), 1);
+ let bs_update = match events_3[0] {
+ Event::BroadcastChannelUpdate { ref msg } => {
+ msg.clone()
+ },
+ _ => panic!("Unexpected event"),
+ };
+
+ (as_update, bs_update)
+ }
+
+ struct SendEvent {
+ node_id: PublicKey,
+ msgs: Vec<msgs::UpdateAddHTLC>,
+ commitment_msg: msgs::CommitmentSigned,
+ }
+ impl SendEvent {
+ fn from_event(event: Event) -> SendEvent {
+ match event {
+ Event::UpdateHTLCs { node_id, updates: msgs::CommitmentUpdate { update_add_htlcs, update_fulfill_htlcs, update_fail_htlcs, update_fail_malformed_htlcs, commitment_signed } } => {
+ assert!(update_fulfill_htlcs.is_empty());
+ assert!(update_fail_htlcs.is_empty());
+ assert!(update_fail_malformed_htlcs.is_empty());
+ SendEvent { node_id: node_id, msgs: update_add_htlcs, commitment_msg: commitment_signed }
+ },
+ _ => panic!("Unexpected event type!"),
+ }
+ }
+ }
+
+ macro_rules! check_added_monitors {
+ ($node: expr, $count: expr) => {
+ {
+ let mut added_monitors = $node.chan_monitor.added_monitors.lock().unwrap();
+ assert_eq!(added_monitors.len(), $count);
+ added_monitors.clear();
+ }
+ }
+ }
+
+ macro_rules! commitment_signed_dance {
+ ($node_a: expr, $node_b: expr, $commitment_signed: expr, $fail_backwards: expr) => {
+ {
+ check_added_monitors!($node_a, 0);
+ let (as_revoke_and_ack, as_commitment_signed) = $node_a.node.handle_commitment_signed(&$node_b.node.get_our_node_id(), &$commitment_signed).unwrap();
+ check_added_monitors!($node_a, 1);
+ check_added_monitors!($node_b, 0);
+ assert!($node_b.node.handle_revoke_and_ack(&$node_a.node.get_our_node_id(), &as_revoke_and_ack).unwrap().is_none());
+ check_added_monitors!($node_b, 1);
+ let (bs_revoke_and_ack, bs_none) = $node_b.node.handle_commitment_signed(&$node_a.node.get_our_node_id(), &as_commitment_signed.unwrap()).unwrap();
+ assert!(bs_none.is_none());
+ check_added_monitors!($node_b, 1);
+ if $fail_backwards {
+ assert!($node_a.node.get_and_clear_pending_events().is_empty());
+ }
+ assert!($node_a.node.handle_revoke_and_ack(&$node_b.node.get_our_node_id(), &bs_revoke_and_ack).unwrap().is_none());
+ {
+ let mut added_monitors = $node_a.chan_monitor.added_monitors.lock().unwrap();
+ if $fail_backwards {
+ assert_eq!(added_monitors.len(), 2);
+ assert!(added_monitors[0].0 != added_monitors[1].0);
+ } else {
+ assert_eq!(added_monitors.len(), 1);
+ }
+ added_monitors.clear();
+ }
+ }
+ }
+ }
+
+ macro_rules! get_payment_preimage_hash {
+ ($node: expr) => {
+ {
+ let payment_preimage = [*$node.network_payment_count.borrow(); 32];
+ *$node.network_payment_count.borrow_mut() += 1;
+ let mut payment_hash = [0; 32];
+ let mut sha = Sha256::new();
+ sha.input(&payment_preimage[..]);
+ sha.result(&mut payment_hash);
+ (payment_preimage, payment_hash)
+ }
+ }
+ }
+
+ fn send_along_route(origin_node: &Node, route: Route, expected_route: &[&Node], recv_value: u64) -> ([u8; 32], [u8; 32]) {
+ let (our_payment_preimage, our_payment_hash) = get_payment_preimage_hash!(origin_node);
+
+ let mut payment_event = {
+ origin_node.node.send_payment(route, our_payment_hash).unwrap();
+ check_added_monitors!(origin_node, 1);
+
+ let mut events = origin_node.node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ SendEvent::from_event(events.remove(0))
+ };
+ let mut prev_node = origin_node;
+
+ for (idx, &node) in expected_route.iter().enumerate() {
+ assert_eq!(node.node.get_our_node_id(), payment_event.node_id);
+
+ node.node.handle_update_add_htlc(&prev_node.node.get_our_node_id(), &payment_event.msgs[0]).unwrap();
+ check_added_monitors!(node, 0);
+ commitment_signed_dance!(node, prev_node, payment_event.commitment_msg, false);
+
+ let events_1 = node.node.get_and_clear_pending_events();
+ assert_eq!(events_1.len(), 1);
+ match events_1[0] {
+ Event::PendingHTLCsForwardable { .. } => { },
+ _ => panic!("Unexpected event"),
+ };
+
+ node.node.channel_state.lock().unwrap().next_forward = Instant::now();
+ node.node.process_pending_htlc_forwards();
+
+ let mut events_2 = node.node.get_and_clear_pending_events();
+ assert_eq!(events_2.len(), 1);
+ if idx == expected_route.len() - 1 {
+ match events_2[0] {
+ Event::PaymentReceived { ref payment_hash, amt } => {
+ assert_eq!(our_payment_hash, *payment_hash);
+ assert_eq!(amt, recv_value);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ } else {
+ check_added_monitors!(node, 1);
+ payment_event = SendEvent::from_event(events_2.remove(0));
+ assert_eq!(payment_event.msgs.len(), 1);
+ }
+
+ prev_node = node;
+ }
+
+ (our_payment_preimage, our_payment_hash)
+ }
+
+ fn claim_payment_along_route(origin_node: &Node, expected_route: &[&Node], skip_last: bool, our_payment_preimage: [u8; 32]) {
+ assert!(expected_route.last().unwrap().node.claim_funds(our_payment_preimage));
+ check_added_monitors!(expected_route.last().unwrap(), 1);
+
+ let mut next_msgs: Option<(msgs::UpdateFulfillHTLC, msgs::CommitmentSigned)> = None;
+ macro_rules! update_fulfill_dance {
+ ($node: expr, $prev_node: expr, $last_node: expr) => {
+ {
+ $node.node.handle_update_fulfill_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0).unwrap();
+ if $last_node {
+ check_added_monitors!($node, 0);
+ } else {
+ check_added_monitors!($node, 1);
+ }
+ commitment_signed_dance!($node, $prev_node, next_msgs.as_ref().unwrap().1, false);
+ }
+ }
+ }
+
+ let mut expected_next_node = expected_route.last().unwrap().node.get_our_node_id();
+ let mut prev_node = expected_route.last().unwrap();
+ for (idx, node) in expected_route.iter().rev().enumerate() {
+ assert_eq!(expected_next_node, node.node.get_our_node_id());
+ if next_msgs.is_some() {
+ update_fulfill_dance!(node, prev_node, false);
+ }
+
+ let events = node.node.get_and_clear_pending_events();
+ if !skip_last || idx != expected_route.len() - 1 {
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref commitment_signed } } => {
+ assert!(update_add_htlcs.is_empty());
+ assert_eq!(update_fulfill_htlcs.len(), 1);
+ assert!(update_fail_htlcs.is_empty());
+ assert!(update_fail_malformed_htlcs.is_empty());
+ expected_next_node = node_id.clone();
+ next_msgs = Some((update_fulfill_htlcs[0].clone(), commitment_signed.clone()));
+ },
+ _ => panic!("Unexpected event"),
+ }
+ } else {
+ assert!(events.is_empty());
+ }
+ if !skip_last && idx == expected_route.len() - 1 {
+ assert_eq!(expected_next_node, origin_node.node.get_our_node_id());
+ }
+
+ prev_node = node;
+ }
+
+ if !skip_last {
+ update_fulfill_dance!(origin_node, expected_route.first().unwrap(), true);
+ let events = origin_node.node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::PaymentSent { payment_preimage } => {
+ assert_eq!(payment_preimage, our_payment_preimage);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+ }
+
+ fn claim_payment(origin_node: &Node, expected_route: &[&Node], our_payment_preimage: [u8; 32]) {
+ claim_payment_along_route(origin_node, expected_route, false, our_payment_preimage);
+ }
+
+ const TEST_FINAL_CLTV: u32 = 32;
+
+ fn route_payment(origin_node: &Node, expected_route: &[&Node], recv_value: u64) -> ([u8; 32], [u8; 32]) {
+ let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
+ assert_eq!(route.hops.len(), expected_route.len());
+ for (node, hop) in expected_route.iter().zip(route.hops.iter()) {
+ assert_eq!(hop.pubkey, node.node.get_our_node_id());
+ }
+
+ send_along_route(origin_node, route, expected_route, recv_value)
+ }
+
+ fn route_over_limit(origin_node: &Node, expected_route: &[&Node], recv_value: u64) {
+ let route = origin_node.router.get_route(&expected_route.last().unwrap().node.get_our_node_id(), None, &Vec::new(), recv_value, TEST_FINAL_CLTV).unwrap();
+ assert_eq!(route.hops.len(), expected_route.len());
+ for (node, hop) in expected_route.iter().zip(route.hops.iter()) {
+ assert_eq!(hop.pubkey, node.node.get_our_node_id());
+ }
+
+ let (_, our_payment_hash) = get_payment_preimage_hash!(origin_node);
+
+ let err = origin_node.node.send_payment(route, our_payment_hash).err().unwrap();
+ match err {
+ APIError::RouteError{err} => assert_eq!(err, "Cannot send value that would put us over our max HTLC value in flight"),
+ _ => panic!("Unknown error variants"),
+ };
+ }
+
+ fn send_payment(origin: &Node, expected_route: &[&Node], recv_value: u64) {
+ let our_payment_preimage = route_payment(&origin, expected_route, recv_value).0;
+ claim_payment(&origin, expected_route, our_payment_preimage);
+ }
+
+ fn fail_payment_along_route(origin_node: &Node, expected_route: &[&Node], skip_last: bool, our_payment_hash: [u8; 32]) {
+ assert!(expected_route.last().unwrap().node.fail_htlc_backwards(&our_payment_hash));
+ check_added_monitors!(expected_route.last().unwrap(), 1);
+
+ let mut next_msgs: Option<(msgs::UpdateFailHTLC, msgs::CommitmentSigned)> = None;
+ macro_rules! update_fail_dance {
+ ($node: expr, $prev_node: expr, $last_node: expr) => {
+ {
+ $node.node.handle_update_fail_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0).unwrap();
+ commitment_signed_dance!($node, $prev_node, next_msgs.as_ref().unwrap().1, !$last_node);
+ }
+ }
+ }
+
+ let mut expected_next_node = expected_route.last().unwrap().node.get_our_node_id();
+ let mut prev_node = expected_route.last().unwrap();
+ for (idx, node) in expected_route.iter().rev().enumerate() {
+ assert_eq!(expected_next_node, node.node.get_our_node_id());
+ if next_msgs.is_some() {
+ // We may be the "last node" for the purpose of the commitment dance if we're
+ // skipping the last node (implying it is disconnected) and we're the
+ // second-to-last node!
+ update_fail_dance!(node, prev_node, skip_last && idx == expected_route.len() - 1);
+ }
+
+ let events = node.node.get_and_clear_pending_events();
+ if !skip_last || idx != expected_route.len() - 1 {
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, ref commitment_signed } } => {
+ assert!(update_add_htlcs.is_empty());
+ assert!(update_fulfill_htlcs.is_empty());
+ assert_eq!(update_fail_htlcs.len(), 1);
+ assert!(update_fail_malformed_htlcs.is_empty());
+ expected_next_node = node_id.clone();
+ next_msgs = Some((update_fail_htlcs[0].clone(), commitment_signed.clone()));
+ },
+ _ => panic!("Unexpected event"),
+ }
+ } else {
+ assert!(events.is_empty());
+ }
+ if !skip_last && idx == expected_route.len() - 1 {
+ assert_eq!(expected_next_node, origin_node.node.get_our_node_id());
+ }
+
+ prev_node = node;
+ }
+
+ if !skip_last {
+ update_fail_dance!(origin_node, expected_route.first().unwrap(), true);
+
+ let events = origin_node.node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ Event::PaymentFailed { payment_hash } => {
+ assert_eq!(payment_hash, our_payment_hash);
+ },
+ _ => panic!("Unexpected event"),
+ }
+ }
+ }
+
+ fn fail_payment(origin_node: &Node, expected_route: &[&Node], our_payment_hash: [u8; 32]) {
+ fail_payment_along_route(origin_node, expected_route, false, our_payment_hash);
+ }
+
+ fn create_network(node_count: usize) -> Vec<Node> {
+ let mut nodes = Vec::new();
+ let mut rng = thread_rng();
+ let secp_ctx = Secp256k1::new();
+ let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
+
+ let chan_count = Rc::new(RefCell::new(0));
+ let payment_count = Rc::new(RefCell::new(0));
+
+ for _ in 0..node_count {
+ let feeest = Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 });
+ let chain_monitor = Arc::new(chaininterface::ChainWatchInterfaceUtil::new(Network::Testnet, Arc::clone(&logger)));
+ let tx_broadcaster = Arc::new(test_utils::TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())});
+ let chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(chain_monitor.clone(), tx_broadcaster.clone()));
+ let node_id = {
+ let mut key_slice = [0; 32];
+ rng.fill_bytes(&mut key_slice);
+ SecretKey::from_slice(&secp_ctx, &key_slice).unwrap()
+ };
+ let node = ChannelManager::new(node_id.clone(), 0, true, Network::Testnet, feeest.clone(), chan_monitor.clone(), chain_monitor.clone(), tx_broadcaster.clone(), Arc::clone(&logger)).unwrap();
+ let router = Router::new(PublicKey::from_secret_key(&secp_ctx, &node_id), chain_monitor.clone(), Arc::clone(&logger));
+ nodes.push(Node { chain_monitor, tx_broadcaster, chan_monitor, node, router,
+ network_payment_count: payment_count.clone(),
+ network_chan_count: chan_count.clone(),
+ });
+ }
+
+ nodes
+ }
+
+ #[test]
+ fn fake_network_test() {
+ // Simple test which builds a network of ChannelManagers, connects them to each other, and
+ // tests that payments get routed and transactions broadcast in semi-reasonable ways.
+ let nodes = create_network(4);
+
+ // Create some initial channels
+ let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
+ let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
+ let chan_3 = create_announced_chan_between_nodes(&nodes, 2, 3);
+
+ // Rebalance the network a bit by relaying one payment through all the channels...
+ send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], 8000000);
+ send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], 8000000);
+ send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], 8000000);
+ send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], 8000000);
+
+ // Send some more payments
+ send_payment(&nodes[1], &vec!(&nodes[2], &nodes[3])[..], 1000000);
+ send_payment(&nodes[3], &vec!(&nodes[2], &nodes[1], &nodes[0])[..], 1000000);
+ send_payment(&nodes[3], &vec!(&nodes[2], &nodes[1])[..], 1000000);
+
+ // Test failure packets
+ let payment_hash_1 = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], 1000000).1;
+ fail_payment(&nodes[0], &vec!(&nodes[1], &nodes[2], &nodes[3])[..], payment_hash_1);
+
+ // Add a new channel that skips 3
+ let chan_4 = create_announced_chan_between_nodes(&nodes, 1, 3);
+
+ send_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], 1000000);
+ send_payment(&nodes[2], &vec!(&nodes[3])[..], 1000000);
+ send_payment(&nodes[1], &vec!(&nodes[3])[..], 8000000);
+ send_payment(&nodes[1], &vec!(&nodes[3])[..], 8000000);
+ send_payment(&nodes[1], &vec!(&nodes[3])[..], 8000000);
+ send_payment(&nodes[1], &vec!(&nodes[3])[..], 8000000);
+ send_payment(&nodes[1], &vec!(&nodes[3])[..], 8000000);
+
+ // Do some rebalance loop payments, simultaneously
+ let mut hops = Vec::with_capacity(3);
+ hops.push(RouteHop {
+ pubkey: nodes[2].node.get_our_node_id(),
+ short_channel_id: chan_2.0.contents.short_channel_id,
+ fee_msat: 0,
+ cltv_expiry_delta: chan_3.0.contents.cltv_expiry_delta as u32
+ });
+ hops.push(RouteHop {
+ pubkey: nodes[3].node.get_our_node_id(),
+ short_channel_id: chan_3.0.contents.short_channel_id,
+ fee_msat: 0,
+ cltv_expiry_delta: chan_4.1.contents.cltv_expiry_delta as u32
+ });
+ hops.push(RouteHop {
+ pubkey: nodes[1].node.get_our_node_id(),
+ short_channel_id: chan_4.0.contents.short_channel_id,
+ fee_msat: 1000000,
+ cltv_expiry_delta: TEST_FINAL_CLTV,
+ });
+ hops[1].fee_msat = chan_4.1.contents.fee_base_msat as u64 + chan_4.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
+ hops[0].fee_msat = chan_3.0.contents.fee_base_msat as u64 + chan_3.0.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
+ let payment_preimage_1 = send_along_route(&nodes[1], Route { hops }, &vec!(&nodes[2], &nodes[3], &nodes[1])[..], 1000000).0;
+
+ let mut hops = Vec::with_capacity(3);
+ hops.push(RouteHop {
+ pubkey: nodes[3].node.get_our_node_id(),
+ short_channel_id: chan_4.0.contents.short_channel_id,
+ fee_msat: 0,
+ cltv_expiry_delta: chan_3.1.contents.cltv_expiry_delta as u32
+ });
+ hops.push(RouteHop {
+ pubkey: nodes[2].node.get_our_node_id(),
+ short_channel_id: chan_3.0.contents.short_channel_id,
+ fee_msat: 0,
+ cltv_expiry_delta: chan_2.1.contents.cltv_expiry_delta as u32
+ });
+ hops.push(RouteHop {
+ pubkey: nodes[1].node.get_our_node_id(),
+ short_channel_id: chan_2.0.contents.short_channel_id,
+ fee_msat: 1000000,
+ cltv_expiry_delta: TEST_FINAL_CLTV,
+ });
+ hops[1].fee_msat = chan_2.1.contents.fee_base_msat as u64 + chan_2.1.contents.fee_proportional_millionths as u64 * hops[2].fee_msat as u64 / 1000000;
+ hops[0].fee_msat = chan_3.1.contents.fee_base_msat as u64 + chan_3.1.contents.fee_proportional_millionths as u64 * hops[1].fee_msat as u64 / 1000000;
+ let payment_hash_2 = send_along_route(&nodes[1], Route { hops }, &vec!(&nodes[3], &nodes[2], &nodes[1])[..], 1000000).1;
+
+ // Claim the rebalances...
+ fail_payment(&nodes[1], &vec!(&nodes[3], &nodes[2], &nodes[1])[..], payment_hash_2);
+ claim_payment(&nodes[1], &vec!(&nodes[2], &nodes[3], &nodes[1])[..], payment_preimage_1);
+
+ // Add a duplicate new channel from 2 to 4
+ let chan_5 = create_announced_chan_between_nodes(&nodes, 1, 3);
+
+ // Send some payments across both channels
+ let payment_preimage_3 = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], 3000000).0;
+ let payment_preimage_4 = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], 3000000).0;
+ let payment_preimage_5 = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], 3000000).0;
+
+ route_over_limit(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], 3000000);
+
+ //TODO: Test that routes work again here as we've been notified that the channel is full
+
+ claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], payment_preimage_3);
+ claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], payment_preimage_4);
+ claim_payment(&nodes[0], &vec!(&nodes[1], &nodes[3])[..], payment_preimage_5);
+
+ // Close down the channels...
+ close_channel(&nodes[0], &nodes[1], &chan_1.2, chan_1.3, true);
+ close_channel(&nodes[1], &nodes[2], &chan_2.2, chan_2.3, false);
+ close_channel(&nodes[2], &nodes[3], &chan_3.2, chan_3.3, true);
+ close_channel(&nodes[1], &nodes[3], &chan_4.2, chan_4.3, false);
+ close_channel(&nodes[1], &nodes[3], &chan_5.2, chan_5.3, false);
+ }
+
+ #[test]
+ fn duplicate_htlc_test() {
+ // Test that we accept duplicate payment_hash HTLCs across the network and that
+ // claiming/failing them are all separate and don't effect each other
+ let mut nodes = create_network(6);
+
+ // Create some initial channels to route via 3 to 4/5 from 0/1/2
+ create_announced_chan_between_nodes(&nodes, 0, 3);
+ create_announced_chan_between_nodes(&nodes, 1, 3);
+ create_announced_chan_between_nodes(&nodes, 2, 3);
+ create_announced_chan_between_nodes(&nodes, 3, 4);
+ create_announced_chan_between_nodes(&nodes, 3, 5);
+
+ let (payment_preimage, payment_hash) = route_payment(&nodes[0], &vec!(&nodes[3], &nodes[4])[..], 1000000);
+
+ *nodes[0].network_payment_count.borrow_mut() -= 1;
+ assert_eq!(route_payment(&nodes[1], &vec!(&nodes[3])[..], 1000000).0, payment_preimage);
+
+ *nodes[0].network_payment_count.borrow_mut() -= 1;
+ assert_eq!(route_payment(&nodes[2], &vec!(&nodes[3], &nodes[5])[..], 1000000).0, payment_preimage);
+
+ claim_payment(&nodes[0], &vec!(&nodes[3], &nodes[4])[..], payment_preimage);
+ fail_payment(&nodes[2], &vec!(&nodes[3], &nodes[5])[..], payment_hash);
+ claim_payment(&nodes[1], &vec!(&nodes[3])[..], payment_preimage);
+ }
+
+ #[derive(PartialEq)]
+ enum HTLCType { NONE, TIMEOUT, SUCCESS }
+ /// Tests that the given node has broadcast transactions for the given Channel
+ ///
+ /// First checks that the latest local commitment tx has been broadcast, unless an explicit
+ /// commitment_tx is provided, which may be used to test that a remote commitment tx was
+ /// broadcast and the revoked outputs were claimed.
+ ///
+ /// Next tests that there is (or is not) a transaction that spends the commitment transaction
+ /// that appears to be the type of HTLC transaction specified in has_htlc_tx.
+ ///
+ /// All broadcast transactions must be accounted for in one of the above three types of we'll
+ /// also fail.
+ fn test_txn_broadcast(node: &Node, chan: &(msgs::ChannelUpdate, msgs::ChannelUpdate, [u8; 32], Transaction), commitment_tx: Option<Transaction>, has_htlc_tx: HTLCType) -> Vec<Transaction> {
+ let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
+ assert!(node_txn.len() >= if commitment_tx.is_some() { 0 } else { 1 } + if has_htlc_tx == HTLCType::NONE { 0 } else { 1 });
+
+ let mut res = Vec::with_capacity(2);
+ node_txn.retain(|tx| {
+ if tx.input.len() == 1 && tx.input[0].previous_output.txid == chan.3.txid() {
+ let mut funding_tx_map = HashMap::new();
+ funding_tx_map.insert(chan.3.txid(), chan.3.clone());
+ tx.verify(&funding_tx_map).unwrap();
+ if commitment_tx.is_none() {
+ res.push(tx.clone());
+ }
+ false
+ } else { true }
+ });
+ if let Some(explicit_tx) = commitment_tx {
+ res.push(explicit_tx.clone());
+ }
+
+ assert_eq!(res.len(), 1);
+
+ if has_htlc_tx != HTLCType::NONE {
+ node_txn.retain(|tx| {
+ if tx.input.len() == 1 && tx.input[0].previous_output.txid == res[0].txid() {
+ let mut funding_tx_map = HashMap::new();
+ funding_tx_map.insert(res[0].txid(), res[0].clone());
+ tx.verify(&funding_tx_map).unwrap();
+ if has_htlc_tx == HTLCType::TIMEOUT {
+ assert!(tx.lock_time != 0);
+ } else {
+ assert!(tx.lock_time == 0);
+ }
+ res.push(tx.clone());
+ false
+ } else { true }
+ });
+ assert_eq!(res.len(), 2);
+ }
+
+ assert!(node_txn.is_empty());
+ res