use util::test_utils;
use util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
use util::errors::APIError;
-use util::ser::{Writeable, ReadableArgs};
+use util::ser::{Writeable, Writer, ReadableArgs};
use util::config::UserConfig;
use util::logger::Logger;
use std::default::Default;
use std::sync::{Arc, Mutex};
use std::sync::atomic::Ordering;
-use std::mem;
+use std::{mem, io};
use rand::{thread_rng, Rng};
}
}
+struct BogusOnionHopData {
+ data: Vec<u8>
+}
+impl BogusOnionHopData {
+ fn new(orig: msgs::OnionHopData) -> Self {
+ Self { data: orig.encode() }
+ }
+}
+impl Writeable for BogusOnionHopData {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ writer.write_all(&self.data[..])
+ }
+}
+
#[test]
fn test_onion_failure() {
use ln::msgs::ChannelUpdate;
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).unwrap();
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route, cur_height).unwrap();
- onion_payloads[0].realm = 3;
- msg.onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
- }, ||{}, true, Some(PERM|1), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));//XXX incremented channels idx here
+ let mut new_payloads = Vec::new();
+ for payload in onion_payloads.drain(..) {
+ new_payloads.push(BogusOnionHopData::new(payload));
+ }
+ // break the first (non-final) hop payload by swapping the realm (0) byte for a byte
+ // describing a length-1 TLV payload, which is obviously bogus.
+ new_payloads[0].data[0] = 1;
+ msg.onion_routing_packet = onion_utils::construct_onion_packet_bogus_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
+ }, ||{}, true, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));//XXX incremented channels idx here
// final node failure
run_onion_failure_test("invalid_realm", 3, &nodes, &route, &payment_hash, |msg| {
let cur_height = nodes[0].node.latest_block_height.load(Ordering::Acquire) as u32 + 1;
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route, &session_priv).unwrap();
let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route, cur_height).unwrap();
- onion_payloads[1].realm = 3;
- msg.onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
- }, ||{}, false, Some(PERM|1), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+ let mut new_payloads = Vec::new();
+ for payload in onion_payloads.drain(..) {
+ new_payloads.push(BogusOnionHopData::new(payload));
+ }
+ // break the last-hop payload by swapping the realm (0) byte for a byte describing a
+ // length-1 TLV payload, which is obviously bogus.
+ new_payloads[1].data[0] = 1;
+ msg.onion_routing_packet = onion_utils::construct_onion_packet_bogus_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
+ }, ||{}, false, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
// the following three with run_onion_failure_test_with_fail_intercept() test only the origin node
// receiving simulated fail messages
// Connect three more block to see if bumped penalty are issued for HTLC txn
let header_132 = connect_blocks(&nodes[0].block_notifier, 3, 129, true, header_129.bitcoin_hash());
- let node_txn = {
+ let penalty_local_tx;
+ {
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(node_txn.len(), 5); // 2 bumped penalty txn on offered/received HTLC outputs of revoked commitment tx + 1 penalty tx on to_local of revoked commitment tx + 2 bumped penalty tx on revoked HTLC txn
+ assert_eq!(node_txn.len(), 3); // 2 bumped penalty txn on offered/received HTLC outputs of revoked commitment tx + 1 penalty tx on to_local of revoked commitment tx + 2 bumped penalty tx on revoked HTLC txn
check_spends!(node_txn[0], revoked_local_txn[0].clone());
check_spends!(node_txn[1], revoked_local_txn[0].clone());
- let mut penalty_local = ::std::usize::MAX;
+ check_spends!(node_txn[2], revoked_local_txn[0].clone());
+
+ penalty_local_tx = node_txn[2].clone();
+ node_txn.clear();
+ };
+ // Few more blocks to broadcast and confirm penalty_local_tx
+ let header_133 = BlockHeader { version: 0x20000000, prev_blockhash: header_132, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[0].block_notifier.block_connected(&Block { header: header_133, txdata: vec![penalty_local_tx] }, 133);
+ let header_135 = connect_blocks(&nodes[0].block_notifier, 2, 133, true, header_133.bitcoin_hash());
+ {
+ let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
+ assert_eq!(node_txn.len(), 1);
+ check_spends!(node_txn[0], revoked_local_txn[0].clone());
+ node_txn.clear();
+ }
+ let header_144 = connect_blocks(&nodes[0].block_notifier, 9, 135, true, header_135);
+ let node_txn = {
+ let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
+ assert_eq!(node_txn.len(), 2);
+
let mut penalty_offered = ::std::usize::MAX;
let mut penalty_received = ::std::usize::MAX;
{
- let iter_txn = node_txn[2..].iter();
- for (i, tx) in iter_txn.enumerate() {
- if tx.input[0].previous_output.txid == revoked_local_txn[0].txid() {
- penalty_local = 2 + i;
- } else if tx.input[0].previous_output.txid == revoked_htlc_txn[offered].txid() {
- penalty_offered = 2+ i;
+ for (i, tx) in node_txn.iter().enumerate() {
+ if tx.input[0].previous_output.txid == revoked_htlc_txn[offered].txid() {
+ penalty_offered = i;
} else if tx.input[0].previous_output.txid == revoked_htlc_txn[received].txid() {
- penalty_received = 2 + i;
+ penalty_received = i;
}
}
}
- check_spends!(node_txn[penalty_local], revoked_local_txn[0].clone());
assert_eq!(node_txn[penalty_received].input.len(), 1);
assert_eq!(node_txn[penalty_received].output.len(), 1);
let fee = revoked_htlc_txn[received].output[0].value - node_txn[penalty_received].output[0].value;
let new_feerate = fee * 1000 / node_txn[penalty_received].get_weight() as u64;
assert!(new_feerate * 100 > feerate_2 * 125);
- let txn = vec![node_txn[2].clone(), node_txn[3].clone(), node_txn[4].clone()];
+ let txn = vec![node_txn[0].clone(), node_txn[1].clone()];
node_txn.clear();
txn
};
// Broadcast claim txn and confirm blocks to avoid further bumps on this outputs
- let header_133 = BlockHeader { version: 0x20000000, prev_blockhash: header_132, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
- nodes[0].block_notifier.block_connected(&Block { header: header_133, txdata: node_txn }, 133);
- let header_140 = connect_blocks(&nodes[0].block_notifier, 6, 134, true, header_133.bitcoin_hash());
- {
- let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
- node_txn.clear();
- }
-
- // Connect few more blocks and check only penalty transaction for to_local output have been issued
- connect_blocks(&nodes[0].block_notifier, 7, 140, true, header_140);
+ let header_145 = BlockHeader { version: 0x20000000, prev_blockhash: header_144, merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
+ nodes[0].block_notifier.block_connected(&Block { header: header_145, txdata: node_txn }, 145);
+ connect_blocks(&nodes[0].block_notifier, 20, 145, true, header_145.bitcoin_hash());
{
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(node_txn.len(), 2); //TODO: should be zero when we fix check_spend_remote_htlc
+ assert_eq!(node_txn.len(), 2); //TODO: fix check_spend_remote_htlc lack of watch output
node_txn.clear();
}
check_closed_broadcast!(nodes[0], false);