// always return a HighPriority feerate here which is >= the maximum Normal feerate and a
// Background feerate which is <= the minimum Normal feerate.
match conf_target {
- ConfirmationTarget::HighPriority => MAX_FEE,
- ConfirmationTarget::Background|ConfirmationTarget::MempoolMinimum => 253,
- ConfirmationTarget::Normal => cmp::min(self.ret_val.load(atomic::Ordering::Acquire), MAX_FEE),
+ ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee => MAX_FEE * 10,
+ ConfirmationTarget::OnChainSweep => MAX_FEE,
+ ConfirmationTarget::ChannelCloseMinimum|ConfirmationTarget::AnchorChannelFee|ConfirmationTarget::MinAllowedAnchorChannelRemoteFee|ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee => 253,
+ ConfirmationTarget::NonAnchorChannelFee => cmp::min(self.ret_val.load(atomic::Ordering::Acquire), MAX_FEE),
}
}
}
}
#[inline]
-pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
+pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out, anchors: bool) {
let out = SearchingOutput::new(underlying_out);
let broadcast = Arc::new(TestBroadcaster{});
let router = FuzzRouter {};
let mut config = UserConfig::default();
config.channel_config.forwarding_fee_proportional_millionths = 0;
config.channel_handshake_config.announced_channel = true;
+ if anchors {
+ config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+ config.manually_accept_inbound_channels = true;
+ }
let network = Network::Bitcoin;
let best_block_timestamp = genesis_block(network).header.time;
let params = ChainParameters {
let mut config = UserConfig::default();
config.channel_config.forwarding_fee_proportional_millionths = 0;
config.channel_handshake_config.announced_channel = true;
+ if anchors {
+ config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+ config.manually_accept_inbound_channels = true;
+ }
let mut monitors = HashMap::new();
let mut old_monitors = $old_monitors.latest_monitors.lock().unwrap();
let mut channel_txn = Vec::new();
macro_rules! make_channel {
- ($source: expr, $dest: expr, $chan_id: expr) => { {
+ ($source: expr, $dest: expr, $dest_keys_manager: expr, $chan_id: expr) => { {
$source.peer_connected(&$dest.get_our_node_id(), &Init {
features: $dest.init_features(), networks: None, remote_network_address: None
}, true).unwrap();
$dest.handle_open_channel(&$source.get_our_node_id(), &open_channel);
let accept_channel = {
+ if anchors {
+ let events = $dest.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ if let events::Event::OpenChannelRequest {
+ ref temporary_channel_id, ref counterparty_node_id, ..
+ } = events[0] {
+ let mut random_bytes = [0u8; 16];
+ random_bytes.copy_from_slice(&$dest_keys_manager.get_secure_random_bytes()[..16]);
+ let user_channel_id = u128::from_be_bytes(random_bytes);
+ $dest.accept_inbound_channel(
+ temporary_channel_id,
+ counterparty_node_id,
+ user_channel_id,
+ ).unwrap();
+ } else { panic!("Wrong event type"); }
+ }
let events = $dest.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
if let events::MessageSendEvent::SendAcceptChannel { ref msg, .. } = events[0] {
let mut nodes = [node_a, node_b, node_c];
- let chan_1_funding = make_channel!(nodes[0], nodes[1], 0);
- let chan_2_funding = make_channel!(nodes[1], nodes[2], 1);
+ let chan_1_funding = make_channel!(nodes[0], nodes[1], keys_manager_b, 0);
+ let chan_2_funding = make_channel!(nodes[1], nodes[2], keys_manager_c, 1);
for node in nodes.iter() {
confirm_txn!(node);
0x6d => { send_hop_payment(&nodes[2], &nodes[1], chan_b, &nodes[0], chan_a, 1, &mut payment_id, &mut payment_idx); },
0x80 => {
- let max_feerate = last_htlc_clear_fee_a * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
+ let mut max_feerate = last_htlc_clear_fee_a;
+ if !anchors {
+ max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
+ }
if fee_est_a.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
fee_est_a.ret_val.store(max_feerate, atomic::Ordering::Release);
}
0x81 => { fee_est_a.ret_val.store(253, atomic::Ordering::Release); nodes[0].maybe_update_chan_fees(); },
0x84 => {
- let max_feerate = last_htlc_clear_fee_b * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
+ let mut max_feerate = last_htlc_clear_fee_b;
+ if !anchors {
+ max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
+ }
if fee_est_b.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
fee_est_b.ret_val.store(max_feerate, atomic::Ordering::Release);
}
0x85 => { fee_est_b.ret_val.store(253, atomic::Ordering::Release); nodes[1].maybe_update_chan_fees(); },
0x88 => {
- let max_feerate = last_htlc_clear_fee_c * FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
+ let mut max_feerate = last_htlc_clear_fee_c;
+ if !anchors {
+ max_feerate *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE as u32;
+ }
if fee_est_c.ret_val.fetch_add(250, atomic::Ordering::AcqRel) + 250 > max_feerate {
fee_est_c.ret_val.store(max_feerate, atomic::Ordering::Release);
}
}
pub fn chanmon_consistency_test<Out: Output>(data: &[u8], out: Out) {
- do_test(data, out);
+ do_test(data, out.clone(), false);
+ do_test(data, out, true);
}
#[no_mangle]
pub extern "C" fn chanmon_consistency_run(data: *const u8, datalen: usize) {
- do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull{});
+ do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull{}, false);
+ do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull{}, true);
}
// 030012 - inbound read from peer id 0 of len 18
// 0147 03000000000000000000000000000000 - message header indicating message length 327
// 0300fe - inbound read from peer id 0 of len 254
- // 0020 7500000000000000000000000000000000000000000000000000000000000000 ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679 000000000000c350 0000000000000000 0000000000000162 ffffffffffffffff 0000000000000222 0000000000000000 000000fd 0006 01e3 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 030000000000000000000000000000000000000000000000000000000000000004 - beginning of open_channel message
+ // 0020 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000 ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679 000000000000c350 0000000000000000 0000000000000162 ffffffffffffffff 0000000000000222 0000000000000000 000000fd 0006 01e3 030000000000000000000000000000000000000000000000000000000000000001 030000000000000000000000000000000000000000000000000000000000000002 030000000000000000000000000000000000000000000000000000000000000003 030000000000000000000000000000000000000000000000000000000000000004 - beginning of open_channel message
// 030059 - inbound read from peer id 0 of len 89
// 030000000000000000000000000000000000000000000000000000000000000005 020900000000000000000000000000000000000000000000000000000000000000 01 0000 01021000 03000000000000000000000000000000 - rest of open_channel and mac
//
// - client now fails the HTLC backwards as it was unable to extract the payment preimage (CHECK 9 duplicate and CHECK 10)
let logger = Arc::new(TrackingLogger { lines: Mutex::new(HashMap::new()) });
- super::do_test(&::hex::decode("01000000000000000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000000020300320003000000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000030012001003000000000000000000000000000000030020001000021aaa0008aaaaaaaaaaaa9aaa030000000000000000000000000000000300120147030000000000000000000000000000000300fe00207500000000000000000000000000000000000000000000000000000000000000ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679000000000000c35000000000000000000000000000000162ffffffffffffffff00000000000002220000000000000000000000fd000601e3030000000000000000000000000000000000000000000000000000000000000001030000000000000000000000000000000000000000000000000000000000000002030000000000000000000000000000000000000000000000000000000000000003030000000000000000000000000000000000000000000000000000000000000004030059030000000000000000000000000000000000000000000000000000000000000005020900000000000000000000000000000000000000000000000000000000000000010000010210000300000000000000000000000000000000fd00fd0300120084030000000000000000000000000000000300940022ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb1819096793d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000210100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000c005e020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0150c3000000000000220020ae00000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c000003001200430300000000000000000000000000000003005300243d0000000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000010301320003000000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000030142000302000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003000000000000000000000000000000030112001001000000000000000000000000000000030120001000021aaa0008aaaaaaaaaaaa9aaa01000000000000000000000000000000050103020000000000000000000000000000000000000000000000000000000000000000c3500003e800fd0301120112010000000000000000000000000000000301ff00210000000000000000000000000000000000000000000000000000000000000e05000000000000016200000000004c4b4000000000000003e800000000000003e80000000203f000050300000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000002000300000000000000000000000000000000000000000000000000000000000003000300000000000000000000000000000000000000000000000000000000000004000300000000000000000000000000000000000000000000000000000000000005000266000000000000000000000000000003012300000000000000000000000000000000000000010000000000000000000000000000000a00fd00fd03011200620100000000000000000000000000000003017200233a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c0001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000b03011200430100000000000000000000000000000003015300243a000000000000000000000000000000000000000000000000000000000000000267000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000020b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000700fd00fd03011200640100000000000000000000000000000003017400843a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000002640000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112004a0100000000000000000000000000000003015a00823a000000000000000000000000000000000000000000000000000000000000000000000000000000ff008888888888888888888888888888888888888888888888888888888888880100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a0000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000265000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000010000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000020d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000700fd00fd03011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112002c0100000000000000000000000000000003013c00833a00000000000000000000000000000000000000000000000000000000000000000000000000000100000100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000703001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000b0838ff00000000000000000000000000000000000000000000000000000000000000000003f0000300000000000000000000000000000000000000000000000000000000000005551202030927c00401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff53000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200a4030000000000000000000000000000000300b400843d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007501000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006705000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000000000000020f00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000700fd00fd0c007d02000000013a000000000000000000000000000000000000000000000000000000000000000000000000000000800258020000000000002200204b0000000000000000000000000000000000000000000000000000000000000014c00000000000001600142800000000000000000000000000000000000000050000200c005e0200000001730000000000000000000000000000000000000000000000000000000000000000000000000000000001a701000000000000220020b200000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c000007").unwrap(), &(Arc::clone(&logger) as Arc<dyn Logger>));
+ super::do_test(&::hex::decode("01000000000000000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000000020300320003000000000000000000000000000000000000000000000000000000000000000203000000000000000000000000000000030012001003000000000000000000000000000000030020001000021aaa0008aaaaaaaaaaaa9aaa030000000000000000000000000000000300120147030000000000000000000000000000000300fe00206fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679000000000000c35000000000000000000000000000000162ffffffffffffffff00000000000002220000000000000000000000fd000601e3030000000000000000000000000000000000000000000000000000000000000001030000000000000000000000000000000000000000000000000000000000000002030000000000000000000000000000000000000000000000000000000000000003030000000000000000000000000000000000000000000000000000000000000004030059030000000000000000000000000000000000000000000000000000000000000005020900000000000000000000000000000000000000000000000000000000000000010000010210000300000000000000000000000000000000fd00fd0300120084030000000000000000000000000000000300940022ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb1819096793d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000210100000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000c005e020000000100000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0150c3000000000000220020ae00000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c00000c000003001200430300000000000000000000000000000003005300243d0000000000000000000000000000000000000000000000000000000000000002080000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000010301320003000000000000000000000000000000000000000000000000000000000000000703000000000000000000000000000000030142000302000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003000000000000000000000000000000030112001001000000000000000000000000000000030120001000021aaa0008aaaaaaaaaaaa9aaa01000000000000000000000000000000050103020000000000000000000000000000000000000000000000000000000000000000c3500003e800fd0301120112010000000000000000000000000000000301ff00210000000000000000000000000000000000000000000000000000000000000e05000000000000016200000000004c4b4000000000000003e800000000000003e80000000203f000050300000000000000000000000000000000000000000000000000000000000001000300000000000000000000000000000000000000000000000000000000000002000300000000000000000000000000000000000000000000000000000000000003000300000000000000000000000000000000000000000000000000000000000004000300000000000000000000000000000000000000000000000000000000000005000266000000000000000000000000000003012300000000000000000000000000000000000000010000000000000000000000000000000a00fd00fd03011200620100000000000000000000000000000003017200233a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007c0001000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000b03011200430100000000000000000000000000000003015300243a000000000000000000000000000000000000000000000000000000000000000267000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000020b00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000700fd00fd03011200640100000000000000000000000000000003017400843a00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006a000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000660000000000000000000000000000000000000000000000000000000000000002640000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112004a0100000000000000000000000000000003015a00823a000000000000000000000000000000000000000000000000000000000000000000000000000000ff008888888888888888888888888888888888888888888888888888888888880100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a0000000000000000000000000000000000000000000000000000000000000067000000000000000000000000000000000000000000000000000000000000000265000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d0000000000000000000000000000000000000000000000000000000000000000000000000000010000000000003e80ff00000000000000000000000000000000000000000000000000000000000000000003f00003000000000000000000000000000000000000000000000000000000000000055511020203e80401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffab000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000020a000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c3010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000b00000000000000000000000000000000000000000000000000000000000000020d00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000700fd00fd03011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a00000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000002700000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000030112002c0100000000000000000000000000000003013c00833a00000000000000000000000000000000000000000000000000000000000000000000000000000100000100000000000000000000000000000003011200640100000000000000000000000000000003017400843a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000039000100000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000003011200630100000000000000000000000000000003017300853a000000000000000000000000000000000000000000000000000000000000006500000000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000703001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000020c000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001200640300000000000000000000000000000003007400843d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032010000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000003001205ac030000000000000000000000000000000300ff00803d00000000000000000000000000000000000000000000000000000000000000000000000000000200000000000b0838ff00000000000000000000000000000000000000000000000000000000000000000003f0000300000000000000000000000000000000000000000000000000000000000005551202030927c00401a0060800000e00000100000a00000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0300c1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff53000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000fd03001200a4030000000000000000000000000000000300b400843d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007501000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000006705000000000000000000000000000000000000000000000000000000000000060300000000000000000000000000000003001200630300000000000000000000000000000003007300853d000000000000000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000000000000000000000020f00000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000700fd00fd0c007d02000000013a000000000000000000000000000000000000000000000000000000000000000000000000000000800258020000000000002200204b0000000000000000000000000000000000000000000000000000000000000014c00000000000001600142800000000000000000000000000000000000000050000200c005e0200000001730000000000000000000000000000000000000000000000000000000000000000000000000000000001a701000000000000220020b200000000000000000000000000000000000000000000000000000000000000000000000c00000c00000c00000c00000c000007").unwrap(), &(Arc::clone(&logger) as Arc<dyn Logger>));
let log_entries = logger.lines.lock().unwrap();
assert_eq!(log_entries.get(&("lightning::ln::peer_handler".to_string(), "Handling SendAcceptChannel event in peer_handler for node 030000000000000000000000000000000000000000000000000000000000000002 for channel ff4f00f805273c1b203bb5ebf8436bfde57b3be8c2f5e95d9491dbb181909679".to_string())), Some(&1)); // 1
use lightning::util::test_channel_signer::TestChannelSigner;
use lightning::util::logger::Logger;
use lightning::util::ser::{Readable, Writeable, Writer};
-use lightning::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessagePath, OnionMessenger};
+use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage};
use crate::utils::test_logger;
const CUSTOM_MESSAGE_TYPE: u64 = 4242;
const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32];
-impl CustomOnionMessageContents for TestCustomMessage {
+impl OnionMessageContents for TestCustomMessage {
fn tlv_type(&self) -> u64 {
CUSTOM_MESSAGE_TYPE
}
buffer.read_to_end(&mut buf)?;
return Ok(Some(TestCustomMessage {}))
}
+ fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>> {
+ vec![]
+ }
}
pub struct VecWriter(pub Vec<u8>);
#[test]
fn test_no_onion_message_breakage() {
- let one_hop_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01ae0276020000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000e0101022a0000000000000000000000000000014551231950b75fc4402da1732fc9bebf00109500000000000000000000000000000004106d000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000";
- let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) };
- super::do_test(&::hex::decode(one_hop_om).unwrap(), &logger);
- {
- let log_entries = logger.lines.lock().unwrap();
- assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
- "Received an onion message with path_id None and a reply_path".to_string())), Some(&1));
- assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
- "Responding to onion message with path_id None".to_string())), Some(&1));
- assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
- "Failed responding to onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1));
- }
-
let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210202020202020202020202020202020202020202020202020202020202020202026d000000000000000000000000000000eb0000000000000000000000000000000000000000000000000000000000000036041096000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000";
let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) };
super::do_test(&::hex::decode(two_unblinded_hops_om).unwrap(), &logger);
// You may not use this file except in accordance with one or both of these
// licenses.
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::transaction::TxOut;
-use bitcoin::hash_types::BlockHash;
use lightning::blinded_path::{BlindedHop, BlindedPath};
use lightning::chain::transaction::OutPoint;
net_graph: &'a NetworkGraph<&'b test_logger::TestLogger<Out>>,
}
impl<Out: test_logger::Output> UtxoLookup for FuzzChainSource<'_, '_, Out> {
- fn get_utxo(&self, _genesis_hash: &BlockHash, _short_channel_id: u64) -> UtxoResult {
+ fn get_utxo(&self, _chain_hash: &ChainHash, _short_channel_id: u64) -> UtxoResult {
let input_slice = self.input.get_slice(2);
if input_slice.is_none() { return UtxoResult::Sync(Err(UtxoLookupError::UnknownTx)); }
let input_slice = input_slice.unwrap();
use crate::{AsyncBlockSourceResult, BlockData, BlockSource, BlockSourceError};
use bitcoin::blockdata::block::Block;
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::transaction::{TxOut, OutPoint};
use bitcoin::hash_types::BlockHash;
CMH::Target: CustomMessageHandler,
NS::Target: NodeSigner,
{
- fn get_utxo(&self, _genesis_hash: &BlockHash, short_channel_id: u64) -> UtxoResult {
+ fn get_utxo(&self, _chain_hash: &ChainHash, short_channel_id: u64) -> UtxoResult {
let res = UtxoFuture::new();
let fut = res.clone();
let source = self.source.clone();
fn handle_error(&self, _their_node_id: &PublicKey, _msg: &ErrorMessage) {}
fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
fn provided_init_features(&self, _their_node_id: &PublicKey) -> InitFeatures { InitFeatures::empty() }
- fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
+ fn get_chain_hashes(&self) -> Option<Vec<ChainHash>> {
Some(vec![ChainHash::using_genesis_block(Network::Testnet)])
}
}
use core::ops::Deref;
use core::sync::atomic::Ordering;
-use bitcoin::BlockHash;
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::secp256k1::PublicKey;
use lightning::ln::msgs::{
return Err(DecodeError::UnknownVersion.into());
}
- let chain_hash: BlockHash = Readable::read(read_cursor)?;
- let ng_genesis_hash = self.network_graph.get_genesis_hash();
- if chain_hash != ng_genesis_hash {
+ let chain_hash: ChainHash = Readable::read(read_cursor)?;
+ let ng_chain_hash = self.network_graph.get_chain_hash();
+ if chain_hash != ng_chain_hash {
return Err(
LightningError {
err: "Rapid Gossip Sync data's chain hash does not match the network graph's".to_owned(),
}
impl BlindedPath {
+ /// Create a one-hop blinded path for a message.
+ pub fn one_hop_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
+ recipient_node_id: PublicKey, entropy_source: &ES, secp_ctx: &Secp256k1<T>
+ ) -> Result<Self, ()> {
+ Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx)
+ }
+
/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
/// pubkey in `node_pks` will be the destination node.
///
- /// Errors if less than two hops are provided or if `node_pk`(s) are invalid.
+ /// Errors if no hops are provided or if `node_pk`(s) are invalid.
// TODO: make all payloads the same size with padding + add dummy hops
- pub fn new_for_message<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>
- (node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>) -> Result<Self, ()>
- {
- if node_pks.len() < 2 { return Err(()) }
+ pub fn new_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
+ node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
+ ) -> Result<Self, ()> {
+ if node_pks.is_empty() { return Err(()) }
let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
let introduction_node_id = node_pks[0];
}
/// Create a one-hop blinded path for a payment.
- pub fn one_hop_for_payment<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
+ pub fn one_hop_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
payee_node_id: PublicKey, payee_tlvs: payment::ReceiveTlvs, entropy_source: &ES,
secp_ctx: &Secp256k1<T>
) -> Result<(BlindedPayInfo, Self), ()> {
///
/// [`ForwardTlvs`]: crate::blinded_path::payment::ForwardTlvs
// TODO: make all payloads the same size with padding + add dummy hops
- pub(crate) fn new_for_payment<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>(
+ pub(crate) fn new_for_payment<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[payment::ForwardNode], payee_node_id: PublicKey,
payee_tlvs: payment::ReceiveTlvs, htlc_maximum_msat: u64, entropy_source: &ES,
secp_ctx: &Secp256k1<T>
/// [`BlindedHop`]: crate::blinded_path::BlindedHop
#[derive(Clone, Debug)]
pub struct PaymentConstraints {
- /// The maximum total CLTV delta that is acceptable when relaying a payment over this
- /// [`BlindedHop`].
+ /// The maximum total CLTV that is acceptable when relaying a payment over this [`BlindedHop`].
pub max_cltv_expiry: u32,
/// The minimum value, in msat, that may be accepted by the node corresponding to this
/// [`BlindedHop`].
/// estimation.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum ConfirmationTarget {
- /// We'd like a transaction to confirm in the future, but don't want to commit most of the fees
- /// required to do so yet. The remaining fees will come via a Child-Pays-For-Parent (CPFP) fee
- /// bump of the transaction.
- ///
- /// The feerate returned should be the absolute minimum feerate required to enter most node
- /// mempools across the network. Note that if you are not able to obtain this feerate estimate,
- /// you should likely use the furthest-out estimate allowed by your fee estimator.
- MempoolMinimum,
- /// We are happy with a transaction confirming slowly, at least within a day or so worth of
- /// blocks.
- Background,
- /// We'd like a transaction to confirm without major delayed, i.e., within the next 12-24 blocks.
- Normal,
- /// We'd like a transaction to confirm in the next few blocks.
- HighPriority,
+ /// We have some funds available on chain which we need to spend prior to some expiry time at
+ /// which point our counterparty may be able to steal them. Generally we have in the high tens
+ /// to low hundreds of blocks to get our transaction on-chain, but we shouldn't risk too low a
+ /// fee - this should be a relatively high priority feerate.
+ OnChainSweep,
+ /// The highest feerate we will allow our channel counterparty to have in a non-anchor channel.
+ ///
+ /// This is the feerate on the transaction which we (or our counterparty) will broadcast in
+ /// order to close the channel unilaterally. Because our counterparty must ensure they can
+ /// always broadcast the latest state, this value being too low will cause immediate
+ /// force-closures.
+ ///
+ /// Allowing this value to be too high can allow our counterparty to burn our HTLC outputs to
+ /// dust, which can result in HTLCs failing or force-closures (when the dust HTLCs exceed
+ /// [`ChannelConfig::max_dust_htlc_exposure`]).
+ ///
+ /// Because most nodes use a feerate estimate which is based on a relatively high priority
+ /// transaction entering the current mempool, setting this to a small multiple of your current
+ /// high priority feerate estimate should suffice.
+ ///
+ /// [`ChannelConfig::max_dust_htlc_exposure`]: crate::util::config::ChannelConfig::max_dust_htlc_exposure
+ MaxAllowedNonAnchorChannelRemoteFee,
+ /// This is the lowest feerate we will allow our channel counterparty to have in an anchor
+ /// channel in order to close the channel if a channel party goes away. Because our counterparty
+ /// must ensure they can always broadcast the latest state, this value being too high will cause
+ /// immediate force-closures.
+ ///
+ /// This needs to be sufficient to get into the mempool when the channel needs to
+ /// be force-closed. Setting too low may result in force-closures. Because this is for anchor
+ /// channels, we can always bump the feerate later, the feerate here only needs to suffice to
+ /// enter the mempool.
+ ///
+ /// A good estimate is the expected mempool minimum at the time of force-closure. Obviously this
+ /// is not an estimate which is very easy to calculate because we do not know the future. Using
+ /// a simple long-term fee estimate or tracking of the mempool minimum is a good approach to
+ /// ensure you can always close the channel. A future change to Bitcoin's P2P network
+ /// (package relay) may obviate the need for this entirely.
+ MinAllowedAnchorChannelRemoteFee,
+ /// The lowest feerate we will allow our channel counterparty to have in a non-anchor channel.
+ /// This needs to be sufficient to get confirmed when the channel needs to be force-closed.
+ /// Setting too low may result in force-closures.
+ ///
+ /// This is the feerate on the transaction which we (or our counterparty) will broadcast in
+ /// order to close the channel if a channel party goes away. Because our counterparty must
+ /// ensure they can always broadcast the latest state, this value being too high will cause
+ /// immediate force-closures.
+ ///
+ /// This feerate represents the fee we pick now, which must be sufficient to enter a block at an
+ /// arbitrary time in the future. Obviously this is not an estimate which is very easy to
+ /// calculate. This can leave channels subject to being unable to close if feerates rise, and in
+ /// general you should prefer anchor channels to ensure you can increase the feerate when the
+ /// transactions need broadcasting.
+ ///
+ /// Do note some fee estimators round up to the next full sat/vbyte (ie 250 sats per kw),
+ /// causing occasional issues with feerate disagreements between an initiator that wants a
+ /// feerate of 1.1 sat/vbyte and a receiver that wants 1.1 rounded up to 2. If your fee
+ /// estimator rounds subtracting 250 to your desired feerate here can help avoid this issue.
+ ///
+ /// [`ChannelConfig::max_dust_htlc_exposure`]: crate::util::config::ChannelConfig::max_dust_htlc_exposure
+ MinAllowedNonAnchorChannelRemoteFee,
+ /// This is the feerate on the transaction which we (or our counterparty) will broadcast in
+ /// order to close the channel if a channel party goes away.
+ ///
+ /// This needs to be sufficient to get into the mempool when the channel needs to
+ /// be force-closed. Setting too low may result in force-closures. Because this is for anchor
+ /// channels, it can be a low value as we can always bump the feerate later.
+ ///
+ /// A good estimate is the expected mempool minimum at the time of force-closure. Obviously this
+ /// is not an estimate which is very easy to calculate because we do not know the future. Using
+ /// a simple long-term fee estimate or tracking of the mempool minimum is a good approach to
+ /// ensure you can always close the channel. A future change to Bitcoin's P2P network
+ /// (package relay) may obviate the need for this entirely.
+ AnchorChannelFee,
+ /// Lightning is built around the ability to broadcast a transaction in the future to close our
+ /// channel and claim all pending funds. In order to do so, non-anchor channels are built with
+ /// transactions which we need to be able to broadcast at some point in the future.
+ ///
+ /// This feerate represents the fee we pick now, which must be sufficient to enter a block at an
+ /// arbitrary time in the future. Obviously this is not an estimate which is very easy to
+ /// calculate, so most lightning nodes use some relatively high-priority feerate using the
+ /// current mempool. This leaves channels subject to being unable to close if feerates rise, and
+ /// in general you should prefer anchor channels to ensure you can increase the feerate when the
+ /// transactions need broadcasting.
+ ///
+ /// Since this should represent the feerate of a channel close that does not need fee
+ /// bumping, this is also used as an upper bound for our attempted feerate when doing cooperative
+ /// closure of any channel.
+ NonAnchorChannelFee,
+ /// When cooperatively closing a channel, this is the minimum feerate we will accept.
+ /// Recommended at least within a day or so worth of blocks.
+ ///
+ /// This will also be used when initiating a cooperative close of a channel. When closing a
+ /// channel you can override this fee by using
+ /// [`ChannelManager::close_channel_with_feerate_and_script`].
+ ///
+ /// [`ChannelManager::close_channel_with_feerate_and_script`]: crate::ln::channelmanager::ChannelManager::close_channel_with_feerate_and_script
+ ChannelCloseMinimum,
}
/// A trait which should be implemented to provide feerate information on a number of time
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
let fee_estimator = LowerBoundedFeeEstimator::new(test_fee_estimator);
- assert_eq!(fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Background), FEERATE_FLOOR_SATS_PER_KW);
+ assert_eq!(fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee), FEERATE_FLOOR_SATS_PER_KW);
}
#[test]
let test_fee_estimator = &TestFeeEstimator { sat_per_kw };
let fee_estimator = LowerBoundedFeeEstimator::new(test_fee_estimator);
- assert_eq!(fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Background), sat_per_kw);
+ assert_eq!(fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee), sat_per_kw);
}
}
if cached_request.is_malleable() {
if cached_request.requires_external_funding() {
let target_feerate_sat_per_1000_weight = cached_request.compute_package_feerate(
- fee_estimator, ConfirmationTarget::HighPriority, force_feerate_bump
+ fee_estimator, ConfirmationTarget::OnChainSweep, force_feerate_bump
);
if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
return Some((
debug_assert_eq!(tx.txid(), self.holder_commitment.trust().txid(),
"Holder commitment transaction mismatch");
- let conf_target = ConfirmationTarget::HighPriority;
+ let conf_target = ConfirmationTarget::OnChainSweep;
let package_target_feerate_sat_per_1000_weight = cached_request
.compute_package_feerate(fee_estimator, conf_target, force_feerate_bump);
if let Some(input_amount_sat) = output.funding_amount {
use crate::ln::chan_utils::{TxCreationKeys, HTLCOutputInCommitment};
use crate::ln::chan_utils;
use crate::ln::msgs::DecodeError;
-use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
+use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT, compute_feerate_sat_per_1000_weight, fee_for_weight, FEERATE_FLOOR_SATS_PER_KW};
use crate::sign::WriteableEcdsaChannelSigner;
use crate::chain::onchaintx::{ExternalHTLCClaim, OnchainTxHandler};
use crate::util::logger::Logger;
}
/// Attempt to propose a bumping fee for a transaction from its spent output's values and predicted
-/// weight. We start with the highest priority feerate returned by the node's fee estimator then
-/// fall-back to lower priorities until we have enough value available to suck from.
+/// weight. We first try our [`OnChainSweep`] feerate, if it's not enough we try to sweep half of
+/// the input amounts.
///
/// If the proposed fee is less than the available spent output's values, we return the proposed
-/// fee and the corresponding updated feerate. If the proposed fee is equal or more than the
-/// available spent output's values, we return nothing
+/// fee and the corresponding updated feerate. If fee is under [`FEERATE_FLOOR_SATS_PER_KW`], we
+/// return nothing.
+///
+/// [`OnChainSweep`]: crate::chain::chaininterface::ConfirmationTarget::OnChainSweep
+/// [`FEERATE_FLOOR_SATS_PER_KW`]: crate::chain::chaininterface::MIN_RELAY_FEE_SAT_PER_1000_WEIGHT
fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predicted_weight: usize, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
where F::Target: FeeEstimator,
L::Target: Logger,
{
- let mut updated_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64;
- let mut fee = updated_feerate * (predicted_weight as u64) / 1000;
- if input_amounts <= fee {
- updated_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal) as u64;
- fee = updated_feerate * (predicted_weight as u64) / 1000;
- if input_amounts <= fee {
- updated_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Background) as u64;
- fee = updated_feerate * (predicted_weight as u64) / 1000;
- if input_amounts <= fee {
- log_error!(logger, "Failed to generate an on-chain punishment tx as even low priority fee ({} sat) was more than the entire claim balance ({} sat)",
- fee, input_amounts);
- None
- } else {
- log_warn!(logger, "Used low priority fee for on-chain punishment tx as high priority fee was more than the entire claim balance ({} sat)",
- input_amounts);
- Some((fee, updated_feerate))
- }
- } else {
- log_warn!(logger, "Used medium priority fee for on-chain punishment tx as high priority fee was more than the entire claim balance ({} sat)",
- input_amounts);
- Some((fee, updated_feerate))
- }
+ let sweep_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::OnChainSweep);
+ let fee_rate = cmp::min(sweep_feerate, compute_feerate_sat_per_1000_weight(input_amounts / 2, predicted_weight as u64)) as u64;
+ let fee = fee_rate * (predicted_weight as u64) / 1000;
+
+ // if the fee rate is below the floor, we don't sweep
+ if fee_rate < FEERATE_FLOOR_SATS_PER_KW as u64 {
+ log_error!(logger, "Failed to generate an on-chain tx with fee ({} sat/kw) was less than the floor ({} sat/kw)",
+ fee_rate, FEERATE_FLOOR_SATS_PER_KW);
+ None
} else {
- Some((fee, updated_feerate))
+ Some((fee, fee_rate))
}
}
sender_intended_total_msat: Option<u64>,
},
/// Indicates a request for an invoice failed to yield a response in a reasonable amount of time
- /// or was explicitly abandoned by [`ChannelManager::abandon_payment`].
+ /// or was explicitly abandoned by [`ChannelManager::abandon_payment`]. This may be for an
+ /// [`InvoiceRequest`] sent for an [`Offer`] or for a [`Refund`] that hasn't been redeemed.
///
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment
- #[cfg(invreqfailed)]
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ /// [`Offer`]: crate::offers::offer::Offer
+ /// [`Refund`]: crate::offers::refund::Refund
InvoiceRequestFailed {
/// The `payment_id` to have been associated with payment for the requested invoice.
payment_id: PaymentId,
(8, funding_txo, required),
});
},
- #[cfg(invreqfailed)]
&Event::InvoiceRequestFailed { ref payment_id } => {
33u8.write(writer)?;
write_tlv_fields!(writer, {
};
f()
},
- #[cfg(invreqfailed)]
33u8 => {
let f = || {
_init_and_read_len_prefixed_tlv_fields!(reader, {
fn get_and_clear_pending_msg_events(&self) -> Vec<MessageSendEvent>;
}
-/// A trait indicating an object may generate onion messages to send
-pub trait OnionMessageProvider {
- /// Gets the next pending onion message for the peer with the given node id.
- fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<msgs::OnionMessage>;
-}
-
/// A trait indicating an object may generate events.
///
/// Events are processed by passing an [`EventHandler`] to [`process_pending_events`].
use crate::events::MessageSendEventsProvider;
use crate::ln::channelmanager;
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
-use crate::ln::features::Bolt12InvoiceFeatures;
use crate::ln::functional_test_utils::*;
use crate::ln::outbound_payment::Retry;
use crate::prelude::*;
nodes[3].node.get_our_node_id(), payee_tlvs, &chanmon_cfgs[3].keys_manager, &secp_ctx
).unwrap();
- let bolt12_features: Bolt12InvoiceFeatures =
- channelmanager::provided_invoice_features(&UserConfig::default()).to_context();
+ let bolt12_features =
+ channelmanager::provided_bolt12_invoice_features(&UserConfig::default());
let route_params = RouteParameters::from_payment_params_and_value(
- PaymentParameters::blinded(vec![blinded_path])
- .with_bolt12_features(bolt12_features).unwrap(),
+ PaymentParameters::blinded(vec![blinded_path]).with_bolt12_features(bolt12_features).unwrap(),
amt_msat,
);
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
do_test_reload_mon_update_completion_actions(true);
do_test_reload_mon_update_completion_actions(false);
}
+
+fn do_test_glacial_peer_cant_hang(hold_chan_a: bool) {
+ // Test that if a peer manages to send an `update_fulfill_htlc` message without a
+ // `commitment_signed`, disconnects, then replays the `update_fulfill_htlc` message it doesn't
+ // result in a channel hang. This was previously broken as the `DuplicateClaim` case wasn't
+ // handled when claiming an HTLC and handling wasn't added when completion actions were added
+ // (which must always complete at some point).
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1);
+ create_announced_chan_between_nodes(&nodes, 1, 2);
+
+ // Route a payment from A, through B, to C, then claim it on C. Replay the
+ // `update_fulfill_htlc` twice on B to check that B doesn't hang.
+ let (payment_preimage, payment_hash, ..) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000);
+
+ nodes[2].node.claim_funds(payment_preimage);
+ check_added_monitors(&nodes[2], 1);
+ expect_payment_claimed!(nodes[2], payment_hash, 1_000_000);
+
+ let cs_updates = get_htlc_update_msgs(&nodes[2], &nodes[1].node.get_our_node_id());
+ if hold_chan_a {
+ // The first update will be on the A <-> B channel, which we allow to complete.
+ chanmon_cfgs[1].persister.set_update_ret(ChannelMonitorUpdateStatus::InProgress);
+ }
+ nodes[1].node.handle_update_fulfill_htlc(&nodes[2].node.get_our_node_id(), &cs_updates.update_fulfill_htlcs[0]);
+ check_added_monitors(&nodes[1], 1);
+
+ if !hold_chan_a {
+ let bs_updates = get_htlc_update_msgs(&nodes[1], &nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &bs_updates.update_fulfill_htlcs[0]);
+ commitment_signed_dance!(nodes[0], nodes[1], bs_updates.commitment_signed, false);
+ expect_payment_sent!(&nodes[0], payment_preimage);
+ }
+
+ nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id());
+ nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id());
+
+ let mut reconnect = ReconnectArgs::new(&nodes[1], &nodes[2]);
+ reconnect.pending_htlc_claims = (1, 0);
+ reconnect_nodes(reconnect);
+
+ if !hold_chan_a {
+ expect_payment_forwarded!(nodes[1], nodes[0], nodes[2], Some(1000), false, false);
+ send_payment(&nodes[0], &[&nodes[1], &nodes[2]], 100_000);
+ } else {
+ assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+ assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
+
+ let (route, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(&nodes[1], nodes[2], 1_000_000);
+
+ nodes[1].node.send_payment_with_route(&route, payment_hash_2,
+ RecipientOnionFields::secret_only(payment_secret_2), PaymentId(payment_hash_2.0)).unwrap();
+ check_added_monitors(&nodes[1], 0);
+
+ assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+ assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
+ }
+}
+
+#[test]
+fn test_glacial_peer_cant_hang() {
+ do_test_glacial_peer_cant_hang(false);
+ do_test_glacial_peer_cant_hang(true);
+}
// You may not use this file except in accordance with one or both of these
// licenses.
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::{Script,Builder};
use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
use bitcoin::util::sighash;
cur_holder_commitment_transaction_number: u64,
cur_counterparty_commitment_transaction_number: u64,
- value_to_self_msat: u64, // Excluding all pending_htlcs, excluding fees
+ value_to_self_msat: u64, // Excluding all pending_htlcs, fees, and anchor outputs
pending_inbound_htlcs: Vec<InboundHTLCOutput>,
pending_outbound_htlcs: Vec<OutboundHTLCOutput>,
holding_cell_htlc_updates: Vec<HTLCUpdateAwaitingACK>,
match self.config.options.max_dust_htlc_exposure {
MaxDustHTLCExposure::FeeRateMultiplier(multiplier) => {
let feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(
- ConfirmationTarget::HighPriority);
+ ConfirmationTarget::OnChainSweep);
feerate_per_kw as u64 * multiplier
},
MaxDustHTLCExposure::FixedLimitMsat(limit) => limit,
let mut available_capacity_msat = outbound_capacity_msat;
+ let anchor_outputs_value_msat = if context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+ ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000
+ } else {
+ 0
+ };
if context.is_outbound() {
// We should mind channel commit tx fee when computing how much of the available capacity
// can be used in the next htlc. Mirrors the logic in send_htlc.
}
let htlc_above_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000, HTLCInitiator::LocalOffered);
- let max_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * context.next_local_commit_tx_fee_msat(htlc_above_dust, Some(()));
+ let mut max_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(htlc_above_dust, Some(()));
let htlc_dust = HTLCCandidate::new(real_dust_limit_timeout_sat * 1000 - 1, HTLCInitiator::LocalOffered);
- let min_reserved_commit_tx_fee_msat = FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE * context.next_local_commit_tx_fee_msat(htlc_dust, Some(()));
+ let mut min_reserved_commit_tx_fee_msat = context.next_local_commit_tx_fee_msat(htlc_dust, Some(()));
+ if !context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+ max_reserved_commit_tx_fee_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
+ min_reserved_commit_tx_fee_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
+ }
// We will first subtract the fee as if we were above-dust. Then, if the resulting
// value ends up being below dust, we have this fee available again. In that case,
// match the value to right-below-dust.
- let mut capacity_minus_commitment_fee_msat: i64 = (available_capacity_msat as i64) - (max_reserved_commit_tx_fee_msat as i64);
+ let mut capacity_minus_commitment_fee_msat: i64 = available_capacity_msat as i64 -
+ max_reserved_commit_tx_fee_msat as i64 - anchor_outputs_value_msat as i64;
if capacity_minus_commitment_fee_msat < (real_dust_limit_timeout_sat as i64) * 1000 {
let one_htlc_difference_msat = max_reserved_commit_tx_fee_msat - min_reserved_commit_tx_fee_msat;
debug_assert!(one_htlc_difference_msat != 0);
let remote_balance_msat = (context.channel_value_satoshis * 1000 - context.value_to_self_msat)
.saturating_sub(inbound_stats.pending_htlcs_value_msat);
- if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat {
+ if remote_balance_msat < max_reserved_commit_tx_fee_msat + holder_selected_chan_reserve_msat + anchor_outputs_value_msat {
// If another HTLC's fee would reduce the remote's balance below the reserve limit
// we've selected for them, we can only send dust HTLCs.
available_capacity_msat = cmp::min(available_capacity_msat, real_dust_limit_success_sat * 1000 - 1);
// Get the fee cost in MSATS of a commitment tx with a given number of HTLC outputs.
// Note that num_htlcs should not include dust HTLCs.
-fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
+pub(crate) fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, channel_type_features: &ChannelTypeFeatures) -> u64 {
// Note that we need to divide before multiplying to round properly,
// since the lowest denomination of bitcoin on-chain is the satoshi.
(commitment_tx_base_weight(channel_type_features) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * feerate_per_kw as u64 / 1000 * 1000
// apply to channels supporting anchor outputs since HTLC transactions are pre-signed with a
// zero fee, so their fee is no longer considered to determine dust limits.
if !channel_type.supports_anchors_zero_fee_htlc_tx() {
- let upper_limit = cmp::max(250 * 25,
- fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::HighPriority) as u64 * 10);
+ let upper_limit =
+ fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee) as u64;
if feerate_per_kw as u64 > upper_limit {
return Err(ChannelError::Close(format!("Peer's feerate much too high. Actual: {}. Our expected upper limit: {}", feerate_per_kw, upper_limit)));
}
}
- // We can afford to use a lower bound with anchors than previously since we can now bump
- // fees when broadcasting our commitment. However, we must still make sure we meet the
- // minimum mempool feerate, until package relay is deployed, such that we can ensure the
- // commitment transaction propagates throughout node mempools on its own.
let lower_limit_conf_target = if channel_type.supports_anchors_zero_fee_htlc_tx() {
- ConfirmationTarget::MempoolMinimum
+ ConfirmationTarget::MinAllowedAnchorChannelRemoteFee
} else {
- ConfirmationTarget::Background
+ ConfirmationTarget::MinAllowedNonAnchorChannelRemoteFee
};
let lower_limit = fee_estimator.bounded_sat_per_1000_weight(lower_limit_conf_target);
- // Some fee estimators round up to the next full sat/vbyte (ie 250 sats per kw), causing
- // occasional issues with feerate disagreements between an initiator that wants a feerate
- // of 1.1 sat/vbyte and a receiver that wants 1.1 rounded up to 2. Thus, we always add 250
- // sat/kw before the comparison here.
- if feerate_per_kw + 250 < lower_limit {
+ if feerate_per_kw < lower_limit {
if let Some(cur_feerate) = cur_feerate_per_kw {
if feerate_per_kw > cur_feerate {
log_warn!(logger,
return Ok(());
}
}
- return Err(ChannelError::Close(format!("Peer's feerate much too low. Actual: {}. Our expected lower limit: {} (- 250)", feerate_per_kw, lower_limit)));
+ return Err(ChannelError::Close(format!("Peer's feerate much too low. Actual: {}. Our expected lower limit: {}", feerate_per_kw, lower_limit)));
}
Ok(())
}
/// and the channel is now usable (and public), this may generate an announcement_signatures to
/// reply with.
pub fn channel_ready<NS: Deref, L: Deref>(
- &mut self, msg: &msgs::ChannelReady, node_signer: &NS, genesis_block_hash: BlockHash,
+ &mut self, msg: &msgs::ChannelReady, node_signer: &NS, chain_hash: ChainHash,
user_config: &UserConfig, best_block: &BestBlock, logger: &L
) -> Result<Option<msgs::AnnouncementSignatures>, ChannelError>
where
log_info!(logger, "Received channel_ready from peer for channel {}", &self.context.channel_id());
- Ok(self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, best_block.height(), logger))
+ Ok(self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height(), logger))
}
pub fn update_add_htlc<F, FE: Deref, L: Deref>(
if inbound_stats.pending_htlcs_value_msat + msg.amount_msat > self.context.holder_max_htlc_value_in_flight_msat {
return Err(ChannelError::Close(format!("Remote HTLC add would put them over our max HTLC value ({})", self.context.holder_max_htlc_value_in_flight_msat)));
}
+
// Check holder_selected_channel_reserve_satoshis (we're getting paid, so they have to at least meet
// the reserve_satoshis we told them to always have as direct payment so that they lose
// something if we punish them for broadcasting an old state).
// Check that the remote can afford to pay for this HTLC on-chain at the current
// feerate_per_kw, while maintaining their channel reserve (as required by the spec).
- let remote_commit_tx_fee_msat = if self.context.is_outbound() { 0 } else {
- let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
- self.context.next_remote_commit_tx_fee_msat(htlc_candidate, None) // Don't include the extra fee spike buffer HTLC in calculations
- };
- if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat {
- return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned()));
- };
-
- if pending_remote_value_msat - msg.amount_msat - remote_commit_tx_fee_msat < self.context.holder_selected_channel_reserve_satoshis * 1000 {
- return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value".to_owned()));
+ {
+ let remote_commit_tx_fee_msat = if self.context.is_outbound() { 0 } else {
+ let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
+ self.context.next_remote_commit_tx_fee_msat(htlc_candidate, None) // Don't include the extra fee spike buffer HTLC in calculations
+ };
+ let anchor_outputs_value_msat = if !self.context.is_outbound() && self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+ ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000
+ } else {
+ 0
+ };
+ if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(anchor_outputs_value_msat) < remote_commit_tx_fee_msat {
+ return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned()));
+ };
+ if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(remote_commit_tx_fee_msat).saturating_sub(anchor_outputs_value_msat) < self.context.holder_selected_channel_reserve_satoshis * 1000 {
+ return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value".to_owned()));
+ }
}
+ let anchor_outputs_value_msat = if self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+ ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000
+ } else {
+ 0
+ };
if !self.context.is_outbound() {
- // `2 *` and `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
- // the spec because in the spec, the fee spike buffer requirement doesn't exist on the
- // receiver's side, only on the sender's.
- // Note that when we eventually remove support for fee updates and switch to anchor output
- // fees, we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep
- // the extra htlc when calculating the next remote commitment transaction fee as we should
- // still be able to afford adding this HTLC plus one more future HTLC, regardless of being
- // sensitive to fee spikes.
+ // `Some(())` is for the fee spike buffer we keep for the remote. This deviates from
+ // the spec because the fee spike buffer requirement doesn't exist on the receiver's
+ // side, only on the sender's. Note that with anchor outputs we are no longer as
+ // sensitive to fee spikes, so we need to account for them.
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
- let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.context.next_remote_commit_tx_fee_msat(htlc_candidate, Some(()));
- if pending_remote_value_msat - msg.amount_msat - self.context.holder_selected_channel_reserve_satoshis * 1000 < remote_fee_cost_incl_stuck_buffer_msat {
+ let mut remote_fee_cost_incl_stuck_buffer_msat = self.context.next_remote_commit_tx_fee_msat(htlc_candidate, Some(()));
+ if !self.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
+ remote_fee_cost_incl_stuck_buffer_msat *= FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE;
+ }
+ if pending_remote_value_msat.saturating_sub(msg.amount_msat).saturating_sub(self.context.holder_selected_channel_reserve_satoshis * 1000).saturating_sub(anchor_outputs_value_msat) < remote_fee_cost_incl_stuck_buffer_msat {
// Note that if the pending_forward_status is not updated here, then it's because we're already failing
// the HTLC, i.e. its status is already set to failing.
log_info!(logger, "Attempting to fail HTLC due to fee spike buffer violation in channel {}. Rebalancing is required.", &self.context.channel_id());
// Check that they won't violate our local required channel reserve by adding this HTLC.
let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered);
let local_commit_tx_fee_msat = self.context.next_local_commit_tx_fee_msat(htlc_candidate, None);
- if self.context.value_to_self_msat < self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 + local_commit_tx_fee_msat {
+ if self.context.value_to_self_msat < self.context.counterparty_selected_channel_reserve_satoshis.unwrap() * 1000 + local_commit_tx_fee_msat + anchor_outputs_value_msat {
return Err(ChannelError::Close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned()));
}
}
/// successfully and we should restore normal operation. Returns messages which should be sent
/// to the remote side.
pub fn monitor_updating_restored<L: Deref, NS: Deref>(
- &mut self, logger: &L, node_signer: &NS, genesis_block_hash: BlockHash,
+ &mut self, logger: &L, node_signer: &NS, chain_hash: ChainHash,
user_config: &UserConfig, best_block_height: u32
) -> MonitorRestoreUpdates
where
})
} else { None };
- let announcement_sigs = self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, best_block_height, logger);
+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block_height, logger);
let mut accepted_htlcs = Vec::new();
mem::swap(&mut accepted_htlcs, &mut self.context.monitor_pending_forwards);
/// [`super::channelmanager::ChannelManager::force_close_all_channels_without_broadcasting_txn`].
pub fn channel_reestablish<L: Deref, NS: Deref>(
&mut self, msg: &msgs::ChannelReestablish, logger: &L, node_signer: &NS,
- genesis_block_hash: BlockHash, user_config: &UserConfig, best_block: &BestBlock
+ chain_hash: ChainHash, user_config: &UserConfig, best_block: &BestBlock
) -> Result<ReestablishResponses, ChannelError>
where
L::Target: Logger,
if msg.next_local_commitment_number >= INITIAL_COMMITMENT_NUMBER || msg.next_remote_commitment_number >= INITIAL_COMMITMENT_NUMBER ||
msg.next_local_commitment_number == 0 {
- return Err(ChannelError::Close("Peer sent a garbage channel_reestablish (usually an lnd node with lost state asking us to force-close for them)".to_owned()));
+ return Err(ChannelError::Close("Peer sent an invalid channel_reestablish to force close in a non-standard way".to_owned()));
}
if msg.next_remote_commitment_number > 0 {
let shutdown_msg = self.get_outbound_shutdown();
- let announcement_sigs = self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, best_block.height(), logger);
+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, best_block.height(), logger);
if self.context.channel_state & (ChannelState::FundingSent as u32) == ChannelState::FundingSent as u32 {
// If we're waiting on a monitor update, we shouldn't re-send any channel_ready's.
// Propose a range from our current Background feerate to our Normal feerate plus our
// force_close_avoidance_max_fee_satoshis.
// If we fail to come to consensus, we'll have to force-close.
- let mut proposed_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Background);
- let normal_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
+ let mut proposed_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::ChannelCloseMinimum);
+ // Use NonAnchorChannelFee because this should be an estimate for a channel close
+ // that we don't expect to need fee bumping
+ let normal_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
let mut proposed_max_feerate = if self.context.is_outbound() { normal_feerate } else { u32::max_value() };
// The spec requires that (when the channel does not have anchors) we only send absolute
/// In the second, we simply return an Err indicating we need to be force-closed now.
pub fn transactions_confirmed<NS: Deref, L: Deref>(
&mut self, block_hash: &BlockHash, height: u32, txdata: &TransactionData,
- genesis_block_hash: BlockHash, node_signer: &NS, user_config: &UserConfig, logger: &L
+ chain_hash: ChainHash, node_signer: &NS, user_config: &UserConfig, logger: &L
) -> Result<(Option<msgs::ChannelReady>, Option<msgs::AnnouncementSignatures>), ClosureReason>
where
NS::Target: NodeSigner,
// may have already happened for this block).
if let Some(channel_ready) = self.check_get_channel_ready(height) {
log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
- let announcement_sigs = self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, height, logger);
+ let announcement_sigs = self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger);
msgs = (Some(channel_ready), announcement_sigs);
}
}
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
/// back.
pub fn best_block_updated<NS: Deref, L: Deref>(
- &mut self, height: u32, highest_header_time: u32, genesis_block_hash: BlockHash,
+ &mut self, height: u32, highest_header_time: u32, chain_hash: ChainHash,
node_signer: &NS, user_config: &UserConfig, logger: &L
) -> Result<(Option<msgs::ChannelReady>, Vec<(HTLCSource, PaymentHash)>, Option<msgs::AnnouncementSignatures>), ClosureReason>
where
NS::Target: NodeSigner,
L::Target: Logger
{
- self.do_best_block_updated(height, highest_header_time, Some((genesis_block_hash, node_signer, user_config)), logger)
+ self.do_best_block_updated(height, highest_header_time, Some((chain_hash, node_signer, user_config)), logger)
}
fn do_best_block_updated<NS: Deref, L: Deref>(
&mut self, height: u32, highest_header_time: u32,
- genesis_node_signer: Option<(BlockHash, &NS, &UserConfig)>, logger: &L
+ chain_node_signer: Option<(ChainHash, &NS, &UserConfig)>, logger: &L
) -> Result<(Option<msgs::ChannelReady>, Vec<(HTLCSource, PaymentHash)>, Option<msgs::AnnouncementSignatures>), ClosureReason>
where
NS::Target: NodeSigner,
self.context.update_time_counter = cmp::max(self.context.update_time_counter, highest_header_time);
if let Some(channel_ready) = self.check_get_channel_ready(height) {
- let announcement_sigs = if let Some((genesis_block_hash, node_signer, user_config)) = genesis_node_signer {
- self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, height, logger)
+ let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
+ self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)
} else { None };
log_info!(logger, "Sending a channel_ready to our peer for channel {}", &self.context.channel_id);
return Ok((Some(channel_ready), timed_out_htlcs, announcement_sigs));
return Err(ClosureReason::FundingTimedOut);
}
- let announcement_sigs = if let Some((genesis_block_hash, node_signer, user_config)) = genesis_node_signer {
- self.get_announcement_sigs(node_signer, genesis_block_hash, user_config, height, logger)
+ let announcement_sigs = if let Some((chain_hash, node_signer, user_config)) = chain_node_signer {
+ self.get_announcement_sigs(node_signer, chain_hash, user_config, height, logger)
} else { None };
Ok((None, timed_out_htlcs, announcement_sigs))
}
// larger. If we don't know that time has moved forward, we can just set it to the last
// time we saw and it will be ignored.
let best_time = self.context.update_time_counter;
- match self.do_best_block_updated(reorg_height, best_time, None::<(BlockHash, &&NodeSigner, &UserConfig)>, logger) {
+ match self.do_best_block_updated(reorg_height, best_time, None::<(ChainHash, &&NodeSigner, &UserConfig)>, logger) {
Ok((channel_ready, timed_out_htlcs, announcement_sigs)) => {
assert!(channel_ready.is_none(), "We can't generate a funding with 0 confirmations?");
assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?");
///
/// [`ChannelReady`]: crate::ln::msgs::ChannelReady
fn get_channel_announcement<NS: Deref>(
- &self, node_signer: &NS, chain_hash: BlockHash, user_config: &UserConfig,
+ &self, node_signer: &NS, chain_hash: ChainHash, user_config: &UserConfig,
) -> Result<msgs::UnsignedChannelAnnouncement, ChannelError> where NS::Target: NodeSigner {
if !self.context.config.announced_channel {
return Err(ChannelError::Ignore("Channel is not available for public announcements".to_owned()));
}
fn get_announcement_sigs<NS: Deref, L: Deref>(
- &mut self, node_signer: &NS, genesis_block_hash: BlockHash, user_config: &UserConfig,
+ &mut self, node_signer: &NS, chain_hash: ChainHash, user_config: &UserConfig,
best_block_height: u32, logger: &L
) -> Option<msgs::AnnouncementSignatures>
where
}
log_trace!(logger, "Creating an announcement_signatures message for channel {}", &self.context.channel_id());
- let announcement = match self.get_channel_announcement(node_signer, genesis_block_hash, user_config) {
+ let announcement = match self.get_channel_announcement(node_signer, chain_hash, user_config) {
Ok(a) => a,
Err(e) => {
log_trace!(logger, "{:?}", e);
/// channel_announcement message which we can broadcast and storing our counterparty's
/// signatures for later reconstruction/rebroadcast of the channel_announcement.
pub fn announcement_signatures<NS: Deref>(
- &mut self, node_signer: &NS, chain_hash: BlockHash, best_block_height: u32,
+ &mut self, node_signer: &NS, chain_hash: ChainHash, best_block_height: u32,
msg: &msgs::AnnouncementSignatures, user_config: &UserConfig
) -> Result<msgs::ChannelAnnouncement, ChannelError> where NS::Target: NodeSigner {
let announcement = self.get_channel_announcement(node_signer, chain_hash, user_config)?;
/// Gets a signed channel_announcement for this channel, if we previously received an
/// announcement_signatures from our counterparty.
pub fn get_signed_channel_announcement<NS: Deref>(
- &self, node_signer: &NS, chain_hash: BlockHash, best_block_height: u32, user_config: &UserConfig
+ &self, node_signer: &NS, chain_hash: ChainHash, best_block_height: u32, user_config: &UserConfig
) -> Option<msgs::ChannelAnnouncement> where NS::Target: NodeSigner {
if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height {
return None;
let channel_type = Self::get_initial_channel_type(&config, their_features);
debug_assert!(channel_type.is_subset(&channelmanager::provided_channel_type_features(&config)));
- let commitment_conf_target = if channel_type.supports_anchors_zero_fee_htlc_tx() {
- ConfirmationTarget::MempoolMinimum
+ let (commitment_conf_target, anchor_outputs_value_msat) = if channel_type.supports_anchors_zero_fee_htlc_tx() {
+ (ConfirmationTarget::AnchorChannelFee, ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000)
} else {
- ConfirmationTarget::Normal
+ (ConfirmationTarget::NonAnchorChannelFee, 0)
};
let commitment_feerate = fee_estimator.bounded_sat_per_1000_weight(commitment_conf_target);
let value_to_self_msat = channel_value_satoshis * 1000 - push_msat;
let commitment_tx_fee = commit_tx_fee_msat(commitment_feerate, MIN_AFFORDABLE_HTLC_COUNT, &channel_type);
- if value_to_self_msat < commitment_tx_fee {
+ if value_to_self_msat.saturating_sub(anchor_outputs_value_msat) < commitment_tx_fee {
return Err(APIError::APIMisuseError{ err: format!("Funding amount ({}) can't even pay fee for initial commitment transaction fee of {}.", value_to_self_msat / 1000, commitment_tx_fee / 1000) });
}
/// not of our ability to open any channel at all. Thus, on error, we should first call this
/// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
pub(crate) fn maybe_handle_error_without_close<F: Deref>(
- &mut self, chain_hash: BlockHash, fee_estimator: &LowerBoundedFeeEstimator<F>
+ &mut self, chain_hash: ChainHash, fee_estimator: &LowerBoundedFeeEstimator<F>
) -> Result<msgs::OpenChannel, ()>
where
F::Target: FeeEstimator
// whatever reason.
if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() {
self.context.channel_type.clear_anchors_zero_fee_htlc_tx();
- self.context.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
+ self.context.feerate_per_kw = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
assert!(!self.context.channel_transaction_parameters.channel_type_features.supports_anchors_nonzero_fee_htlc_tx());
} else if self.context.channel_type.supports_scid_privacy() {
self.context.channel_type.clear_scid_privacy();
Ok(self.get_open_channel(chain_hash))
}
- pub fn get_open_channel(&self, chain_hash: BlockHash) -> msgs::OpenChannel {
+ pub fn get_open_channel(&self, chain_hash: ChainHash) -> msgs::OpenChannel {
if !self.context.is_outbound() {
panic!("Tried to open a channel for an inbound channel?");
}
// check if the funder's amount for the initial commitment tx is sufficient
// for full fee payment plus a few HTLCs to ensure the channel will be useful.
+ let anchor_outputs_value = if channel_type.supports_anchors_zero_fee_htlc_tx() {
+ ANCHOR_OUTPUT_VALUE_SATOSHI * 2
+ } else {
+ 0
+ };
let funders_amount_msat = msg.funding_satoshis * 1000 - msg.push_msat;
let commitment_tx_fee = commit_tx_fee_msat(msg.feerate_per_kw, MIN_AFFORDABLE_HTLC_COUNT, &channel_type) / 1000;
- if funders_amount_msat / 1000 < commitment_tx_fee {
- return Err(ChannelError::Close(format!("Funding amount ({} sats) can't even pay fee for initial commitment transaction fee of {} sats.", funders_amount_msat / 1000, commitment_tx_fee)));
+ if (funders_amount_msat / 1000).saturating_sub(anchor_outputs_value) < commitment_tx_fee {
+ return Err(ChannelError::Close(format!("Funding amount ({} sats) can't even pay fee for initial commitment transaction fee of {} sats.", (funders_amount_msat / 1000).saturating_sub(anchor_outputs_value), commitment_tx_fee)));
}
- let to_remote_satoshis = funders_amount_msat / 1000 - commitment_tx_fee;
+ let to_remote_satoshis = funders_amount_msat / 1000 - commitment_tx_fee - anchor_outputs_value;
// While it's reasonable for us to not meet the channel reserve initially (if they don't
// want to push much to us), our counterparty should always have more than our reserve.
if to_remote_satoshis < holder_selected_channel_reserve_satoshis {
#[cfg(test)]
mod tests {
use std::cmp;
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::{Script, Builder};
use bitcoin::blockdata::transaction::{Transaction, TxOut};
- use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::opcodes;
use bitcoin::network::constants::Network;
use hex;
// Now change the fee so we can check that the fee in the open_channel message is the
// same as the old fee.
fee_est.fee_est = 500;
- let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ let open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network));
assert_eq!(open_channel_msg.feerate_per_kw, original_fee);
}
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
- let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ let open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network));
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
let mut node_b_chan = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
let seed = [42; 32];
let network = Network::Testnet;
let best_block = BestBlock::from_network(network);
- let chain_hash = best_block.block_hash();
+ let chain_hash = ChainHash::using_genesis_block(network);
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
// Go through the flow of opening a channel between two nodes.
let chan_2_value_msat = chan_2.context.channel_value_satoshis * 1000;
assert_eq!(chan_2.context.holder_max_htlc_value_in_flight_msat, (chan_2_value_msat as f64 * 0.99) as u64);
- let chan_1_open_channel_msg = chan_1.get_open_channel(genesis_block(network).header.block_hash());
+ let chan_1_open_channel_msg = chan_1.get_open_channel(ChainHash::using_genesis_block(network));
// Test that `InboundV1Channel::new` creates a channel with the correct value for
// `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
let expected_outbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.context.channel_value_satoshis as f64 * outbound_selected_channel_reserve_perc) as u64);
assert_eq!(chan.context.holder_selected_channel_reserve_satoshis, expected_outbound_selected_chan_reserve);
- let chan_open_channel_msg = chan.get_open_channel(genesis_block(network).header.block_hash());
+ let chan_open_channel_msg = chan.get_open_channel(ChainHash::using_genesis_block(network));
let mut inbound_node_config = UserConfig::default();
inbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (inbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
let seed = [42; 32];
let network = Network::Testnet;
let best_block = BestBlock::from_network(network);
- let chain_hash = genesis_block(network).header.block_hash();
+ let chain_hash = ChainHash::using_genesis_block(network);
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
// Create Node A's channel pointing to Node B's pubkey
// Create Node B's channel by receiving Node A's open_channel message
// Make sure A's dust limit is as we expect.
- let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ let open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network));
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
let mut node_b_chan = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider, node_b_node_id, &channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config), &open_channel_msg, 7, &config, 0, &&logger, /*is_0conf=*/false).unwrap();
let mut channel_type_features = ChannelTypeFeatures::only_static_remote_key();
channel_type_features.set_zero_conf_required();
- let mut open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ let mut open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network));
open_channel_msg.channel_type = Some(channel_type_features);
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
let res = InboundV1Channel::<&TestKeysInterface>::new(&feeest, &&keys_provider, &&keys_provider,
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
- let open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network));
let channel_b = InboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
&channelmanager::provided_channel_type_features(&config), &channelmanager::provided_init_features(&config),
).unwrap();
// Set `channel_type` to `None` to force the implicit feature negotiation.
- let mut open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network));
open_channel_msg.channel_type = None;
// Since A supports both `static_remote_key` and `option_anchors`, but B only accepts
&channelmanager::provided_init_features(&config), 10000000, 100000, 42, &config, 0, 42
).unwrap();
- let mut open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ let mut open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network));
open_channel_msg.channel_type = Some(simple_anchors_channel_type.clone());
let res = InboundV1Channel::<&TestKeysInterface>::new(
10000000, 100000, 42, &config, 0, 42
).unwrap();
- let open_channel_msg = channel_a.get_open_channel(genesis_block(network).header.block_hash());
+ let open_channel_msg = channel_a.get_open_channel(ChainHash::using_genesis_block(network));
let channel_b = InboundV1Channel::<&TestKeysInterface>::new(
&fee_estimator, &&keys_provider, &&keys_provider, node_id_a,
let seed = [42; 32];
let network = Network::Testnet;
let best_block = BestBlock::from_network(network);
- let chain_hash = genesis_block(network).header.block_hash();
+ let chain_hash = ChainHash::using_genesis_block(network);
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
let mut config = UserConfig::default();
42,
).unwrap();
- let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash());
+ let open_channel_msg = node_a_chan.get_open_channel(ChainHash::using_genesis_block(network));
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
let mut node_b_chan = InboundV1Channel::<&TestKeysInterface>::new(
&feeest,
use bitcoin::blockdata::block::BlockHeader;
use bitcoin::blockdata::transaction::Transaction;
-use bitcoin::blockdata::constants::{genesis_block, ChainHash};
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{LockTime, secp256k1, Sequence};
+use crate::blinded_path::BlindedPath;
+use crate::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
use crate::chain;
use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
// construct one themselves.
use crate::ln::{inbound_payment, ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use crate::ln::channel::{Channel, ChannelPhase, ChannelContext, ChannelError, ChannelUpdateStatus, ShutdownResult, UnfundedChannelContext, UpdateFulfillCommitFetch, OutboundV1Channel, InboundV1Channel};
-use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
+use crate::ln::features::{Bolt12InvoiceFeatures, ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
#[cfg(any(feature = "_test_utils", test))]
use crate::ln::features::Bolt11InvoiceFeatures;
use crate::routing::gossip::NetworkGraph;
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
#[cfg(test)]
use crate::ln::outbound_payment;
-use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs};
+use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
use crate::ln::wire::Encode;
+use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder};
+use crate::offers::invoice_error::InvoiceError;
+use crate::offers::merkle::SignError;
+use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
+use crate::offers::parse::Bolt12SemanticError;
+use crate::offers::refund::{Refund, RefundBuilder};
+use crate::onion_message::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage};
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
use crate::util::wakers::{Future, Notifier};
}
#[inline]
fn from_finish_shutdown(err: String, channel_id: ChannelId, user_channel_id: u128, shutdown_res: ShutdownResult, channel_update: Option<msgs::ChannelUpdate>, channel_capacity: u64) -> Self {
+ let err_msg = msgs::ErrorMessage { channel_id, data: err.clone() };
+ let action = if let (Some(_), ..) = &shutdown_res {
+ // We have a closing `ChannelMonitorUpdate`, which means the channel was funded and we
+ // should disconnect our peer such that we force them to broadcast their latest
+ // commitment upon reconnecting.
+ msgs::ErrorAction::DisconnectPeer { msg: Some(err_msg) }
+ } else {
+ msgs::ErrorAction::SendErrorMessage { msg: err_msg }
+ };
Self {
- err: LightningError {
- err: err.clone(),
- action: msgs::ErrorAction::SendErrorMessage {
- msg: msgs::ErrorMessage {
- channel_id,
- data: err
- },
- },
- },
+ err: LightningError { err, action },
chan_id: Some((channel_id, user_channel_id)),
shutdown_finish: Some((shutdown_res, channel_update)),
channel_capacity: Some(channel_capacity)
/// usually because we're running pre-full-init. They are handled immediately once we detect we are
/// running normally, and specifically must be processed before any other non-background
/// [`ChannelMonitorUpdate`]s are applied.
+#[derive(Debug)]
enum BackgroundEvent {
/// Handle a ChannelMonitorUpdate which closes the channel or for an already-closed channel.
/// This is only separated from [`Self::MonitorUpdateRegeneratedOnStartup`] as the
event: events::Event,
downstream_counterparty_and_funding_outpoint: Option<(PublicKey, OutPoint, RAAMonitorUpdateBlockingAction)>,
},
+ /// Indicates we should immediately resume the operation of another channel, unless there is
+ /// some other reason why the channel is blocked. In practice this simply means immediately
+ /// removing the [`RAAMonitorUpdateBlockingAction`] provided from the blocking set.
+ ///
+ /// This is usually generated when we've forwarded an HTLC and want to block the outbound edge
+ /// from completing a monitor update which removes the payment preimage until the inbound edge
+ /// completes a monitor update containing the payment preimage. However, we use this variant
+ /// instead of [`Self::EmitEventAndFreeOtherChannel`] when we discover that the claim was in
+ /// fact duplicative and we simply want to resume the outbound edge channel immediately.
+ ///
+ /// This variant should thus never be written to disk, as it is processed inline rather than
+ /// stored for later processing.
+ FreeOtherChannelImmediately {
+ downstream_counterparty_node_id: PublicKey,
+ downstream_funding_outpoint: OutPoint,
+ blocking_action: RAAMonitorUpdateBlockingAction,
+ },
}
impl_writeable_tlv_based_enum_upgradable!(MonitorUpdateCompletionAction,
(0, PaymentClaimed) => { (0, payment_hash, required) },
+ // Note that FreeOtherChannelImmediately should never be written - we were supposed to free
+ // *immediately*. However, for simplicity we implement read/write here.
+ (1, FreeOtherChannelImmediately) => {
+ (0, downstream_counterparty_node_id, required),
+ (2, downstream_funding_outpoint, required),
+ (4, blocking_action, required),
+ },
(2, EmitEventAndFreeOtherChannel) => {
(0, event, upgradable_required),
// LDK prior to 0.0.116 did not have this field as the monitor update application order was
//
// Lock order tree:
//
+// `pending_offers_messages`
+//
// `total_consistency_lock`
// |
// |__`forward_htlcs`
// | |__`pending_intercepted_htlcs`
// |
// |__`per_peer_state`
-// | |
-// | |__`pending_inbound_payments`
-// | |
-// | |__`claimable_payments`
-// | |
-// | |__`pending_outbound_payments` // This field's struct contains a map of pending outbounds
-// | |
-// | |__`peer_state`
-// | |
-// | |__`id_to_peer`
-// | |
-// | |__`short_to_chan_info`
-// | |
-// | |__`outbound_scid_aliases`
-// | |
-// | |__`best_block`
-// | |
-// | |__`pending_events`
-// | |
-// | |__`pending_background_events`
+// |
+// |__`pending_inbound_payments`
+// |
+// |__`claimable_payments`
+// |
+// |__`pending_outbound_payments` // This field's struct contains a map of pending outbounds
+// |
+// |__`peer_state`
+// |
+// |__`id_to_peer`
+// |
+// |__`short_to_chan_info`
+// |
+// |__`outbound_scid_aliases`
+// |
+// |__`best_block`
+// |
+// |__`pending_events`
+// |
+// |__`pending_background_events`
//
pub struct ChannelManager<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
where
L::Target: Logger,
{
default_configuration: UserConfig,
- genesis_hash: BlockHash,
+ chain_hash: ChainHash,
fee_estimator: LowerBoundedFeeEstimator<F>,
chain_monitor: M,
tx_broadcaster: T,
event_persist_notifier: Notifier,
needs_persist_flag: AtomicBool,
+ pending_offers_messages: Mutex<Vec<PendingOnionMessage<OffersMessage>>>,
+
entropy_source: ES,
node_signer: NS,
signer_provider: SP,
macro_rules! handle_monitor_update_completion {
($self: ident, $peer_state_lock: expr, $peer_state: expr, $per_peer_state_lock: expr, $chan: expr) => { {
let mut updates = $chan.monitor_updating_restored(&$self.logger,
- &$self.node_signer, $self.genesis_hash, &$self.default_configuration,
+ &$self.node_signer, $self.chain_hash, &$self.default_configuration,
$self.best_block.read().unwrap().height());
let counterparty_node_id = $chan.context.get_counterparty_node_id();
let channel_update = if updates.channel_ready.is_some() && $chan.context.is_usable() {
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
ChannelManager {
default_configuration: config.clone(),
- genesis_hash: genesis_block(params.network).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(params.network),
fee_estimator: LowerBoundedFeeEstimator::new(fee_est),
chain_monitor,
tx_broadcaster,
needs_persist_flag: AtomicBool::new(false),
funding_batch_states: Mutex::new(BTreeMap::new()),
+ pending_offers_messages: Mutex::new(Vec::new()),
+
entropy_source,
node_signer,
signer_provider,
if cfg!(fuzzing) { // fuzzing chacha20 doesn't use the key at all so we always get the same alias
outbound_scid_alias += 1;
} else {
- outbound_scid_alias = fake_scid::Namespace::OutboundAlias.get_fake_scid(height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
+ outbound_scid_alias = fake_scid::Namespace::OutboundAlias.get_fake_scid(height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
}
if outbound_scid_alias != 0 && self.outbound_scid_aliases.lock().unwrap().insert(outbound_scid_alias) {
break;
},
}
};
- let res = channel.get_open_channel(self.genesis_hash.clone());
+ let res = channel.get_open_channel(self.chain_hash);
let temporary_channel_id = channel.context.channel_id();
match peer_state.channel_by_id.entry(temporary_channel_id) {
/// will be accepted on the given channel, and after additional timeout/the closing of all
/// pending HTLCs, the channel will be closed on chain.
///
- /// * If we are the channel initiator, we will pay between our [`Background`] and
- /// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`] plus our [`Normal`] fee
- /// estimate.
+ /// * If we are the channel initiator, we will pay between our [`ChannelCloseMinimum`] and
+ /// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`] plus our [`NonAnchorChannelFee`]
+ /// fee estimate.
/// * If our counterparty is the channel initiator, we will require a channel closing
- /// transaction feerate of at least our [`Background`] feerate or the feerate which
+ /// transaction feerate of at least our [`ChannelCloseMinimum`] feerate or the feerate which
/// would appear on a force-closure transaction, whichever is lower. We will allow our
/// counterparty to pay as much fee as they'd like, however.
///
/// channel.
///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
- /// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
- /// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
+ /// [`ChannelCloseMinimum`]: crate::chain::chaininterface::ConfirmationTarget::ChannelCloseMinimum
+ /// [`NonAnchorChannelFee`]: crate::chain::chaininterface::ConfirmationTarget::NonAnchorChannelFee
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
pub fn close_channel(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey) -> Result<(), APIError> {
self.close_channel_internal(channel_id, counterparty_node_id, None, None)
/// the channel being closed or not:
/// * If we are the channel initiator, we will pay at least this feerate on the closing
/// transaction. The upper-bound is set by
- /// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`] plus our [`Normal`] fee
- /// estimate (or `target_feerate_sat_per_1000_weight`, if it is greater).
+ /// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`] plus our [`NonAnchorChannelFee`]
+ /// fee estimate (or `target_feerate_sat_per_1000_weight`, if it is greater).
/// * If our counterparty is the channel initiator, we will refuse to accept a channel closure
/// transaction feerate below `target_feerate_sat_per_1000_weight` (or the feerate which
/// will appear on a force-closure transaction, whichever is lower).
/// channel.
///
/// [`ChannelConfig::force_close_avoidance_max_fee_satoshis`]: crate::util::config::ChannelConfig::force_close_avoidance_max_fee_satoshis
- /// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
- /// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
+ /// [`NonAnchorChannelFee`]: crate::chain::chaininterface::ConfirmationTarget::NonAnchorChannelFee
/// [`SendShutdown`]: crate::events::MessageSendEvent::SendShutdown
pub fn close_channel_with_feerate_and_script(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, target_feerate_sats_per_1000_weight: Option<u32>, shutdown_script: Option<ShutdownScript>) -> Result<(), APIError> {
self.close_channel_internal(channel_id, counterparty_node_id, target_feerate_sats_per_1000_weight, shutdown_script)
peer_state.pending_msg_events.push(
events::MessageSendEvent::HandleError {
node_id: counterparty_node_id,
- action: msgs::ErrorAction::SendErrorMessage {
- msg: msgs::ErrorMessage { channel_id: *channel_id, data: "Channel force-closed".to_owned() }
+ action: msgs::ErrorAction::DisconnectPeer {
+ msg: Some(msgs::ErrorMessage { channel_id: *channel_id, data: "Channel force-closed".to_owned() })
},
}
);
// Note that this is likely a timing oracle for detecting whether an scid is a
// phantom or an intercept.
if (self.default_configuration.accept_intercept_htlcs &&
- fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, outgoing_scid, &self.genesis_hash)) ||
- fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, outgoing_scid, &self.genesis_hash)
+ fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)) ||
+ fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, outgoing_scid, &self.chain_hash)
{
None
} else {
};
let unsigned = msgs::UnsignedChannelUpdate {
- chain_hash: self.genesis_hash,
+ chain_hash: self.chain_hash,
short_channel_id,
timestamp: chan.context.get_update_time_counter(),
flags: (!were_node_one) as u8 | ((!enabled as u8) << 1),
self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata);
}
+ pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
+ let best_block_height = self.best_block.read().unwrap().height();
+ let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+ self.pending_outbound_payments
+ .send_payment_for_bolt12_invoice(
+ invoice, payment_id, &self.router, self.list_usable_channels(),
+ || self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer,
+ best_block_height, &self.logger, &self.pending_events,
+ |args| self.send_payment_along_path(args)
+ )
+ }
/// Signals that no further attempts for the given payment should occur. Useful if you have a
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
/// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
/// determine the ultimate status of a payment.
///
+ /// # Requested Invoices
+ ///
+ /// In the case of paying a [`Bolt12Invoice`] via [`ChannelManager::pay_for_offer`], abandoning
+ /// the payment prior to receiving the invoice will result in an [`Event::InvoiceRequestFailed`]
+ /// and prevent any attempts at paying it once received. The other events may only be generated
+ /// once the invoice has been received.
+ ///
/// # Restart Behavior
///
/// If an [`Event::PaymentFailed`] is generated and we restart without first persisting the
- /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated.
+ /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated; likewise for
+ /// [`Event::InvoiceRequestFailed`].
+ ///
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
pub fn abandon_payment(&self, payment_id: PaymentId) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments.abandon_payment(payment_id, PaymentFailureReason::UserAbandoned, &self.pending_events);
}
if let PendingHTLCRouting::Forward { onion_packet, .. } = routing {
let phantom_pubkey_res = self.node_signer.get_node_id(Recipient::PhantomNode);
- if phantom_pubkey_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id, &self.genesis_hash) {
+ if phantom_pubkey_res.is_ok() && fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, short_chan_id, &self.chain_hash) {
let phantom_shared_secret = self.node_signer.ecdh(Recipient::PhantomNode, &onion_packet.public_key.unwrap(), None).unwrap().secret_bytes();
let next_hop = match onion_utils::decode_next_payment_hop(
phantom_shared_secret, &onion_packet.hop_data, onion_packet.hmac,
}
}
}
- let (counterparty_node_id, forward_chan_id) = match self.short_to_chan_info.read().unwrap().get(&short_chan_id) {
- Some((cp_id, chan_id)) => (cp_id.clone(), chan_id.clone()),
+ let chan_info_opt = self.short_to_chan_info.read().unwrap().get(&short_chan_id).cloned();
+ let (counterparty_node_id, forward_chan_id) = match chan_info_opt {
+ Some((cp_id, chan_id)) => (cp_id, chan_id),
None => {
forwarding_channel_not_found!();
continue;
PersistenceNotifierGuard::optionally_notify(self, || {
let mut should_persist = NotifyOption::SkipPersistNoEvents;
- let normal_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
- let min_mempool_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::MempoolMinimum);
+ let non_anchor_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
+ let anchor_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee);
let per_peer_state = self.per_peer_state.read().unwrap();
for (_cp_id, peer_state_mutex) in per_peer_state.iter() {
|(chan_id, phase)| if let ChannelPhase::Funded(chan) = phase { Some((chan_id, chan)) } else { None }
) {
let new_feerate = if chan.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
- min_mempool_feerate
+ anchor_feerate
} else {
- normal_feerate
+ non_anchor_feerate
};
let chan_needs_persist = self.update_channel_fee(chan_id, chan, new_feerate);
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
/// with the current [`ChannelConfig`].
/// * Removing peers which have disconnected but and no longer have any channels.
/// * Force-closing and removing channels which have not completed establishment in a timely manner.
+ /// * Forgetting about stale outbound payments, either those that have already been fulfilled
+ /// or those awaiting an invoice that hasn't been delivered in the necessary amount of time.
+ /// The latter is determined using the system clock in `std` and the highest seen block time
+ /// minus two hours in `no-std`.
///
/// Note that this may cause reentrancy through [`chain::Watch::update_channel`] calls or feerate
/// estimate fetches.
PersistenceNotifierGuard::optionally_notify(self, || {
let mut should_persist = NotifyOption::SkipPersistNoEvents;
- let normal_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::Normal);
- let min_mempool_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::MempoolMinimum);
+ let non_anchor_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::NonAnchorChannelFee);
+ let anchor_feerate = self.fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::AnchorChannelFee);
let mut handle_errors: Vec<(Result<(), _>, _)> = Vec::new();
let mut timed_out_mpp_htlcs = Vec::new();
match phase {
ChannelPhase::Funded(chan) => {
let new_feerate = if chan.context.get_channel_type().supports_anchors_zero_fee_htlc_tx() {
- min_mempool_feerate
+ anchor_feerate
} else {
- normal_feerate
+ non_anchor_feerate
};
let chan_needs_persist = self.update_channel_fee(chan_id, chan, new_feerate);
if chan_needs_persist == NotifyOption::DoPersist { should_persist = NotifyOption::DoPersist; }
self.finish_close_channel(shutdown_res);
}
- self.pending_outbound_payments.remove_stale_payments(&self.pending_events);
+ #[cfg(feature = "std")]
+ let duration_since_epoch = std::time::SystemTime::now()
+ .duration_since(std::time::SystemTime::UNIX_EPOCH)
+ .expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
+ #[cfg(not(feature = "std"))]
+ let duration_since_epoch = Duration::from_secs(
+ self.highest_seen_timestamp.load(Ordering::Acquire).saturating_sub(7200) as u64
+ );
+
+ self.pending_outbound_payments.remove_stale_payments(
+ duration_since_epoch, &self.pending_events
+ );
// Technically we don't need to do this here, but if we have holding cell entries in a
// channel that need freeing, it's better to do that here and block a background task
for htlc in sources.drain(..) {
if let Err((pk, err)) = self.claim_funds_from_hop(
htlc.prev_hop, payment_preimage,
- |_| Some(MonitorUpdateCompletionAction::PaymentClaimed { payment_hash }))
- {
+ |_, definitely_duplicate| {
+ debug_assert!(!definitely_duplicate, "We shouldn't claim duplicatively from a payment");
+ Some(MonitorUpdateCompletionAction::PaymentClaimed { payment_hash })
+ }
+ ) {
if let msgs::ErrorAction::IgnoreError = err.err.action {
// We got a temporary failure updating monitor, but will claim the
// HTLC when the monitor updating is restored (or on chain).
}
}
- fn claim_funds_from_hop<ComplFunc: FnOnce(Option<u64>) -> Option<MonitorUpdateCompletionAction>>(&self,
+ fn claim_funds_from_hop<ComplFunc: FnOnce(Option<u64>, bool) -> Option<MonitorUpdateCompletionAction>>(&self,
prev_hop: HTLCPreviousHopData, payment_preimage: PaymentPreimage, completion_action: ComplFunc)
-> Result<(), (PublicKey, MsgHandleErrInternal)> {
//TODO: Delay the claimed_funds relaying just like we do outbound relay!
// `BackgroundEvent`s.
let during_init = !self.background_events_processed_since_startup.load(Ordering::Acquire);
+ // As we may call handle_monitor_update_completion_actions in rather rare cases, check that
+ // the required mutexes are not held before we start.
+ debug_assert_ne!(self.pending_events.held_by_thread(), LockHeldState::HeldByThread);
+ debug_assert_ne!(self.claimable_payments.held_by_thread(), LockHeldState::HeldByThread);
+
{
let per_peer_state = self.per_peer_state.read().unwrap();
let chan_id = prev_hop.outpoint.to_channel_id();
let counterparty_node_id = chan.context.get_counterparty_node_id();
let fulfill_res = chan.get_update_fulfill_htlc_and_commit(prev_hop.htlc_id, payment_preimage, &self.logger);
- if let UpdateFulfillCommitFetch::NewClaim { htlc_value_msat, monitor_update } = fulfill_res {
- if let Some(action) = completion_action(Some(htlc_value_msat)) {
- log_trace!(self.logger, "Tracking monitor update completion action for channel {}: {:?}",
- chan_id, action);
- peer_state.monitor_update_blocked_actions.entry(chan_id).or_insert(Vec::new()).push(action);
+ match fulfill_res {
+ UpdateFulfillCommitFetch::NewClaim { htlc_value_msat, monitor_update } => {
+ if let Some(action) = completion_action(Some(htlc_value_msat), false) {
+ log_trace!(self.logger, "Tracking monitor update completion action for channel {}: {:?}",
+ chan_id, action);
+ peer_state.monitor_update_blocked_actions.entry(chan_id).or_insert(Vec::new()).push(action);
+ }
+ if !during_init {
+ handle_new_monitor_update!(self, prev_hop.outpoint, monitor_update, peer_state_lock,
+ peer_state, per_peer_state, chan);
+ } else {
+ // If we're running during init we cannot update a monitor directly -
+ // they probably haven't actually been loaded yet. Instead, push the
+ // monitor update as a background event.
+ self.pending_background_events.lock().unwrap().push(
+ BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
+ counterparty_node_id,
+ funding_txo: prev_hop.outpoint,
+ update: monitor_update.clone(),
+ });
+ }
}
- if !during_init {
- handle_new_monitor_update!(self, prev_hop.outpoint, monitor_update, peer_state_lock,
- peer_state, per_peer_state, chan);
- } else {
- // If we're running during init we cannot update a monitor directly -
- // they probably haven't actually been loaded yet. Instead, push the
- // monitor update as a background event.
- self.pending_background_events.lock().unwrap().push(
- BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
- counterparty_node_id,
- funding_txo: prev_hop.outpoint,
- update: monitor_update.clone(),
- });
+ UpdateFulfillCommitFetch::DuplicateClaim {} => {
+ let action = if let Some(action) = completion_action(None, true) {
+ action
+ } else {
+ return Ok(());
+ };
+ mem::drop(peer_state_lock);
+
+ log_trace!(self.logger, "Completing monitor update completion action for channel {} as claim was redundant: {:?}",
+ chan_id, action);
+ let (node_id, funding_outpoint, blocker) =
+ if let MonitorUpdateCompletionAction::FreeOtherChannelImmediately {
+ downstream_counterparty_node_id: node_id,
+ downstream_funding_outpoint: funding_outpoint,
+ blocking_action: blocker,
+ } = action {
+ (node_id, funding_outpoint, blocker)
+ } else {
+ debug_assert!(false,
+ "Duplicate claims should always free another channel immediately");
+ return Ok(());
+ };
+ if let Some(peer_state_mtx) = per_peer_state.get(&node_id) {
+ let mut peer_state = peer_state_mtx.lock().unwrap();
+ if let Some(blockers) = peer_state
+ .actions_blocking_raa_monitor_updates
+ .get_mut(&funding_outpoint.to_channel_id())
+ {
+ let mut found_blocker = false;
+ blockers.retain(|iter| {
+ // Note that we could actually be blocked, in
+ // which case we need to only remove the one
+ // blocker which was added duplicatively.
+ let first_blocker = !found_blocker;
+ if *iter == blocker { found_blocker = true; }
+ *iter != blocker || !first_blocker
+ });
+ debug_assert!(found_blocker);
+ }
+ } else {
+ debug_assert!(false);
+ }
}
}
}
// `ChannelMonitor` we've provided the above update to. Instead, note that `Event`s are
// generally always allowed to be duplicative (and it's specifically noted in
// `PaymentForwarded`).
- self.handle_monitor_update_completion_actions(completion_action(None));
+ self.handle_monitor_update_completion_actions(completion_action(None, false));
Ok(())
}
}
fn claim_funds_internal(&self, source: HTLCSource, payment_preimage: PaymentPreimage,
- forwarded_htlc_value_msat: Option<u64>, from_onchain: bool,
+ forwarded_htlc_value_msat: Option<u64>, from_onchain: bool, startup_replay: bool,
next_channel_counterparty_node_id: Option<PublicKey>, next_channel_outpoint: OutPoint
) {
match source {
HTLCSource::PreviousHopData(hop_data) => {
let prev_outpoint = hop_data.outpoint;
let completed_blocker = RAAMonitorUpdateBlockingAction::from_prev_hop_data(&hop_data);
+ #[cfg(debug_assertions)]
+ let claiming_chan_funding_outpoint = hop_data.outpoint;
let res = self.claim_funds_from_hop(hop_data, payment_preimage,
- |htlc_claim_value_msat| {
- if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
- let fee_earned_msat = if let Some(claimed_htlc_value) = htlc_claim_value_msat {
- Some(claimed_htlc_value - forwarded_htlc_value)
- } else { None };
+ |htlc_claim_value_msat, definitely_duplicate| {
+ let chan_to_release =
+ if let Some(node_id) = next_channel_counterparty_node_id {
+ Some((node_id, next_channel_outpoint, completed_blocker))
+ } else {
+ // We can only get `None` here if we are processing a
+ // `ChannelMonitor`-originated event, in which case we
+ // don't care about ensuring we wake the downstream
+ // channel's monitor updating - the channel is already
+ // closed.
+ None
+ };
+ if definitely_duplicate && startup_replay {
+ // On startup we may get redundant claims which are related to
+ // monitor updates still in flight. In that case, we shouldn't
+ // immediately free, but instead let that monitor update complete
+ // in the background.
+ #[cfg(debug_assertions)] {
+ let background_events = self.pending_background_events.lock().unwrap();
+ // There should be a `BackgroundEvent` pending...
+ assert!(background_events.iter().any(|ev| {
+ match ev {
+ // to apply a monitor update that blocked the claiming channel,
+ BackgroundEvent::MonitorUpdateRegeneratedOnStartup {
+ funding_txo, update, ..
+ } => {
+ if *funding_txo == claiming_chan_funding_outpoint {
+ assert!(update.updates.iter().any(|upd|
+ if let ChannelMonitorUpdateStep::PaymentPreimage {
+ payment_preimage: update_preimage
+ } = upd {
+ payment_preimage == *update_preimage
+ } else { false }
+ ), "{:?}", update);
+ true
+ } else { false }
+ },
+ // or the channel we'd unblock is already closed,
+ BackgroundEvent::ClosedMonitorUpdateRegeneratedOnStartup(
+ (funding_txo, monitor_update)
+ ) => {
+ if *funding_txo == next_channel_outpoint {
+ assert_eq!(monitor_update.updates.len(), 1);
+ assert!(matches!(
+ monitor_update.updates[0],
+ ChannelMonitorUpdateStep::ChannelForceClosed { .. }
+ ));
+ true
+ } else { false }
+ },
+ // or the monitor update has completed and will unblock
+ // immediately once we get going.
+ BackgroundEvent::MonitorUpdatesComplete {
+ channel_id, ..
+ } =>
+ *channel_id == claiming_chan_funding_outpoint.to_channel_id(),
+ }
+ }), "{:?}", *background_events);
+ }
+ None
+ } else if definitely_duplicate {
+ if let Some(other_chan) = chan_to_release {
+ Some(MonitorUpdateCompletionAction::FreeOtherChannelImmediately {
+ downstream_counterparty_node_id: other_chan.0,
+ downstream_funding_outpoint: other_chan.1,
+ blocking_action: other_chan.2,
+ })
+ } else { None }
+ } else {
+ let fee_earned_msat = if let Some(forwarded_htlc_value) = forwarded_htlc_value_msat {
+ if let Some(claimed_htlc_value) = htlc_claim_value_msat {
+ Some(claimed_htlc_value - forwarded_htlc_value)
+ } else { None }
+ } else { None };
Some(MonitorUpdateCompletionAction::EmitEventAndFreeOtherChannel {
event: events::Event::PaymentForwarded {
fee_earned_msat,
next_channel_id: Some(next_channel_outpoint.to_channel_id()),
outbound_amount_forwarded_msat: forwarded_htlc_value_msat,
},
- downstream_counterparty_and_funding_outpoint:
- if let Some(node_id) = next_channel_counterparty_node_id {
- Some((node_id, next_channel_outpoint, completed_blocker))
- } else {
- // We can only get `None` here if we are processing a
- // `ChannelMonitor`-originated event, in which case we
- // don't care about ensuring we wake the downstream
- // channel's monitor updating - the channel is already
- // closed.
- None
- },
+ downstream_counterparty_and_funding_outpoint: chan_to_release,
})
- } else { None }
+ }
});
if let Err((pk, err)) = res {
let result: Result<(), _> = Err(err);
}
fn handle_monitor_update_completion_actions<I: IntoIterator<Item=MonitorUpdateCompletionAction>>(&self, actions: I) {
+ debug_assert_ne!(self.pending_events.held_by_thread(), LockHeldState::HeldByThread);
+ debug_assert_ne!(self.claimable_payments.held_by_thread(), LockHeldState::HeldByThread);
+ debug_assert_ne!(self.per_peer_state.held_by_thread(), LockHeldState::HeldByThread);
+
for action in actions.into_iter() {
match action {
MonitorUpdateCompletionAction::PaymentClaimed { payment_hash } => {
self.handle_monitor_update_release(node_id, funding_outpoint, Some(blocker));
}
},
+ MonitorUpdateCompletionAction::FreeOtherChannelImmediately {
+ downstream_counterparty_node_id, downstream_funding_outpoint, blocking_action,
+ } => {
+ self.handle_monitor_update_release(
+ downstream_counterparty_node_id,
+ downstream_funding_outpoint,
+ Some(blocking_action),
+ );
+ },
}
}
}
fn internal_open_channel(&self, counterparty_node_id: &PublicKey, msg: &msgs::OpenChannel) -> Result<(), MsgHandleErrInternal> {
// Note that the ChannelManager is NOT re-persisted on disk after this, so any changes are
// likely to be lost on restart!
- if msg.chain_hash != self.genesis_hash {
+ if msg.chain_hash != self.chain_hash {
return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash".to_owned(), msg.temporary_channel_id.clone()));
}
hash_map::Entry::Occupied(mut chan_phase_entry) => {
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
let announcement_sigs_opt = try_chan_phase_entry!(self, chan.channel_ready(&msg, &self.node_signer,
- self.genesis_hash.clone(), &self.default_configuration, &self.best_block.read().unwrap(), &self.logger), chan_phase_entry);
+ self.chain_hash, &self.default_configuration, &self.best_block.read().unwrap(), &self.logger), chan_phase_entry);
if let Some(announcement_sigs) = announcement_sigs_opt {
log_trace!(self.logger, "Sending announcement_signatures for channel {}", chan.context.channel_id());
peer_state.pending_msg_events.push(events::MessageSendEvent::SendAnnouncementSignatures {
if let ChannelPhase::Funded(chan) = chan_phase_entry.get_mut() {
let res = try_chan_phase_entry!(self, chan.update_fulfill_htlc(&msg), chan_phase_entry);
if let HTLCSource::PreviousHopData(prev_hop) = &res.0 {
+ log_trace!(self.logger,
+ "Holding the next revoke_and_ack from {} until the preimage is durably persisted in the inbound edge's ChannelMonitor",
+ msg.channel_id);
peer_state.actions_blocking_raa_monitor_updates.entry(msg.channel_id)
.or_insert_with(Vec::new)
.push(RAAMonitorUpdateBlockingAction::from_prev_hop_data(&prev_hop));
hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
}
};
- self.claim_funds_internal(htlc_source, msg.payment_preimage.clone(), Some(forwarded_htlc_value), false, Some(*counterparty_node_id), funding_txo);
+ self.claim_funds_internal(htlc_source, msg.payment_preimage.clone(), Some(forwarded_htlc_value), false, false, Some(*counterparty_node_id), funding_txo);
Ok(())
}
},
hash_map::Entry::Vacant(entry) => {
if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
- fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
+ fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.chain_hash)
{
let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
peer_state.pending_msg_events.push(events::MessageSendEvent::BroadcastChannelAnnouncement {
msg: try_chan_phase_entry!(self, chan.announcement_signatures(
- &self.node_signer, self.genesis_hash.clone(), self.best_block.read().unwrap().height(),
+ &self.node_signer, self.chain_hash, self.best_block.read().unwrap().height(),
msg, &self.default_configuration
), chan_phase_entry),
// Note that announcement_signatures fails if the channel cannot be announced,
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
.ok_or_else(|| {
debug_assert!(false);
- MsgHandleErrInternal::send_err_msg_no_close(format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id), msg.channel_id)
+ MsgHandleErrInternal::send_err_msg_no_close(
+ format!("Can't find a peer matching the passed counterparty node_id {}", counterparty_node_id),
+ msg.channel_id
+ )
})?;
let mut peer_state_lock = peer_state_mutex.lock().unwrap();
let peer_state = &mut *peer_state_lock;
// freed HTLCs to fail backwards. If in the future we no longer drop pending
// add-HTLCs on disconnect, we may be handed HTLCs to fail backwards here.
let responses = try_chan_phase_entry!(self, chan.channel_reestablish(
- msg, &self.logger, &self.node_signer, self.genesis_hash,
+ msg, &self.logger, &self.node_signer, self.chain_hash,
&self.default_configuration, &*self.best_block.read().unwrap()), chan_phase_entry);
let mut channel_update = None;
if let Some(msg) = responses.shutdown_msg {
"Got a channel_reestablish message for an unfunded channel!".into())), chan_phase_entry);
}
},
- hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", counterparty_node_id), msg.channel_id))
+ hash_map::Entry::Vacant(_) => {
+ log_debug!(self.logger, "Sending bogus ChannelReestablish for unknown channel {} to force channel closure",
+ log_bytes!(msg.channel_id.0));
+ // Unfortunately, lnd doesn't force close on errors
+ // (https://github.com/lightningnetwork/lnd/blob/abb1e3463f3a83bbb843d5c399869dbe930ad94f/htlcswitch/link.go#L2119).
+ // One of the few ways to get an lnd counterparty to force close is by
+ // replicating what they do when restoring static channel backups (SCBs). They
+ // send an invalid `ChannelReestablish` with `0` commitment numbers and an
+ // invalid `your_last_per_commitment_secret`.
+ //
+ // Since we received a `ChannelReestablish` for a channel that doesn't exist, we
+ // can assume it's likely the channel closed from our point of view, but it
+ // remains open on the counterparty's side. By sending this bogus
+ // `ChannelReestablish` message now as a response to theirs, we trigger them to
+ // force close broadcasting their latest state. If the closing transaction from
+ // our point of view remains unconfirmed, it'll enter a race with the
+ // counterparty's to-be-broadcast latest commitment transaction.
+ peer_state.pending_msg_events.push(MessageSendEvent::SendChannelReestablish {
+ node_id: *counterparty_node_id,
+ msg: msgs::ChannelReestablish {
+ channel_id: msg.channel_id,
+ next_local_commitment_number: 0,
+ next_remote_commitment_number: 0,
+ your_last_per_commitment_secret: [1u8; 32],
+ my_current_per_commitment_point: PublicKey::from_slice(&[2u8; 33]).unwrap(),
+ next_funding_txid: None,
+ },
+ });
+ return Err(MsgHandleErrInternal::send_err_msg_no_close(
+ format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}",
+ counterparty_node_id), msg.channel_id)
+ )
+ }
}
};
MonitorEvent::HTLCEvent(htlc_update) => {
if let Some(preimage) = htlc_update.payment_preimage {
log_trace!(self.logger, "Claiming HTLC with preimage {} from our monitor", preimage);
- self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, counterparty_node_id, funding_outpoint);
+ self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, false, counterparty_node_id, funding_outpoint);
} else {
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", &htlc_update.payment_hash);
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
self.issue_channel_close_events(&chan.context, ClosureReason::HolderForceClosed);
pending_msg_events.push(events::MessageSendEvent::HandleError {
node_id: chan.context.get_counterparty_node_id(),
- action: msgs::ErrorAction::SendErrorMessage {
- msg: msgs::ErrorMessage { channel_id: chan.context.channel_id(), data: "Channel force-closed".to_owned() }
+ action: msgs::ErrorAction::DisconnectPeer {
+ msg: Some(msgs::ErrorMessage { channel_id: chan.context.channel_id(), data: "Channel force-closed".to_owned() })
},
});
}
}
}
+ /// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the
+ /// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
+ /// not have an expiration unless otherwise set on the builder.
+ ///
+ /// # Privacy
+ ///
+ /// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
+ /// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
+ /// the node must be announced. Otherwise, there is no way to find a path to the introduction
+ /// node in order to send the [`InvoiceRequest`].
+ ///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to the introduction node in the responding [`InvoiceRequest`]'s
+ /// reply path.
+ ///
+ /// [`Offer`]: crate::offers::offer::Offer
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ pub fn create_offer_builder(
+ &self, description: String
+ ) -> OfferBuilder<DerivedMetadata, secp256k1::All> {
+ let node_id = self.get_our_node_id();
+ let expanded_key = &self.inbound_payment_key;
+ let entropy = &*self.entropy_source;
+ let secp_ctx = &self.secp_ctx;
+ let path = self.create_one_hop_blinded_path();
+
+ OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx)
+ .chain_hash(self.chain_hash)
+ .path(path)
+ }
+
+ /// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
+ /// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund.
+ ///
+ /// # Payment
+ ///
+ /// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
+ /// See [Avoiding Duplicate Payments] for other requirements once the payment has been sent.
+ ///
+ /// The builder will have the provided expiration set. Any changes to the expiration on the
+ /// returned builder will not be honored by [`ChannelManager`]. For `no-std`, the highest seen
+ /// block time minus two hours is used for the current time when determining if the refund has
+ /// expired.
+ ///
+ /// To revoke the refund, use [`ChannelManager::abandon_payment`] prior to receiving the
+ /// invoice. If abandoned, or an invoice isn't received before expiration, the payment will fail
+ /// with an [`Event::InvoiceRequestFailed`].
+ ///
+ /// # Privacy
+ ///
+ /// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as
+ /// the introduction node and a derived payer id for payer privacy. As such, currently, the
+ /// node must be announced. Otherwise, there is no way to find a path to the introduction node
+ /// in order to send the [`Bolt12Invoice`].
+ ///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to an introduction node in the responding
+ /// [`Bolt12Invoice::payment_paths`].
+ ///
+ /// # Errors
+ ///
+ /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
+ /// or if `amount_msats` is invalid.
+ ///
+ /// [`Refund`]: crate::offers::refund::Refund
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
+ pub fn create_refund_builder(
+ &self, description: String, amount_msats: u64, absolute_expiry: Duration,
+ payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
+ ) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError> {
+ let node_id = self.get_our_node_id();
+ let expanded_key = &self.inbound_payment_key;
+ let entropy = &*self.entropy_source;
+ let secp_ctx = &self.secp_ctx;
+ let path = self.create_one_hop_blinded_path();
+
+ let builder = RefundBuilder::deriving_payer_id(
+ description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
+ )?
+ .chain_hash(self.chain_hash)
+ .absolute_expiry(absolute_expiry)
+ .path(path);
+
+ let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
+ self.pending_outbound_payments
+ .add_new_awaiting_invoice(
+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
+ )
+ .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
+
+ Ok(builder)
+ }
+
+ /// Pays for an [`Offer`] using the given parameters by creating an [`InvoiceRequest`] and
+ /// enqueuing it to be sent via an onion message. [`ChannelManager`] will pay the actual
+ /// [`Bolt12Invoice`] once it is received.
+ ///
+ /// Uses [`InvoiceRequestBuilder`] such that the [`InvoiceRequest`] it builds is recognized by
+ /// the [`ChannelManager`] when handling a [`Bolt12Invoice`] message in response to the request.
+ /// The optional parameters are used in the builder, if `Some`:
+ /// - `quantity` for [`InvoiceRequest::quantity`] which must be set if
+ /// [`Offer::expects_quantity`] is `true`.
+ /// - `amount_msats` if overpaying what is required for the given `quantity` is desired, and
+ /// - `payer_note` for [`InvoiceRequest::payer_note`].
+ ///
+ /// # Payment
+ ///
+ /// The provided `payment_id` is used to ensure that only one invoice is paid for the request
+ /// when received. See [Avoiding Duplicate Payments] for other requirements once the payment has
+ /// been sent.
+ ///
+ /// To revoke the request, use [`ChannelManager::abandon_payment`] prior to receiving the
+ /// invoice. If abandoned, or an invoice isn't received in a reasonable amount of time, the
+ /// payment will fail with an [`Event::InvoiceRequestFailed`].
+ ///
+ /// # Privacy
+ ///
+ /// Uses a one-hop [`BlindedPath`] for the reply path with [`ChannelManager::get_our_node_id`]
+ /// as the introduction node and a derived payer id for payer privacy. As such, currently, the
+ /// node must be announced. Otherwise, there is no way to find a path to the introduction node
+ /// in order to send the [`Bolt12Invoice`].
+ ///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to an introduction node in [`Offer::paths`] or to
+ /// [`Offer::signing_pubkey`], if empty. A similar restriction applies to the responding
+ /// [`Bolt12Invoice::payment_paths`].
+ ///
+ /// # Errors
+ ///
+ /// Errors if a duplicate `payment_id` is provided given the caveats in the aforementioned link
+ /// or if the provided parameters are invalid for the offer.
+ ///
+ /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+ /// [`InvoiceRequest::quantity`]: crate::offers::invoice_request::InvoiceRequest::quantity
+ /// [`InvoiceRequest::payer_note`]: crate::offers::invoice_request::InvoiceRequest::payer_note
+ /// [`InvoiceRequestBuilder`]: crate::offers::invoice_request::InvoiceRequestBuilder
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ /// [`Bolt12Invoice::payment_paths`]: crate::offers::invoice::Bolt12Invoice::payment_paths
+ /// [Avoiding Duplicate Payments]: #avoiding-duplicate-payments
+ pub fn pay_for_offer(
+ &self, offer: &Offer, quantity: Option<u64>, amount_msats: Option<u64>,
+ payer_note: Option<String>, payment_id: PaymentId, retry_strategy: Retry,
+ max_total_routing_fee_msat: Option<u64>
+ ) -> Result<(), Bolt12SemanticError> {
+ let expanded_key = &self.inbound_payment_key;
+ let entropy = &*self.entropy_source;
+ let secp_ctx = &self.secp_ctx;
+
+ let builder = offer
+ .request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)?
+ .chain_hash(self.chain_hash)?;
+ let builder = match quantity {
+ None => builder,
+ Some(quantity) => builder.quantity(quantity)?,
+ };
+ let builder = match amount_msats {
+ None => builder,
+ Some(amount_msats) => builder.amount_msats(amount_msats)?,
+ };
+ let builder = match payer_note {
+ None => builder,
+ Some(payer_note) => builder.payer_note(payer_note),
+ };
+
+ let invoice_request = builder.build_and_sign()?;
+ let reply_path = self.create_one_hop_blinded_path();
+
+ let expiration = StaleExpiration::TimerTicks(1);
+ self.pending_outbound_payments
+ .add_new_awaiting_invoice(
+ payment_id, expiration, retry_strategy, max_total_routing_fee_msat
+ )
+ .map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
+
+ let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
+ if offer.paths().is_empty() {
+ let message = PendingOnionMessage {
+ contents: OffersMessage::InvoiceRequest(invoice_request),
+ destination: Destination::Node(offer.signing_pubkey()),
+ reply_path: Some(reply_path),
+ };
+ pending_offers_messages.push(message);
+ } else {
+ // Send as many invoice requests as there are paths in the offer (with an upper bound).
+ // Using only one path could result in a failure if the path no longer exists. But only
+ // one invoice for a given payment id will be paid, even if more than one is received.
+ const REQUEST_LIMIT: usize = 10;
+ for path in offer.paths().into_iter().take(REQUEST_LIMIT) {
+ let message = PendingOnionMessage {
+ contents: OffersMessage::InvoiceRequest(invoice_request.clone()),
+ destination: Destination::BlindedPath(path.clone()),
+ reply_path: Some(reply_path.clone()),
+ };
+ pending_offers_messages.push(message);
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Creates a [`Bolt12Invoice`] for a [`Refund`] and enqueues it to be sent via an onion
+ /// message.
+ ///
+ /// The resulting invoice uses a [`PaymentHash`] recognized by the [`ChannelManager`] and a
+ /// [`BlindedPath`] containing the [`PaymentSecret`] needed to reconstruct the corresponding
+ /// [`PaymentPreimage`].
+ ///
+ /// # Limitations
+ ///
+ /// Requires a direct connection to an introduction node in [`Refund::paths`] or to
+ /// [`Refund::payer_id`], if empty. This request is best effort; an invoice will be sent to each
+ /// node meeting the aforementioned criteria, but there's no guarantee that they will be
+ /// received and no retries will be made.
+ ///
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
+ pub fn request_refund_payment(&self, refund: &Refund) -> Result<(), Bolt12SemanticError> {
+ let expanded_key = &self.inbound_payment_key;
+ let entropy = &*self.entropy_source;
+ let secp_ctx = &self.secp_ctx;
+
+ let amount_msats = refund.amount_msats();
+ let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
+
+ match self.create_inbound_payment(Some(amount_msats), relative_expiry, None) {
+ Ok((payment_hash, payment_secret)) => {
+ let payment_paths = vec![
+ self.create_one_hop_blinded_payment_path(payment_secret),
+ ];
+ #[cfg(not(feature = "no-std"))]
+ let builder = refund.respond_using_derived_keys(
+ payment_paths, payment_hash, expanded_key, entropy
+ )?;
+ #[cfg(feature = "no-std")]
+ let created_at = Duration::from_secs(
+ self.highest_seen_timestamp.load(Ordering::Acquire) as u64
+ );
+ #[cfg(feature = "no-std")]
+ let builder = refund.respond_using_derived_keys_no_std(
+ payment_paths, payment_hash, created_at, expanded_key, entropy
+ )?;
+ let invoice = builder.allow_mpp().build_and_sign(secp_ctx)?;
+ let reply_path = self.create_one_hop_blinded_path();
+
+ let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
+ if refund.paths().is_empty() {
+ let message = PendingOnionMessage {
+ contents: OffersMessage::Invoice(invoice),
+ destination: Destination::Node(refund.payer_id()),
+ reply_path: Some(reply_path),
+ };
+ pending_offers_messages.push(message);
+ } else {
+ for path in refund.paths() {
+ let message = PendingOnionMessage {
+ contents: OffersMessage::Invoice(invoice.clone()),
+ destination: Destination::BlindedPath(path.clone()),
+ reply_path: Some(reply_path.clone()),
+ };
+ pending_offers_messages.push(message);
+ }
+ }
+
+ Ok(())
+ },
+ Err(()) => Err(Bolt12SemanticError::InvalidAmount),
+ }
+ }
+
/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
/// to pay us.
///
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
}
+ /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
+ /// node.
+ fn create_one_hop_blinded_path(&self) -> BlindedPath {
+ let entropy_source = self.entropy_source.deref();
+ let secp_ctx = &self.secp_ctx;
+ BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
+ }
+
+ /// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
+ /// node.
+ fn create_one_hop_blinded_payment_path(
+ &self, payment_secret: PaymentSecret
+ ) -> (BlindedPayInfo, BlindedPath) {
+ let entropy_source = self.entropy_source.deref();
+ let secp_ctx = &self.secp_ctx;
+
+ let payee_node_id = self.get_our_node_id();
+ let max_cltv_expiry = self.best_block.read().unwrap().height() + LATENCY_GRACE_PERIOD_BLOCKS;
+ let payee_tlvs = ReceiveTlvs {
+ payment_secret,
+ payment_constraints: PaymentConstraints {
+ max_cltv_expiry,
+ htlc_minimum_msat: 1,
+ },
+ };
+ // TODO: Err for overflow?
+ BlindedPath::one_hop_for_payment(
+ payee_node_id, payee_tlvs, entropy_source, secp_ctx
+ ).unwrap()
+ }
+
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
/// are used when constructing the phantom invoice's route hints.
///
let best_block_height = self.best_block.read().unwrap().height();
let short_to_chan_info = self.short_to_chan_info.read().unwrap();
loop {
- let scid_candidate = fake_scid::Namespace::Phantom.get_fake_scid(best_block_height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
+ let scid_candidate = fake_scid::Namespace::Phantom.get_fake_scid(best_block_height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
// Ensure the generated scid doesn't conflict with a real channel.
match short_to_chan_info.get(&scid_candidate) {
Some(_) => continue,
let best_block_height = self.best_block.read().unwrap().height();
let short_to_chan_info = self.short_to_chan_info.read().unwrap();
loop {
- let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
+ let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source);
// Ensure the generated scid doesn't conflict with a real channel.
if short_to_chan_info.contains_key(&scid_candidate) { continue }
return scid_candidate
*best_block = BestBlock::new(header.prev_blockhash, new_height)
}
- self.do_chain_event(Some(new_height), |channel| channel.best_block_updated(new_height, header.time, self.genesis_hash.clone(), &self.node_signer, &self.default_configuration, &self.logger));
+ self.do_chain_event(Some(new_height), |channel| channel.best_block_updated(new_height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &self.logger));
}
}
let _persistence_guard =
PersistenceNotifierGuard::optionally_notify_skipping_background_events(
self, || -> NotifyOption { NotifyOption::DoPersist });
- self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.genesis_hash.clone(), &self.node_signer, &self.default_configuration, &self.logger)
+ self.do_chain_event(Some(height), |channel| channel.transactions_confirmed(&block_hash, height, txdata, self.chain_hash, &self.node_signer, &self.default_configuration, &self.logger)
.map(|(a, b)| (a, Vec::new(), b)));
let last_best_block_height = self.best_block.read().unwrap().height();
if height < last_best_block_height {
let timestamp = self.highest_seen_timestamp.load(Ordering::Acquire);
- self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.genesis_hash.clone(), &self.node_signer, &self.default_configuration, &self.logger));
+ self.do_chain_event(Some(last_best_block_height), |channel| channel.best_block_updated(last_best_block_height, timestamp as u32, self.chain_hash, &self.node_signer, &self.default_configuration, &self.logger));
}
}
self, || -> NotifyOption { NotifyOption::DoPersist });
*self.best_block.write().unwrap() = BestBlock::new(block_hash, height);
- self.do_chain_event(Some(height), |channel| channel.best_block_updated(height, header.time, self.genesis_hash.clone(), &self.node_signer, &self.default_configuration, &self.logger));
+ self.do_chain_event(Some(height), |channel| channel.best_block_updated(height, header.time, self.chain_hash, &self.node_signer, &self.default_configuration, &self.logger));
macro_rules! max_time {
($timestamp: expr) => {
msg: announcement_sigs,
});
if let Some(height) = height_opt {
- if let Some(announcement) = channel.get_signed_channel_announcement(&self.node_signer, self.genesis_hash, height, &self.default_configuration) {
+ if let Some(announcement) = channel.get_signed_channel_announcement(&self.node_signer, self.chain_hash, height, &self.default_configuration) {
pending_msg_events.push(events::MessageSendEvent::BroadcastChannelAnnouncement {
msg: announcement,
// Note that announcement_signatures fails if the channel cannot be announced,
self.issue_channel_close_events(&channel.context, reason);
pending_msg_events.push(events::MessageSendEvent::HandleError {
node_id: channel.context.get_counterparty_node_id(),
- action: msgs::ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage {
- channel_id: channel.context.channel_id(),
- data: reason_message,
- } },
+ action: msgs::ErrorAction::DisconnectPeer {
+ msg: Some(msgs::ErrorMessage {
+ channel_id: channel.context.channel_id(),
+ data: reason_message,
+ })
+ },
});
return false;
}
self.best_block.read().unwrap().clone()
}
- /// Fetches the set of [`NodeFeatures`] flags which are provided by or required by
+ /// Fetches the set of [`NodeFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub fn node_features(&self) -> NodeFeatures {
provided_node_features(&self.default_configuration)
}
- /// Fetches the set of [`Bolt11InvoiceFeatures`] flags which are provided by or required by
+ /// Fetches the set of [`Bolt11InvoiceFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
///
/// Note that the invoice feature flags can vary depending on if the invoice is a "phantom invoice"
/// or not. Thus, this method is not public.
#[cfg(any(feature = "_test_utils", test))]
- pub fn invoice_features(&self) -> Bolt11InvoiceFeatures {
- provided_invoice_features(&self.default_configuration)
+ pub fn bolt11_invoice_features(&self) -> Bolt11InvoiceFeatures {
+ provided_bolt11_invoice_features(&self.default_configuration)
}
- /// Fetches the set of [`ChannelFeatures`] flags which are provided by or required by
+ /// Fetches the set of [`Bolt12InvoiceFeatures`] flags that are provided by or required by
+ /// [`ChannelManager`].
+ fn bolt12_invoice_features(&self) -> Bolt12InvoiceFeatures {
+ provided_bolt12_invoice_features(&self.default_configuration)
+ }
+
+ /// Fetches the set of [`ChannelFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub fn channel_features(&self) -> ChannelFeatures {
provided_channel_features(&self.default_configuration)
}
- /// Fetches the set of [`ChannelTypeFeatures`] flags which are provided by or required by
+ /// Fetches the set of [`ChannelTypeFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub fn channel_type_features(&self) -> ChannelTypeFeatures {
provided_channel_type_features(&self.default_configuration)
}
- /// Fetches the set of [`InitFeatures`] flags which are provided by or required by
+ /// Fetches the set of [`InitFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub fn init_features(&self) -> InitFeatures {
provided_init_features(&self.default_configuration)
let mut peer_state_lock = peer_state_mutex_opt.unwrap().lock().unwrap();
let peer_state = &mut *peer_state_lock;
if let Some(ChannelPhase::UnfundedOutboundV1(chan)) = peer_state.channel_by_id.get_mut(&msg.channel_id) {
- if let Ok(msg) = chan.maybe_handle_error_without_close(self.genesis_hash, &self.fee_estimator) {
+ if let Ok(msg) = chan.maybe_handle_error_without_close(self.chain_hash, &self.fee_estimator) {
peer_state.pending_msg_events.push(events::MessageSendEvent::SendOpenChannel {
node_id: *counterparty_node_id,
msg,
provided_init_features(&self.default_configuration)
}
- fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
- Some(vec![ChainHash::from(&self.genesis_hash[..])])
+ fn get_chain_hashes(&self) -> Option<Vec<ChainHash>> {
+ Some(vec![self.chain_hash])
}
fn handle_tx_add_input(&self, counterparty_node_id: &PublicKey, msg: &msgs::TxAddInput) {
}
}
-/// Fetches the set of [`NodeFeatures`] flags which are provided by or required by
+impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
+OffersMessageHandler for ChannelManager<M, T, ES, NS, SP, F, R, L>
+where
+ M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ ES::Target: EntropySource,
+ NS::Target: NodeSigner,
+ SP::Target: SignerProvider,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
+{
+ fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
+ let secp_ctx = &self.secp_ctx;
+ let expanded_key = &self.inbound_payment_key;
+
+ match message {
+ OffersMessage::InvoiceRequest(invoice_request) => {
+ let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
+ &invoice_request
+ ) {
+ Ok(amount_msats) => Some(amount_msats),
+ Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
+ };
+ let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
+ Ok(invoice_request) => invoice_request,
+ Err(()) => {
+ let error = Bolt12SemanticError::InvalidMetadata;
+ return Some(OffersMessage::InvoiceError(error.into()));
+ },
+ };
+ let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
+
+ match self.create_inbound_payment(amount_msats, relative_expiry, None) {
+ Ok((payment_hash, payment_secret)) if invoice_request.keys.is_some() => {
+ let payment_paths = vec![
+ self.create_one_hop_blinded_payment_path(payment_secret),
+ ];
+ #[cfg(not(feature = "no-std"))]
+ let builder = invoice_request.respond_using_derived_keys(
+ payment_paths, payment_hash
+ );
+ #[cfg(feature = "no-std")]
+ let created_at = Duration::from_secs(
+ self.highest_seen_timestamp.load(Ordering::Acquire) as u64
+ );
+ #[cfg(feature = "no-std")]
+ let builder = invoice_request.respond_using_derived_keys_no_std(
+ payment_paths, payment_hash, created_at
+ );
+ match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
+ Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
+ Err(error) => Some(OffersMessage::InvoiceError(error.into())),
+ }
+ },
+ Ok((payment_hash, payment_secret)) => {
+ let payment_paths = vec![
+ self.create_one_hop_blinded_payment_path(payment_secret),
+ ];
+ #[cfg(not(feature = "no-std"))]
+ let builder = invoice_request.respond_with(payment_paths, payment_hash);
+ #[cfg(feature = "no-std")]
+ let created_at = Duration::from_secs(
+ self.highest_seen_timestamp.load(Ordering::Acquire) as u64
+ );
+ #[cfg(feature = "no-std")]
+ let builder = invoice_request.respond_with_no_std(
+ payment_paths, payment_hash, created_at
+ );
+ let response = builder.and_then(|builder| builder.allow_mpp().build())
+ .map_err(|e| OffersMessage::InvoiceError(e.into()))
+ .and_then(|invoice|
+ match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
+ Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
+ Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
+ InvoiceError::from_str("Failed signing invoice")
+ )),
+ Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
+ InvoiceError::from_str("Failed invoice signature verification")
+ )),
+ });
+ match response {
+ Ok(invoice) => Some(invoice),
+ Err(error) => Some(error),
+ }
+ },
+ Err(()) => {
+ Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into()))
+ },
+ }
+ },
+ OffersMessage::Invoice(invoice) => {
+ match invoice.verify(expanded_key, secp_ctx) {
+ Err(()) => {
+ Some(OffersMessage::InvoiceError(InvoiceError::from_str("Unrecognized invoice")))
+ },
+ Ok(_) if invoice.invoice_features().requires_unknown_bits_from(&self.bolt12_invoice_features()) => {
+ Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
+ },
+ Ok(payment_id) => {
+ if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
+ log_trace!(self.logger, "Failed paying invoice: {:?}", e);
+ Some(OffersMessage::InvoiceError(InvoiceError::from_str(&format!("{:?}", e))))
+ } else {
+ None
+ }
+ },
+ }
+ },
+ OffersMessage::InvoiceError(invoice_error) => {
+ log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
+ None
+ },
+ }
+ }
+
+ fn release_pending_messages(&self) -> Vec<PendingOnionMessage<OffersMessage>> {
+ core::mem::take(&mut self.pending_offers_messages.lock().unwrap())
+ }
+}
+
+/// Fetches the set of [`NodeFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub(crate) fn provided_node_features(config: &UserConfig) -> NodeFeatures {
let mut node_features = provided_init_features(config).to_context();
node_features
}
-/// Fetches the set of [`Bolt11InvoiceFeatures`] flags which are provided by or required by
+/// Fetches the set of [`Bolt11InvoiceFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
///
/// Note that the invoice feature flags can vary depending on if the invoice is a "phantom invoice"
/// or not. Thus, this method is not public.
#[cfg(any(feature = "_test_utils", test))]
-pub(crate) fn provided_invoice_features(config: &UserConfig) -> Bolt11InvoiceFeatures {
+pub(crate) fn provided_bolt11_invoice_features(config: &UserConfig) -> Bolt11InvoiceFeatures {
+ provided_init_features(config).to_context()
+}
+
+/// Fetches the set of [`Bolt12InvoiceFeatures`] flags that are provided by or required by
+/// [`ChannelManager`].
+pub(crate) fn provided_bolt12_invoice_features(config: &UserConfig) -> Bolt12InvoiceFeatures {
provided_init_features(config).to_context()
}
-/// Fetches the set of [`ChannelFeatures`] flags which are provided by or required by
+/// Fetches the set of [`ChannelFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub(crate) fn provided_channel_features(config: &UserConfig) -> ChannelFeatures {
provided_init_features(config).to_context()
}
-/// Fetches the set of [`ChannelTypeFeatures`] flags which are provided by or required by
+/// Fetches the set of [`ChannelTypeFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub(crate) fn provided_channel_type_features(config: &UserConfig) -> ChannelTypeFeatures {
ChannelTypeFeatures::from_init(&provided_init_features(config))
}
-/// Fetches the set of [`InitFeatures`] flags which are provided by or required by
+/// Fetches the set of [`InitFeatures`] flags that are provided by or required by
/// [`ChannelManager`].
pub fn provided_init_features(config: &UserConfig) -> InitFeatures {
// Note that if new features are added here which other peers may (eventually) require, we
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
- self.genesis_hash.write(writer)?;
+ self.chain_hash.write(writer)?;
{
let best_block = self.best_block.read().unwrap();
best_block.height().write(writer)?;
fn read<Reader: io::Read>(reader: &mut Reader, mut args: ChannelManagerReadArgs<'a, M, T, ES, NS, SP, F, R, L>) -> Result<Self, DecodeError> {
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
- let genesis_hash: BlockHash = Readable::read(reader)?;
+ let chain_hash: ChainHash = Readable::read(reader)?;
let best_block_height: u32 = Readable::read(reader)?;
let best_block_hash: BlockHash = Readable::read(reader)?;
let mut outbound_scid_alias;
loop {
outbound_scid_alias = fake_scid::Namespace::OutboundAlias
- .get_fake_scid(best_block_height, &genesis_hash, fake_scid_rand_bytes.as_ref().unwrap(), &args.entropy_source);
+ .get_fake_scid(best_block_height, &chain_hash, fake_scid_rand_bytes.as_ref().unwrap(), &args.entropy_source);
if outbound_scid_aliases.insert(outbound_scid_alias) { break; }
}
chan.context.set_outbound_scid_alias(outbound_scid_alias);
Some((blocked_node_id, blocked_channel_outpoint, blocking_action)), ..
} = action {
if let Some(blocked_peer_state) = per_peer_state.get(&blocked_node_id) {
+ log_trace!(args.logger,
+ "Holding the next revoke_and_ack from {} until the preimage is durably persisted in the inbound edge's ChannelMonitor",
+ blocked_channel_outpoint.to_channel_id());
blocked_peer_state.lock().unwrap().actions_blocking_raa_monitor_updates
.entry(blocked_channel_outpoint.to_channel_id())
.or_insert_with(Vec::new).push(blocking_action.clone());
// anymore.
}
}
+ if let MonitorUpdateCompletionAction::FreeOtherChannelImmediately { .. } = action {
+ debug_assert!(false, "Non-event-generating channel freeing should not appear in our queue");
+ }
}
}
peer_state.lock().unwrap().monitor_update_blocked_actions = monitor_update_blocked_actions;
}
let channel_manager = ChannelManager {
- genesis_hash,
+ chain_hash,
fee_estimator: bounded_fee_estimator,
chain_monitor: args.chain_monitor,
tx_broadcaster: args.tx_broadcaster,
funding_batch_states: Mutex::new(BTreeMap::new()),
+ pending_offers_messages: Mutex::new(Vec::new()),
+
entropy_source: args.entropy_source,
node_signer: args.node_signer,
signer_provider: args.signer_provider,
// don't remember in the `ChannelMonitor` where we got a preimage from, but if the
// channel is closed we just assume that it probably came from an on-chain claim.
channel_manager.claim_funds_internal(source, preimage, Some(downstream_value),
- downstream_closed, downstream_node_id, downstream_funding);
+ downstream_closed, true, downstream_node_id, downstream_funding);
}
//TODO: Broadcast channel update for closed channels, but only after we've made a
let payment_preimage = PaymentPreimage([42; 32]);
assert_eq!(format!("{}", &payment_preimage), "2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a");
}
+
+ #[test]
+ fn test_trigger_lnd_force_close() {
+ let chanmon_cfg = create_chanmon_cfgs(2);
+ let node_cfg = create_node_cfgs(2, &chanmon_cfg);
+ let user_config = test_default_channel_config();
+ let node_chanmgr = create_node_chanmgrs(2, &node_cfg, &[Some(user_config), Some(user_config)]);
+ let nodes = create_network(2, &node_cfg, &node_chanmgr);
+
+ // Open a channel, immediately disconnect each other, and broadcast Alice's latest state.
+ let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
+ nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
+ nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+ check_closed_broadcast(&nodes[0], 1, true);
+ check_added_monitors(&nodes[0], 1);
+ check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
+ {
+ let txn = nodes[0].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ check_spends!(txn[0], funding_tx);
+ }
+
+ // Since they're disconnected, Bob won't receive Alice's `Error` message. Reconnect them
+ // such that Bob sends a `ChannelReestablish` to Alice since the channel is still open from
+ // their side.
+ nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init {
+ features: nodes[1].node.init_features(), networks: None, remote_network_address: None
+ }, true).unwrap();
+ nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init {
+ features: nodes[0].node.init_features(), networks: None, remote_network_address: None
+ }, false).unwrap();
+ assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
+ let channel_reestablish = get_event_msg!(
+ nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id()
+ );
+ nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &channel_reestablish);
+
+ // Alice should respond with an error since the channel isn't known, but a bogus
+ // `ChannelReestablish` should be sent first, such that we actually trigger Bob to force
+ // close even if it was an lnd node.
+ let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(msg_events.len(), 2);
+ if let MessageSendEvent::SendChannelReestablish { node_id, msg } = &msg_events[0] {
+ assert_eq!(*node_id, nodes[1].node.get_our_node_id());
+ assert_eq!(msg.next_local_commitment_number, 0);
+ assert_eq!(msg.next_remote_commitment_number, 0);
+ nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &msg);
+ } else { panic!() };
+ check_closed_broadcast(&nodes[1], 1, true);
+ check_added_monitors(&nodes[1], 1);
+ let expected_close_reason = ClosureReason::ProcessingError {
+ err: "Peer sent an invalid channel_reestablish to force close in a non-standard way".to_string()
+ };
+ check_closed_event!(nodes[1], 1, expected_close_reason, [nodes[0].node.get_our_node_id()], 100000);
+ {
+ let txn = nodes[1].tx_broadcaster.txn_broadcast();
+ assert_eq!(txn.len(), 1);
+ check_spends!(txn[0], funding_tx);
+ }
+ }
}
#[cfg(ldk_bench)]
macro_rules! send_payment {
($node_a: expr, $node_b: expr) => {
let payment_params = PaymentParameters::from_node_id($node_b.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features($node_b.invoice_features()).unwrap();
+ .with_bolt11_features($node_b.bolt11_invoice_features()).unwrap();
let mut payment_preimage = PaymentPreimage([0; 32]);
payment_preimage.0[0..8].copy_from_slice(&payment_count.to_le_bytes());
payment_count += 1;
assert_eq!(node_id, recipient);
(*msg).clone()
},
+ MessageSendEvent::HandleError {
+ action: msgs::ErrorAction::DisconnectPeer { ref msg }, ref node_id
+ } => {
+ assert_eq!(node_id, recipient);
+ msg.as_ref().unwrap().clone()
+ },
_ => panic!("Unexpected event"),
}
}
assert_eq!(msg.contents.flags & 2, 2);
None
},
- MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { ref msg }, node_id: _ } => {
+ MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { msg }, node_id: _ } => {
assert!(with_error_msg);
// TODO: Check node_id
- Some(msg.clone())
+ Some(msg)
+ },
+ MessageSendEvent::HandleError { action: msgs::ErrorAction::DisconnectPeer { msg }, node_id: _ } => {
+ assert!(with_error_msg);
+ // TODO: Check node_id
+ Some(msg.unwrap())
},
_ => panic!("Unexpected event"),
}
macro_rules! get_route_and_payment_hash {
($send_node: expr, $recv_node: expr, $recv_value: expr) => {{
let payment_params = $crate::routing::router::PaymentParameters::from_node_id($recv_node.node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features($recv_node.node.invoice_features()).unwrap();
+ .with_bolt11_features($recv_node.node.bolt11_invoice_features()).unwrap();
$crate::get_route_and_payment_hash!($send_node, $recv_node, payment_params, $recv_value)
}};
($send_node: expr, $recv_node: expr, $payment_params: expr, $recv_value: expr) => {{
pub fn route_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) -> (PaymentPreimage, PaymentHash, PaymentSecret, PaymentId) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
+ .with_bolt11_features(expected_route.last().unwrap().node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value);
let route = get_route(origin_node, &route_params).unwrap();
assert_eq!(route.paths.len(), 1);
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
let payment_params = PaymentParameters::from_node_id(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(expected_route.last().unwrap().node.invoice_features()).unwrap();
+ .with_bolt11_features(expected_route.last().unwrap().node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, recv_value);
let network_graph = origin_node.network_graph.read_only();
let scorer = test_utils::TestScorer::new();
nodes[b].node.handle_error(&nodes[a].node.get_our_node_id(), msg);
}
},
+ MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::DisconnectPeer { ref msg } } => {
+ assert_eq!(node_id, nodes[b].node.get_our_node_id());
+ assert_eq!(msg.as_ref().unwrap().data, expected_error);
+ if needs_err_handle {
+ nodes[b].node.handle_error(&nodes[a].node.get_our_node_id(), msg.as_ref().unwrap());
+ }
+ },
_ => panic!("Unexpected event"),
}
assert_eq!(node_id, nodes[a].node.get_our_node_id());
assert_eq!(msg.data, expected_error);
},
+ MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::DisconnectPeer { ref msg } } => {
+ assert_eq!(node_id, nodes[a].node.get_our_node_id());
+ assert_eq!(msg.as_ref().unwrap().data, expected_error);
+ },
_ => panic!("Unexpected event"),
}
}
use bitcoin::hash_types::BlockHash;
use bitcoin::blockdata::script::{Builder, Script};
use bitcoin::blockdata::opcodes;
-use bitcoin::blockdata::constants::genesis_block;
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
use bitcoin::{PackedLockTime, Sequence, Transaction, TxIn, TxOut, Witness};
use bitcoin::OutPoint as BitcoinOutPoint;
for e in events {
match e {
MessageSendEvent::BroadcastChannelUpdate { .. } => {},
- MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
+ MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::DisconnectPeer { ref msg } } => {
assert_eq!(node_id, nodes[1].node.get_our_node_id());
- assert_eq!(msg.data, "Channel closed because commitment or closing transaction was confirmed on chain.");
+ assert_eq!(msg.as_ref().unwrap().data, "Channel closed because commitment or closing transaction was confirmed on chain.");
},
MessageSendEvent::UpdateHTLCs { ref node_id, updates: msgs::CommitmentUpdate { ref update_add_htlcs, ref update_fulfill_htlcs, ref update_fail_htlcs, ref update_fail_malformed_htlcs, .. } } => {
assert!(update_add_htlcs.is_empty());
// attempt to send amt_msat > their_max_htlc_value_in_flight_msat
{
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, recv_value_0);
route.paths[0].hops.last_mut().unwrap().fee_msat += 1;
assert!(route.paths[0].hops.iter().rev().skip(1).all(|h| h.fee_msat == feemsat));
}
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap().with_max_channel_saturation_power_of_half(0);
let route = get_route!(nodes[0], payment_params, recv_value_0).unwrap();
let (payment_preimage, ..) = send_along_route(&nodes[0], route, &[&nodes[1], &nodes[2]], recv_value_0);
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
_ => panic!("Unexpected event"),
};
match events[1] {
- MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { .. }, node_id } => {
+ MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { .. }, node_id } => {
assert_eq!(node_id, nodes[4].node.get_our_node_id());
},
_ => panic!("Unexpected event"),
_ => panic!("Unexpected event"),
};
match events[1] {
- MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { .. }, node_id } => {
+ MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { .. }, node_id } => {
assert_eq!(node_id, nodes[3].node.get_our_node_id());
},
_ => panic!("Unexpected event"),
let nodes_0_event = remove_first_msg_event_to_node(&nodes[0].node.get_our_node_id(), &mut events);
match nodes_2_event {
- MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { .. }, node_id: _ } => {},
+ MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { .. }, node_id: _ } => {},
_ => panic!("Unexpected event"),
}
let nodes_2_event = remove_first_msg_event_to_node(&nodes[2].node.get_our_node_id(), &mut events);
match nodes_2_event {
- MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { msg: msgs::ErrorMessage { channel_id, ref data } }, node_id: _ } => {
+ MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { msg: Some(msgs::ErrorMessage { channel_id, ref data }) }, node_id: _ } => {
assert_eq!(channel_id, chan_2.2);
assert_eq!(data.as_str(), "Channel closed because commitment or closing transaction was confirmed on chain.");
},
let nodes_0_event = remove_first_msg_event_to_node(&nodes[0].node.get_our_node_id(), &mut msg_events);
match nodes_2_event {
- MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { .. }, node_id: _ } => {},
+ MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { .. }, node_id: _ } => {},
_ => panic!("Unexpected event"),
}
// script push size limit so that the below script length checks match
// ACCEPTED_HTLC_SCRIPT_WEIGHT.
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV - 40)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let (route, _, _, _) = get_route_and_payment_hash!(nodes[0], nodes[3], payment_params, 800_000);
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[2], &nodes[3]]], 800_000, duplicate_payment_hash, payment_secret);
assert!(node0_to_1_send_open_channel.to_self_delay==BREAKDOWN_TIMEOUT);
// BOLT #2 spec: Sending node must ensure the chain_hash value identifies the chain it wishes to open the channel within.
- let chain_hash=genesis_block(Network::Testnet).header.block_hash();
- assert_eq!(node0_to_1_send_open_channel.chain_hash,chain_hash);
+ let chain_hash = ChainHash::using_genesis_block(Network::Testnet);
+ assert_eq!(node0_to_1_send_open_channel.chain_hash, chain_hash);
// BOLT #2 spec: Sending node must set funding_pubkey, revocation_basepoint, htlc_basepoint, payment_basepoint, and delayed_payment_basepoint to valid DER-encoded, compressed, secp256k1 pubkeys.
assert!(PublicKey::from_slice(&node0_to_1_send_open_channel.funding_pubkey.serialize()).is_ok());
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 0)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000);
route.paths[0].hops.last_mut().unwrap().cltv_expiry_delta = 500000001;
unwrap_send_err!(nodes[0].node.send_payment_with_route(&route, our_payment_hash,
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
- let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(),
- TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 10_000);
let route = get_route(&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(),
None, nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 30)
- .with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[0].node.bolt11_invoice_features()).unwrap();
let (route,_, _, _) = get_route_and_payment_hash!(nodes[1], nodes[0], payment_params, 3000000);
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000);
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
- let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), 50).with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let scorer = test_utils::TestScorer::new();
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
let route = get_route(&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(), None,
nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
- let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), 50)
+ .with_bolt11_features(nodes[0].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 3_000_000);
let route = get_route(&nodes[1].node.get_our_node_id(), &route_params, &nodes[1].network_graph.read_only(), None,
nodes[0].logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
let close_ev = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(close_ev.len(), 1);
match close_ev[0] {
- MessageSendEvent::HandleError { action: ErrorAction::SendErrorMessage { ref msg }, ref node_id } => {
+ MessageSendEvent::HandleError { action: ErrorAction::DisconnectPeer { ref msg }, ref node_id } => {
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
- assert_eq!(msg.data, "Channel closed because funding transaction failed to confirm within 2016 blocks");
+ assert_eq!(msg.as_ref().unwrap().data, "Channel closed because funding transaction failed to confirm within 2016 blocks");
},
_ => panic!("Unexpected event"),
}
assert_eq!(events_2.len(), 1);
if let MessageSendEvent::HandleError { node_id, action } = &events_2[0] {
assert_eq!(*node_id, nodes[0].node.get_our_node_id());
- if let msgs::ErrorAction::SendErrorMessage { msg } = action {
- assert_eq!(msg.data, "Channel closed because of an exception: ".to_owned() + expected_err);
+ if let msgs::ErrorAction::DisconnectPeer { msg } = action {
+ assert_eq!(msg.as_ref().unwrap().data, "Channel closed because of an exception: ".to_owned() + expected_err);
} else { panic!(); }
} else { panic!(); }
assert_eq!(nodes[1].node.list_channels().len(), 0);
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001);
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let route = get_route!(nodes[0], payment_params, 10_000).unwrap();
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(&nodes[1]);
let chan_2_3 =create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 100_000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let mut route = get_route!(nodes[0], payment_params, 15_000_000).unwrap();
assert_eq!(route.paths.len(), 2);
route.paths.sort_by(|path_a, _| {
MessageSendEvent::UpdateHTLCs { updates: msgs::CommitmentUpdate { ref update_fee, .. }, .. } => {
nodes[1].node.handle_update_fee(&nodes[0].node.get_our_node_id(), update_fee.as_ref().unwrap());
check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError {
- err: "Peer's feerate much too low. Actual: 1000. Our expected lower limit: 5000 (- 250)".to_owned() },
+ err: "Peer's feerate much too low. Actual: 1000. Our expected lower limit: 5000".to_owned() },
[nodes[0].node.get_our_node_id()], 100000);
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
let mut msg_events = closing_node.node.get_and_clear_pending_msg_events();
assert_eq!(msg_events.len(), 1);
match msg_events.pop().unwrap() {
- MessageSendEvent::HandleError { action: msgs::ErrorAction::SendErrorMessage { .. }, .. } => {},
+ MessageSendEvent::HandleError { action: msgs::ErrorAction::DisconnectPeer { .. }, .. } => {},
_ => panic!("Unexpected event"),
}
check_added_monitors(closing_node, 1);
// Create some initial channels
let (_, _, chan_id, funding_tx) =
- create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 11_000_000);
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 12_000_000);
let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
assert_eq!(funding_outpoint.to_channel_id(), chan_id);
assert_eq!(revoked_local_txn[0].input.len(), 1);
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, funding_tx.txid());
if anchors {
- assert_eq!(revoked_local_txn[0].output[4].value, 10000); // to_self output
+ assert_eq!(revoked_local_txn[0].output[4].value, 11000); // to_self output
} else {
- assert_eq!(revoked_local_txn[0].output[2].value, 10000); // to_self output
+ assert_eq!(revoked_local_txn[0].output[2].value, 11000); // to_self output
}
// The to-be-revoked commitment tx should have two HTLCs, an output for each side, and an
let anchor_outputs_value = if anchors { channel::ANCHOR_OUTPUT_VALUE_SATOSHI * 2 } else { 0 };
let as_balances = sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
// to_remote output in B's revoked commitment
- amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
+ amount_satoshis: 1_000_000 - 12_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
confirmation_height: to_remote_conf_height,
}, Balance::CounterpartyRevokedOutputClaimable {
// to_self output in B's revoked commitment
- amount_satoshis: 10_000,
+ amount_satoshis: 11_000,
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 1
amount_satoshis: 3_000,
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
mine_transaction(&nodes[0], &as_htlc_claim_tx[0]);
assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
// to_remote output in B's revoked commitment
- amount_satoshis: 1_000_000 - 11_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
+ amount_satoshis: 1_000_000 - 12_000 - 3_000 - commitment_tx_fee - anchor_outputs_value,
confirmation_height: to_remote_conf_height,
}, Balance::CounterpartyRevokedOutputClaimable {
// to_self output in B's revoked commitment
- amount_satoshis: 10_000,
+ amount_satoshis: 11_000,
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
amount_satoshis: 1_000,
}, Balance::ClaimableAwaitingConfirmations {
test_spendable_output(&nodes[0], &revoked_local_txn[0], false);
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
// to_self output to B
- amount_satoshis: 10_000,
+ amount_satoshis: 11_000,
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
amount_satoshis: 1_000,
}, Balance::ClaimableAwaitingConfirmations {
test_spendable_output(&nodes[0], &as_htlc_claim_tx[0], false);
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
// to_self output in B's revoked commitment
- amount_satoshis: 10_000,
+ amount_satoshis: 11_000,
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
amount_satoshis: 1_000,
}]),
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
// to_self output in B's revoked commitment
- amount_satoshis: 10_000,
+ amount_satoshis: 11_000,
}, Balance::CounterpartyRevokedOutputClaimable { // HTLC 2
amount_satoshis: 1_000,
}]),
mine_transaction(&nodes[0], &revoked_htlc_timeout_claim);
assert_eq!(sorted_vec(vec![Balance::CounterpartyRevokedOutputClaimable {
// to_self output in B's revoked commitment
- amount_satoshis: 10_000,
+ amount_satoshis: 11_000,
}, Balance::ClaimableAwaitingConfirmations {
amount_satoshis: revoked_htlc_timeout_claim.output[0].value,
confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
use bitcoin::secp256k1::ecdsa::Signature;
use bitcoin::{secp256k1, Witness};
use bitcoin::blockdata::script::Script;
-use bitcoin::hash_types::{Txid, BlockHash};
+use bitcoin::hash_types::Txid;
use crate::blinded_path::payment::ReceiveTlvs;
use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
use core::ops::Deref;
#[cfg(feature = "std")]
use core::str::FromStr;
+#[cfg(feature = "std")]
+use std::net::SocketAddr;
+use core::fmt::Display;
use crate::io::{self, Cursor, Read};
use crate::io_extras::read_to_end;
-use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
+use crate::events::MessageSendEventsProvider;
use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
use crate::util::logger;
use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannel {
/// The genesis hash of the blockchain where the channel is to be opened
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// A temporary channel ID, until the funding outpoint is announced
pub temporary_channel_id: ChannelId,
/// The channel value
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenChannelV2 {
/// The genesis hash of the blockchain where the channel is to be opened
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// A temporary channel ID derived using a zeroed out value for the channel acceptor's revocation basepoint
pub temporary_channel_id: ChannelId,
/// The feerate for the funding transaction set by the channel initiator
}
}
+#[cfg(feature = "std")]
+impl std::net::ToSocketAddrs for SocketAddress {
+ type Iter = std::vec::IntoIter<std::net::SocketAddr>;
+
+ fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
+ match self {
+ SocketAddress::TcpIpV4 { addr, port } => {
+ let ip_addr = std::net::Ipv4Addr::from(*addr);
+ let socket_addr = SocketAddr::new(ip_addr.into(), *port);
+ Ok(vec![socket_addr].into_iter())
+ }
+ SocketAddress::TcpIpV6 { addr, port } => {
+ let ip_addr = std::net::Ipv6Addr::from(*addr);
+ let socket_addr = SocketAddr::new(ip_addr.into(), *port);
+ Ok(vec![socket_addr].into_iter())
+ }
+ SocketAddress::Hostname { ref hostname, port } => {
+ (hostname.as_str(), *port).to_socket_addrs()
+ }
+ SocketAddress::OnionV2(..) => {
+ Err(std::io::Error::new(std::io::ErrorKind::Other, "Resolution of OnionV2 \
+ addresses is currently unsupported."))
+ }
+ SocketAddress::OnionV3 { .. } => {
+ Err(std::io::Error::new(std::io::ErrorKind::Other, "Resolution of OnionV3 \
+ addresses is currently unsupported."))
+ }
+ }
+ }
+}
+
/// Parses an OnionV3 host and port into a [`SocketAddress::OnionV3`].
///
/// The host part must end with ".onion".
}
}
+impl Display for SocketAddress {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ match self {
+ SocketAddress::TcpIpV4{addr, port} => write!(
+ f, "{}.{}.{}.{}:{}", addr[0], addr[1], addr[2], addr[3], port)?,
+ SocketAddress::TcpIpV6{addr, port} => write!(
+ f,
+ "[{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}:{:02x}{:02x}]:{}",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], addr[10], addr[11], addr[12], addr[13], addr[14], addr[15], port
+ )?,
+ SocketAddress::OnionV2(bytes) => write!(f, "OnionV2({:?})", bytes)?,
+ SocketAddress::OnionV3 {
+ ed25519_pubkey,
+ checksum,
+ version,
+ port,
+ } => {
+ let [first_checksum_flag, second_checksum_flag] = checksum.to_be_bytes();
+ let mut addr = vec![*version, first_checksum_flag, second_checksum_flag];
+ addr.extend_from_slice(ed25519_pubkey);
+ let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&addr);
+ write!(f, "{}.onion:{}", onion, port)?
+ },
+ SocketAddress::Hostname { hostname, port } => write!(f, "{}:{}", hostname, port)?,
+ }
+ Ok(())
+ }
+}
+
#[cfg(feature = "std")]
impl FromStr for SocketAddress {
type Err = SocketAddressParseError;
/// The advertised channel features
pub features: ChannelFeatures,
/// The genesis hash of the blockchain where the channel is to be opened
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// The short channel ID
pub short_channel_id: u64,
/// One of the two `node_id`s which are endpoints of this channel
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnsignedChannelUpdate {
/// The genesis hash of the blockchain where the channel is to be opened
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// The short channel ID
pub short_channel_id: u64,
/// A strictly monotonic announcement counter, with gaps allowed, specific to this channel
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryChannelRange {
/// The genesis hash of the blockchain being queried
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// The height of the first block for the channel UTXOs being queried
pub first_blocknum: u32,
/// The number of blocks to include in the query results
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplyChannelRange {
/// The genesis hash of the blockchain being queried
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// The height of the first block in the range of the reply
pub first_blocknum: u32,
/// The number of blocks included in the range of the reply
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryShortChannelIds {
/// The genesis hash of the blockchain being queried
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// The short_channel_ids that are being queried
pub short_channel_ids: Vec<u64>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReplyShortChannelIdsEnd {
/// The genesis hash of the blockchain that was queried
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// Indicates if the query recipient maintains up-to-date channel
/// information for the `chain_hash`
pub full_information: bool,
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GossipTimestampFilter {
/// The genesis hash of the blockchain for channel and node information
- pub chain_hash: BlockHash,
+ pub chain_hash: ChainHash,
/// The starting unix timestamp
pub first_timestamp: u32,
/// The range of information in seconds
/// Note that this method is called before [`Self::peer_connected`].
fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
- /// Gets the genesis hashes for this `ChannelMessageHandler` indicating which chains it supports.
+ /// Gets the chain hashes for this `ChannelMessageHandler` indicating which chains it supports.
///
/// If it's `None`, then no particular network chain hash compatibility will be enforced when
/// connecting to peers.
- fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>>;
+ fn get_chain_hashes(&self) -> Option<Vec<ChainHash>>;
}
/// A trait to describe an object which can receive routing messages.
fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
}
-/// A trait to describe an object that can receive onion messages.
-pub trait OnionMessageHandler : OnionMessageProvider {
+/// A handler for received [`OnionMessage`]s and for providing generated ones to send.
+pub trait OnionMessageHandler {
/// Handle an incoming `onion_message` message from the given peer.
fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage);
+
+ /// Returns the next pending onion message for the peer with the given node id.
+ fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<OnionMessage>;
+
/// Called when a connection is established with a peer. Can be used to track which peers
/// advertise onion message support and are online.
///
/// with us. Implementors should be somewhat conservative about doing so, however, as other
/// message handlers may still wish to communicate with this peer.
fn peer_connected(&self, their_node_id: &PublicKey, init: &Init, inbound: bool) -> Result<(), ()>;
+
/// Indicates a connection to the peer failed/an existing connection was lost. Allows handlers to
/// drop and refuse to forward onion messages to this peer.
fn peer_disconnected(&self, their_node_id: &PublicKey);
impl Readable for QueryShortChannelIds {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let chain_hash: BlockHash = Readable::read(r)?;
+ let chain_hash: ChainHash = Readable::read(r)?;
let encoding_len: u16 = Readable::read(r)?;
let encoding_type: u8 = Readable::read(r)?;
impl Readable for ReplyChannelRange {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let chain_hash: BlockHash = Readable::read(r)?;
+ let chain_hash: ChainHash = Readable::read(r)?;
let first_blocknum: u32 = Readable::read(r)?;
let number_of_blocks: u32 = Readable::read(r)?;
let sync_complete: bool = Readable::read(r)?;
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
- use bitcoin::blockdata::constants::ChainHash;
use bitcoin::{Transaction, PackedLockTime, TxIn, Script, Sequence, Witness, TxOut};
use hex;
use crate::ln::{PaymentPreimage, PaymentHash, PaymentSecret};
use bitcoin::hashes::hex::FromHex;
use bitcoin::util::address::Address;
use bitcoin::network::constants::Network;
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::opcodes;
- use bitcoin::hash_types::{Txid, BlockHash};
+ use bitcoin::hash_types::Txid;
use bitcoin::secp256k1::{PublicKey,SecretKey};
use bitcoin::secp256k1::{Secp256k1, Message};
use crate::chain::transaction::OutPoint;
#[cfg(feature = "std")]
- use std::net::{Ipv4Addr, Ipv6Addr};
+ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
+ #[cfg(feature = "std")]
use crate::ln::msgs::SocketAddressParseError;
#[test]
}
let unsigned_channel_announcement = msgs::UnsignedChannelAnnouncement {
features,
- chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
+ chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
short_channel_id: 2316138423780173,
node_id_1: NodeId::from_pubkey(&pubkey_1),
node_id_2: NodeId::from_pubkey(&pubkey_2),
} else {
target_value.append(&mut hex::decode("0000").unwrap());
}
- target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
+ target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap());
target_value.append(&mut hex::decode("00083a840000034d031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b").unwrap());
if excess_data {
target_value.append(&mut hex::decode("0a00001400001e000028").unwrap());
let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
let unsigned_channel_update = msgs::UnsignedChannelUpdate {
- chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
+ chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
short_channel_id: 2316138423780173,
timestamp: 20190119,
flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 },
};
let encoded_value = channel_update.encode();
let mut target_value = hex::decode("d977cb9b53d93a6ff64bb5f1e158b4094b66e798fb12911168a3ccdf80a83096340a6a95da0ae8d9f776528eecdbb747eb6b545495a4319ed5378e35b21e073a").unwrap();
- target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
+ target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap());
target_value.append(&mut hex::decode("00083a840000034d013413a7").unwrap());
target_value.append(&mut hex::decode("01").unwrap());
target_value.append(&mut hex::decode("00").unwrap());
let (_, pubkey_5) = get_keys_from!("0505050505050505050505050505050505050505050505050505050505050505", secp_ctx);
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let open_channel = msgs::OpenChannel {
- chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
+ chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
temporary_channel_id: ChannelId::from_bytes([2; 32]),
funding_satoshis: 1311768467284833366,
push_msat: 2536655962884945560,
};
let encoded_value = open_channel.encode();
let mut target_value = Vec::new();
- target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
+ target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap());
target_value.append(&mut hex::decode("02020202020202020202020202020202020202020202020202020202020202021234567890123456233403289122369832144668701144767633030896203198784335490624111800083a840000034d000c89d4c0bcc0bc031b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f024d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d076602531fe6068134503d2723133227c867ac8fa6c83c537e9a44c3c5bdbdcb1fe33703462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b0362c0a046dacce86ddd0343c6d3c7c79c2208ba0d9c9cf24a6d046d21d21f90f703f006a18d5653c4edf5391ff23a61f03ff83d237e880ee61187fa9f379a028e0a").unwrap());
if random_bit {
target_value.append(&mut hex::decode("20").unwrap());
let (_, pubkey_6) = get_keys_from!("0606060606060606060606060606060606060606060606060606060606060606", secp_ctx);
let (_, pubkey_7) = get_keys_from!("0707070707070707070707070707070707070707070707070707070707070707", secp_ctx);
let open_channelv2 = msgs::OpenChannelV2 {
- chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
+ chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
temporary_channel_id: ChannelId::from_bytes([2; 32]),
funding_feerate_sat_per_1000_weight: 821716,
commitment_feerate_sat_per_1000_weight: 821716,
};
let encoded_value = open_channelv2.encode();
let mut target_value = Vec::new();
- target_value.append(&mut hex::decode("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f").unwrap());
+ target_value.append(&mut hex::decode("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap());
target_value.append(&mut hex::decode("0202020202020202020202020202020202020202020202020202020202020202").unwrap());
target_value.append(&mut hex::decode("000c89d4").unwrap());
target_value.append(&mut hex::decode("000c89d4").unwrap());
#[test]
fn encoding_init() {
- let mainnet_hash = ChainHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap();
+ let mainnet_hash = ChainHash::using_genesis_block(Network::Bitcoin);
assert_eq!(msgs::Init {
features: InitFeatures::from_le_bytes(vec![0xFF, 0xFF, 0xFF]),
networks: Some(vec![mainnet_hash]),
for (first_blocknum, number_of_blocks, expected) in tests.into_iter() {
let sut = msgs::QueryChannelRange {
- chain_hash: BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(),
+ chain_hash: ChainHash::using_genesis_block(Network::Regtest),
first_blocknum,
number_of_blocks,
};
#[test]
fn encoding_query_channel_range() {
let mut query_channel_range = msgs::QueryChannelRange {
- chain_hash: BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap(),
+ chain_hash: ChainHash::using_genesis_block(Network::Regtest),
first_blocknum: 100000,
number_of_blocks: 1500,
};
let encoded_value = query_channel_range.encode();
- let target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000186a0000005dc").unwrap();
+ let target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000186a0000005dc").unwrap();
assert_eq!(encoded_value, target_value);
query_channel_range = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
}
fn do_encoding_reply_channel_range(encoding_type: u8) {
- let mut target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000b8a06000005dc01").unwrap();
- let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap();
+ let mut target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f000b8a06000005dc01").unwrap();
+ let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest);
let mut reply_channel_range = msgs::ReplyChannelRange {
chain_hash: expected_chain_hash,
first_blocknum: 756230,
}
fn do_encoding_query_short_channel_ids(encoding_type: u8) {
- let mut target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206").unwrap();
- let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap();
+ let mut target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap();
+ let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest);
let mut query_short_channel_ids = msgs::QueryShortChannelIds {
chain_hash: expected_chain_hash,
short_channel_ids: vec![0x0000000000008e, 0x0000000000003c69, 0x000000000045a6c4],
#[test]
fn encoding_reply_short_channel_ids_end() {
- let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap();
+ let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest);
let mut reply_short_channel_ids_end = msgs::ReplyShortChannelIdsEnd {
chain_hash: expected_chain_hash,
full_information: true,
};
let encoded_value = reply_short_channel_ids_end.encode();
- let target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e220601").unwrap();
+ let target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f01").unwrap();
assert_eq!(encoded_value, target_value);
reply_short_channel_ids_end = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
#[test]
fn encoding_gossip_timestamp_filter(){
- let expected_chain_hash = BlockHash::from_hex("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f").unwrap();
+ let expected_chain_hash = ChainHash::using_genesis_block(Network::Regtest);
let mut gossip_timestamp_filter = msgs::GossipTimestampFilter {
chain_hash: expected_chain_hash,
first_timestamp: 1590000000,
timestamp_range: 0xffff_ffff,
};
let encoded_value = gossip_timestamp_filter.encode();
- let target_value = hex::decode("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e22065ec57980ffffffff").unwrap();
+ let target_value = hex::decode("06226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f5ec57980ffffffff").unwrap();
assert_eq!(encoded_value, target_value);
gossip_timestamp_filter = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
#[test]
#[cfg(feature = "std")]
fn test_socket_address_from_str() {
- assert_eq!(SocketAddress::TcpIpV4 {
+ let tcpip_v4 = SocketAddress::TcpIpV4 {
addr: Ipv4Addr::new(127, 0, 0, 1).octets(),
port: 1234,
- }, SocketAddress::from_str("127.0.0.1:1234").unwrap());
+ };
+ assert_eq!(tcpip_v4, SocketAddress::from_str("127.0.0.1:1234").unwrap());
+ assert_eq!(tcpip_v4, SocketAddress::from_str(&tcpip_v4.to_string()).unwrap());
- assert_eq!(SocketAddress::TcpIpV6 {
+ let tcpip_v6 = SocketAddress::TcpIpV6 {
addr: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).octets(),
port: 1234,
- }, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap());
- assert_eq!(
- SocketAddress::Hostname {
+ };
+ assert_eq!(tcpip_v6, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap());
+ assert_eq!(tcpip_v6, SocketAddress::from_str(&tcpip_v6.to_string()).unwrap());
+
+ let hostname = SocketAddress::Hostname {
hostname: Hostname::try_from("lightning-node.mydomain.com".to_string()).unwrap(),
port: 1234,
- }, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap());
- assert_eq!(
- SocketAddress::Hostname {
- hostname: Hostname::try_from("example.com".to_string()).unwrap(),
- port: 1234,
- }, SocketAddress::from_str("example.com:1234").unwrap());
- assert_eq!(SocketAddress::OnionV3 {
+ };
+ assert_eq!(hostname, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap());
+ assert_eq!(hostname, SocketAddress::from_str(&hostname.to_string()).unwrap());
+
+ let onion_v2 = SocketAddress::OnionV2 ([40, 4, 64, 185, 202, 19, 162, 75, 90, 200, 38, 7],);
+ assert_eq!("OnionV2([40, 4, 64, 185, 202, 19, 162, 75, 90, 200, 38, 7])", &onion_v2.to_string());
+ assert_eq!(Err(SocketAddressParseError::InvalidOnionV3), SocketAddress::from_str("FACEBOOKCOREWWWI.onion:9735"));
+
+ let onion_v3 = SocketAddress::OnionV3 {
ed25519_pubkey: [37, 24, 75, 5, 25, 73, 117, 194, 139, 102, 182, 107, 4, 105, 247, 246, 85,
111, 177, 172, 49, 137, 167, 155, 64, 221, 163, 47, 31, 33, 71, 3],
checksum: 48326,
version: 121,
port: 1234
- }, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap());
+ };
+ assert_eq!(onion_v3, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap());
+ assert_eq!(onion_v3, SocketAddress::from_str(&onion_v3.to_string()).unwrap());
+
assert_eq!(Err(SocketAddressParseError::InvalidOnionV3), SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234"));
assert_eq!(Err(SocketAddressParseError::InvalidInput), SocketAddress::from_str("127.0.0.1@1234"));
assert_eq!(Err(SocketAddressParseError::InvalidInput), "".parse::<SocketAddress>());
assert!("invalid-address".parse::<SocketAddress>().is_err());
assert!(SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:1234").is_err());
}
+
+ #[test]
+ #[cfg(feature = "std")]
+ fn test_socket_address_to_socket_addrs() {
+ assert_eq!(SocketAddress::TcpIpV4 {addr:[0u8; 4], port: 1337,}.to_socket_addrs().unwrap().next().unwrap(),
+ SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0,0,0,0), 1337)));
+ assert_eq!(SocketAddress::TcpIpV6 {addr:[0u8; 16], port: 1337,}.to_socket_addrs().unwrap().next().unwrap(),
+ SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from([0u8; 16]), 1337, 0, 0)));
+ assert_eq!(SocketAddress::Hostname { hostname: Hostname::try_from("0.0.0.0".to_string()).unwrap(), port: 0 }
+ .to_socket_addrs().unwrap().next().unwrap(), SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from([0u8; 4]),0)));
+ assert!(SocketAddress::OnionV2([0u8; 12]).to_socket_addrs().is_err());
+ assert!(SocketAddress::OnionV3{ ed25519_pubkey: [37, 24, 75, 5, 25, 73, 117, 194, 139, 102,
+ 182, 107, 4, 105, 247, 246, 85, 111, 177, 172, 49, 137, 167, 155, 64, 221, 163, 47, 31,
+ 33, 71, 3],
+ checksum: 48326,
+ version: 121,
+ port: 1234 }.to_socket_addrs().is_err());
+ }
}
use crate::util::config::{UserConfig, ChannelConfig, MaxDustHTLCExposure};
use crate::util::errors::APIError;
-use bitcoin::hash_types::BlockHash;
-
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
impl msgs::ChannelUpdate {
fn dummy(short_channel_id: u64) -> msgs::ChannelUpdate {
+ use bitcoin::hash_types::BlockHash;
use bitcoin::secp256k1::ffi::Signature as FFISignature;
use bitcoin::secp256k1::ecdsa::Signature;
msgs::ChannelUpdate {
signature: Signature::from(unsafe { FFISignature::new() }),
contents: msgs::UnsignedChannelUpdate {
- chain_hash: BlockHash::hash(&vec![0u8][..]),
+ chain_hash: ChainHash::from(BlockHash::hash(&vec![0u8][..]).as_ref()),
short_channel_id,
timestamp: 0,
flags: 0,
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(*channel_to_update_counterparty, TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, PAYMENT_AMT)
};
let phantom_pubkey = $nodes[1].keys_manager.get_node_id(Recipient::PhantomNode).unwrap();
let phantom_route_hint = $nodes[1].node.get_phantom_route_hints();
let payment_params = PaymentParameters::from_node_id(phantom_pubkey, TEST_FINAL_CLTV)
- .with_bolt11_features($nodes[1].node.invoice_features()).unwrap()
+ .with_bolt11_features($nodes[1].node.bolt11_invoice_features()).unwrap()
.with_route_hints(vec![RouteHint(vec![
RouteHintHop {
src_node_id: $nodes[0].node.get_our_node_id(),
use core::fmt::{self, Display, Formatter};
use core::ops::Deref;
+use core::time::Duration;
use crate::prelude::*;
use crate::sync::Mutex;
/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred
pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7;
-/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until an invoice request without
-/// a response is timed out.
-///
-/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred
-const INVOICE_REQUEST_TIMEOUT_TICKS: u8 = 3;
-
/// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102
/// and later, also stores information for retrying the payment.
pub(crate) enum PendingOutboundPayment {
session_privs: HashSet<[u8; 32]>,
},
AwaitingInvoice {
- timer_ticks_without_response: u8,
+ expiration: StaleExpiration,
retry_strategy: Retry,
max_total_routing_fee_msat: Option<u64>,
},
}
}
+/// How long before a [`PendingOutboundPayment::AwaitingInvoice`] should be considered stale and
+/// candidate for removal in [`OutboundPayments::remove_stale_payments`].
+#[derive(Clone, Copy)]
+pub(crate) enum StaleExpiration {
+ /// Number of times [`OutboundPayments::remove_stale_payments`] is called.
+ TimerTicks(u64),
+ /// Duration since the Unix epoch.
+ AbsoluteTimeout(core::time::Duration),
+}
+
+impl_writeable_tlv_based_enum!(StaleExpiration,
+ ;
+ (0, TimerTicks),
+ (2, AbsoluteTimeout)
+);
+
/// Indicates an immediate error on [`ChannelManager::send_payment`]. Further errors may be
/// surfaced later via [`Event::PaymentPathFailed`] and [`Event::PaymentFailed`].
///
(payment, onion_session_privs)
}
- #[allow(unused)]
pub(super) fn add_new_awaiting_invoice(
- &self, payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
+ &self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
+ max_total_routing_fee_msat: Option<u64>
) -> Result<(), ()> {
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
match pending_outbounds.entry(payment_id) {
hash_map::Entry::Occupied(_) => Err(()),
hash_map::Entry::Vacant(entry) => {
entry.insert(PendingOutboundPayment::AwaitingInvoice {
- timer_ticks_without_response: 0,
+ expiration,
retry_strategy,
max_total_routing_fee_msat,
});
}
pub(super) fn remove_stale_payments(
- &self, pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>)
+ &self, duration_since_epoch: Duration,
+ pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>)
{
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
- #[cfg(not(invreqfailed))]
- let pending_events = pending_events.lock().unwrap();
- #[cfg(invreqfailed)]
let mut pending_events = pending_events.lock().unwrap();
- pending_outbound_payments.retain(|payment_id, payment| {
+ pending_outbound_payments.retain(|payment_id, payment| match payment {
// If an outbound payment was completed, and no pending HTLCs remain, we should remove it
// from the map. However, if we did that immediately when the last payment HTLC is claimed,
// this could race the user making a duplicate send_payment call and our idempotency
// removal. This should be more than sufficient to ensure the idempotency of any
// `send_payment` calls that were made at the same time the `PaymentSent` event was being
// processed.
- if let PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment {
+ PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } => {
let mut no_remaining_entries = session_privs.is_empty();
if no_remaining_entries {
for (ev, _) in pending_events.iter() {
*timer_ticks_without_htlcs = 0;
true
}
- } else if let PendingOutboundPayment::AwaitingInvoice { timer_ticks_without_response, .. } = payment {
- *timer_ticks_without_response += 1;
- if *timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS {
- true
- } else {
- #[cfg(invreqfailed)]
+ },
+ PendingOutboundPayment::AwaitingInvoice { expiration, .. } => {
+ let is_stale = match expiration {
+ StaleExpiration::AbsoluteTimeout(absolute_expiry) => {
+ *absolute_expiry <= duration_since_epoch
+ },
+ StaleExpiration::TimerTicks(timer_ticks_remaining) => {
+ if *timer_ticks_remaining > 0 {
+ *timer_ticks_remaining -= 1;
+ false
+ } else {
+ true
+ }
+ },
+ };
+ if is_stale {
pending_events.push_back(
(events::Event::InvoiceRequestFailed { payment_id: *payment_id }, None)
);
false
+ } else {
+ true
}
- } else { true }
+ },
+ _ => true,
});
}
payment.remove();
}
} else if let PendingOutboundPayment::AwaitingInvoice { .. } = payment.get() {
- #[cfg(invreqfailed)]
pending_events.lock().unwrap().push_back((events::Event::InvoiceRequestFailed {
payment_id,
}, None));
(2, payment_hash, required),
},
(5, AwaitingInvoice) => {
- (0, timer_ticks_without_response, required),
+ (0, expiration, required),
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
},
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
+ use core::time::Duration;
+
use crate::events::{Event, PathFailure, PaymentFailureReason};
use crate::ln::PaymentHash;
use crate::ln::channelmanager::{PaymentId, RecipientOnionFields};
use crate::ln::features::{ChannelFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError};
- use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure};
- #[cfg(invreqfailed)]
- use crate::ln::outbound_payment::INVOICE_REQUEST_TIMEOUT_TICKS;
+ use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure, StaleExpiration};
+ #[cfg(feature = "std")]
use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY;
use crate::offers::offer::OfferBuilder;
use crate::offers::test_utils::*;
}
#[test]
- #[cfg(invreqfailed)]
- fn removes_stale_awaiting_invoice() {
+ fn removes_stale_awaiting_invoice_using_absolute_timeout() {
+ let pending_events = Mutex::new(VecDeque::new());
+ let outbound_payments = OutboundPayments::new();
+ let payment_id = PaymentId([0; 32]);
+ let absolute_expiry = 100;
+ let tick_interval = 10;
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(absolute_expiry));
+
+ assert!(!outbound_payments.has_pending_payments());
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
+ for seconds_since_epoch in (0..absolute_expiry).step_by(tick_interval) {
+ let duration_since_epoch = Duration::from_secs(seconds_since_epoch);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
+ assert!(outbound_payments.has_pending_payments());
+ assert!(pending_events.lock().unwrap().is_empty());
+ }
+
+ let duration_since_epoch = Duration::from_secs(absolute_expiry);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
+ assert!(!outbound_payments.has_pending_payments());
+ assert!(!pending_events.lock().unwrap().is_empty());
+ assert_eq!(
+ pending_events.lock().unwrap().pop_front(),
+ Some((Event::InvoiceRequestFailed { payment_id }, None)),
+ );
+ assert!(pending_events.lock().unwrap().is_empty());
+
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
+ );
+ assert!(outbound_payments.has_pending_payments());
+
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_err()
+ );
+ }
+
+ #[test]
+ fn removes_stale_awaiting_invoice_using_timer_ticks() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
+ let timer_ticks = 3;
+ let expiration = StaleExpiration::TimerTicks(timer_ticks);
assert!(!outbound_payments.has_pending_payments());
assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
- for _ in 0..INVOICE_REQUEST_TIMEOUT_TICKS {
- outbound_payments.remove_stale_payments(&pending_events);
+ for i in 0..timer_ticks {
+ let duration_since_epoch = Duration::from_secs(i * 60);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
assert!(outbound_payments.has_pending_payments());
assert!(pending_events.lock().unwrap().is_empty());
}
- outbound_payments.remove_stale_payments(&pending_events);
+ let duration_since_epoch = Duration::from_secs(timer_ticks * 60);
+ outbound_payments.remove_stale_payments(duration_since_epoch, &pending_events);
+
assert!(!outbound_payments.has_pending_payments());
assert!(!pending_events.lock().unwrap().is_empty());
assert_eq!(
assert!(pending_events.lock().unwrap().is_empty());
assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None)
- .is_err()
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_err()
);
}
#[test]
- #[cfg(invreqfailed)]
fn removes_abandoned_awaiting_invoice() {
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
assert!(!outbound_payments.has_pending_payments());
assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok()
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), None
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.sign(recipient_sign).unwrap();
- assert!(outbound_payments.add_new_awaiting_invoice(
- payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000))
- .is_ok()
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0),
+ Some(invoice.amount_msats() / 100 + 50_000)
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
.build().unwrap()
.sign(recipient_sign).unwrap();
- assert!(outbound_payments.add_new_awaiting_invoice(
- payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000))
- .is_ok()
+ assert!(
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0),
+ Some(invoice.amount_msats() / 100 + 50_000)
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
let pending_events = Mutex::new(VecDeque::new());
let outbound_payments = OutboundPayments::new();
let payment_id = PaymentId([0; 32]);
+ let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
let invoice = OfferBuilder::new("foo".into(), recipient_pubkey())
.amount_msats(1000)
assert!(pending_events.lock().unwrap().is_empty());
assert!(
- outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), Some(1234)).is_ok()
+ outbound_payments.add_new_awaiting_invoice(
+ payment_id, expiration, Retry::Attempts(0), Some(1234)
+ ).is_ok()
);
assert!(outbound_payments.has_pending_payments());
use crate::sign::EntropySource;
use crate::chain::transaction::OutPoint;
use crate::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentFailureReason, PaymentPurpose};
-use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
+use crate::ln::channel::{EXPIRE_PREV_CONFIG_TICKS, commit_tx_fee_msat, get_holder_selected_channel_reserve_satoshis, ANCHOR_OUTPUT_VALUE_SATOSHI};
use crate::ln::channelmanager::{BREAKDOWN_TIMEOUT, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, RecentPaymentDetails, RecipientOnionFields, HTLCForwardInfo, PendingHTLCRouting, PendingAddHTLCInfo};
-use crate::ln::features::Bolt11InvoiceFeatures;
+use crate::ln::features::{Bolt11InvoiceFeatures, ChannelTypeFeatures};
use crate::ln::{msgs, ChannelId, PaymentSecret, PaymentPreimage};
use crate::ln::msgs::ChannelMessageHandler;
use crate::ln::outbound_payment::{IDEMPOTENCY_TIMEOUT_TICKS, Retry};
let amt_msat = 1_000_000;
let max_total_routing_fee_msat = 50_000;
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(
nodes[0], nodes[3], payment_params, amt_msat, Some(max_total_routing_fee_msat));
let path = route.paths[0].clone();
let max_total_routing_fee_msat = Some(1_000_000);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let (mut route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(
nodes[0], nodes[3], payment_params, amt_msat, max_total_routing_fee_msat);
let bs_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[0]).pop().unwrap();
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish);
let as_err = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(as_err.len(), 1);
- match as_err[0] {
+ assert_eq!(as_err.len(), 2);
+ match as_err[1] {
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
assert_eq!(node_id, nodes[1].node.get_our_node_id());
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), msg);
let bs_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[0]).pop().unwrap();
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish);
let as_err = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(as_err.len(), 1);
+ assert_eq!(as_err.len(), 2);
let bs_commitment_tx;
- match as_err[0] {
+ match as_err[1] {
MessageSendEvent::HandleError { node_id, action: msgs::ErrorAction::SendErrorMessage { ref msg } } => {
assert_eq!(node_id, nodes[1].node.get_our_node_id());
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), msg);
let (payment_hash, payment_secret) = nodes[1].node.create_inbound_payment(Some(amt_msat), expiry_secs, None).unwrap();
let payment_params = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let scorer = test_utils::TestScorer::new();
let keys_manager = test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
htlc_maximum_msat: None,
}])
]).unwrap()
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap();
- let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat,);
- let route = get_route(&nodes[0].node.get_our_node_id(), &route_params,
- &nodes[0].network_graph.read_only(), None, nodes[0].logger, &scorer, &Default::default(),
- &random_seed_bytes).unwrap();
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap();
+ let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
+ let route = get_route(
+ &nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph.read_only(), None,
+ nodes[0].logger, &scorer, &(), &random_seed_bytes
+ ).unwrap();
let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap();
nodes[0].node.send_payment_with_route(&route, payment_hash,
}
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
.with_route_hints(route_hints).unwrap()
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60, None).unwrap();
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret),
create_announced_chan_between_nodes(&nodes, 2, 3);
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[3]);
- let mut route_params = RouteParameters::from_payment_params_and_value(
- PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap(),
- 10_000_000);
+ let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
+ let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 10_000_000);
let mut route = nodes[0].router.find_route(&nodes[0].node.get_our_node_id(), &route_params,
None, nodes[0].node.compute_inflight_htlcs()).unwrap();
// Make sure the route is ordered as the B->D path before C->D
let chan_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 100_000, 0);
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[3].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[3].node.bolt11_invoice_features()).unwrap();
let mut route = get_route!(nodes[0], payment_params, 15_000_000).unwrap();
assert_eq!(route.paths.len(), 2);
route.paths.sort_by(|path_a, _| {
let payment_metadata = vec![44, 49, 52, 142];
let payment_params = PaymentParameters::from_node_id(nodes[3].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, amt_msat);
// Send the MPP payment, delivering the updated commitment state to nodes[1].
do_test_payment_metadata_consistency(false, true);
do_test_payment_metadata_consistency(false, false);
}
+
+#[test]
+fn test_htlc_forward_considers_anchor_outputs_value() {
+ // Tests that:
+ //
+ // 1) Forwarding nodes don't forward HTLCs that would cause their balance to dip below the
+ // reserve when considering the value of anchor outputs.
+ //
+ // 2) Recipients of `update_add_htlc` properly reject HTLCs that would cause the initiator's
+ // balance to dip below the reserve when considering the value of anchor outputs.
+ let mut config = test_default_channel_config();
+ config.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
+ config.manually_accept_inbound_channels = true;
+ config.channel_config.forwarding_fee_base_msat = 0;
+ config.channel_config.forwarding_fee_proportional_millionths = 0;
+
+ // Set up a test network of three nodes that replicates a production failure leading to the
+ // discovery of this bug.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(config), Some(config), Some(config)]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ const CHAN_AMT: u64 = 1_000_000;
+ const PUSH_MSAT: u64 = 900_000_000;
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, CHAN_AMT, 500_000_000);
+ let (_, _, chan_id_2, _) = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, CHAN_AMT, PUSH_MSAT);
+
+ let channel_reserve_msat = get_holder_selected_channel_reserve_satoshis(CHAN_AMT, &config) * 1000;
+ let commitment_fee_msat = commit_tx_fee_msat(
+ *nodes[1].fee_estimator.sat_per_kw.lock().unwrap(), 2, &ChannelTypeFeatures::anchors_zero_htlc_fee_and_dependencies()
+ );
+ let anchor_outpus_value_msat = ANCHOR_OUTPUT_VALUE_SATOSHI * 2 * 1000;
+ let sendable_balance_msat = CHAN_AMT * 1000 - PUSH_MSAT - channel_reserve_msat - commitment_fee_msat - anchor_outpus_value_msat;
+ let channel_details = nodes[1].node.list_channels().into_iter().find(|channel| channel.channel_id == chan_id_2).unwrap();
+ assert!(sendable_balance_msat >= channel_details.next_outbound_htlc_minimum_msat);
+ assert!(sendable_balance_msat <= channel_details.next_outbound_htlc_limit_msat);
+
+ send_payment(&nodes[0], &[&nodes[1], &nodes[2]], sendable_balance_msat);
+ send_payment(&nodes[2], &[&nodes[1], &nodes[0]], sendable_balance_msat);
+
+ // Send out an HTLC that would cause the forwarding node to dip below its reserve when
+ // considering the value of anchor outputs.
+ let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(
+ nodes[0], nodes[2], sendable_balance_msat + anchor_outpus_value_msat
+ );
+ nodes[0].node.send_payment_with_route(
+ &route, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
+ ).unwrap();
+ check_added_monitors!(nodes[0], 1);
+
+ let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ let mut update_add_htlc = if let MessageSendEvent::UpdateHTLCs { updates, .. } = events.pop().unwrap() {
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
+ check_added_monitors(&nodes[1], 0);
+ commitment_signed_dance!(nodes[1], nodes[0], &updates.commitment_signed, false);
+ updates.update_add_htlcs[0].clone()
+ } else {
+ panic!("Unexpected event");
+ };
+
+ // The forwarding node should reject forwarding it as expected.
+ expect_pending_htlcs_forwardable!(nodes[1]);
+ expect_pending_htlcs_forwardable_and_htlc_handling_failed!(&nodes[1], vec![HTLCDestination::NextHopChannel {
+ node_id: Some(nodes[2].node.get_our_node_id()),
+ channel_id: chan_id_2
+ }]);
+ check_added_monitors(&nodes[1], 1);
+
+ let mut events = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ if let MessageSendEvent::UpdateHTLCs { updates, .. } = events.pop().unwrap() {
+ nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
+ check_added_monitors(&nodes[0], 0);
+ commitment_signed_dance!(nodes[0], nodes[1], &updates.commitment_signed, false);
+ } else {
+ panic!("Unexpected event");
+ }
+
+ expect_payment_failed!(nodes[0], payment_hash, false);
+
+ // Assume that the forwarding node did forward it, and make sure the recipient rejects it as an
+ // invalid update and closes the channel.
+ update_add_htlc.channel_id = chan_id_2;
+ nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
+ check_closed_event(&nodes[2], 1, ClosureReason::ProcessingError {
+ err: "Remote HTLC add would put them under remote reserve value".to_owned()
+ }, false, &[nodes[1].node.get_our_node_id()], 1_000_000);
+ check_closed_broadcast(&nodes[2], 1, true);
+ check_added_monitors(&nodes[2], 1);
+}
use bitcoin::secp256k1::{self, Secp256k1, SecretKey, PublicKey};
use crate::sign::{KeysManager, NodeSigner, Recipient};
-use crate::events::{MessageSendEvent, MessageSendEventsProvider, OnionMessageProvider};
+use crate::events::{MessageSendEvent, MessageSendEventsProvider};
use crate::ln::ChannelId;
use crate::ln::features::{InitFeatures, NodeFeatures};
use crate::ln::msgs;
use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor,NextNoiseStep};
use crate::ln::wire;
use crate::ln::wire::{Encode, Type};
-use crate::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
+use crate::onion_message::{CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents, PendingOnionMessage, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
use crate::routing::gossip::{NetworkGraph, P2PGossipSync, NodeId, NodeAlias};
use crate::util::atomic_counter::AtomicCounter;
use crate::util::logger::Logger;
}
fn processing_queue_high(&self) -> bool { false }
}
-impl OnionMessageProvider for IgnoringMessageHandler {
- fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option<msgs::OnionMessage> { None }
-}
impl OnionMessageHandler for IgnoringMessageHandler {
fn handle_onion_message(&self, _their_node_id: &PublicKey, _msg: &msgs::OnionMessage) {}
+ fn next_onion_message_for_peer(&self, _peer_node_id: PublicKey) -> Option<msgs::OnionMessage> { None }
fn peer_connected(&self, _their_node_id: &PublicKey, _init: &msgs::Init, _inbound: bool) -> Result<(), ()> { Ok(()) }
fn peer_disconnected(&self, _their_node_id: &PublicKey) {}
fn provided_node_features(&self) -> NodeFeatures { NodeFeatures::empty() }
fn read_custom_message<R: io::Read>(&self, _msg_type: u64, _buffer: &mut R) -> Result<Option<Infallible>, msgs::DecodeError> where Self: Sized {
Ok(None)
}
+ fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Infallible>> {
+ vec![]
+ }
}
-impl CustomOnionMessageContents for Infallible {
+impl OnionMessageContents for Infallible {
fn tlv_type(&self) -> u64 { unreachable!(); }
}
features
}
- fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
+ fn get_chain_hashes(&self) -> Option<Vec<ChainHash>> {
// We don't enforce any chains upon peer connection for `ErroringMessageHandler` and leave it up
// to users of `ErroringMessageHandler` to make decisions on network compatiblility.
// There's not really any way to pull in specific networks here, and hardcoding can cause breakages.
SD,
Arc<SimpleArcChannelManager<M, T, F, L>>,
Arc<P2PGossipSync<Arc<NetworkGraph<Arc<L>>>, C, Arc<L>>>,
- Arc<SimpleArcOnionMessenger<L>>,
+ Arc<SimpleArcOnionMessenger<M, T, F, L>>,
Arc<L>,
IgnoringMessageHandler,
Arc<KeysManager>
///
/// This is not exported to bindings users as general type aliases don't make sense in bindings.
pub type SimpleRefPeerManager<
- 'a, 'b, 'c, 'd, 'e, 'f, 'logger, 'h, 'i, 'j, 'graph, SD, M, T, F, C, L
+ 'a, 'b, 'c, 'd, 'e, 'f, 'logger, 'h, 'i, 'j, 'graph, 'k, SD, M, T, F, C, L
> = PeerManager<
SD,
&'j SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'graph, 'logger, 'i, M, T, F, L>,
&'f P2PGossipSync<&'graph NetworkGraph<&'logger L>, C, &'logger L>,
- &'h SimpleRefOnionMessenger<'logger, 'i, 'j, L>,
+ &'h SimpleRefOnionMessenger<'a, 'b, 'c, 'd, 'e, 'graph, 'logger, 'i, 'j, 'k, M, T, F, L>,
&'logger L,
IgnoringMessageHandler,
&'c KeysManager
peer.set_their_node_id(their_node_id);
insert_node_id!();
let features = self.init_features(&their_node_id);
- let networks = self.message_handler.chan_handler.get_genesis_hashes();
+ let networks = self.message_handler.chan_handler.get_chain_hashes();
let resp = msgs::Init { features, networks, remote_network_address: filter_addresses(peer.their_socket_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
peer.set_their_node_id(their_node_id);
insert_node_id!();
let features = self.init_features(&their_node_id);
- let networks = self.message_handler.chan_handler.get_genesis_hashes();
+ let networks = self.message_handler.chan_handler.get_chain_hashes();
let resp = msgs::Init { features, networks, remote_network_address: filter_addresses(peer.their_socket_address.clone()) };
self.enqueue_message(peer, &resp);
peer.awaiting_pong_timer_tick_intervals = 0;
if let wire::Message::Init(msg) = message {
// Check if we have any compatible chains if the `networks` field is specified.
if let Some(networks) = &msg.networks {
- if let Some(our_chains) = self.message_handler.chan_handler.get_genesis_hashes() {
+ if let Some(our_chains) = self.message_handler.chan_handler.get_chain_hashes() {
let mut have_compatible_chains = false;
'our_chains: for our_chain in our_chains.iter() {
for their_chain in networks {
let flush_read_disabled = self.gossip_processing_backlog_lifted.swap(false, Ordering::Relaxed);
let mut peers_to_disconnect = HashMap::new();
- let mut events_generated = self.message_handler.chan_handler.get_and_clear_pending_msg_events();
- events_generated.append(&mut self.message_handler.route_handler.get_and_clear_pending_msg_events());
{
- // TODO: There are some DoS attacks here where you can flood someone's outbound send
- // buffer by doing things like announcing channels on another node. We should be willing to
- // drop optional-ish messages when send buffers get full!
-
let peers_lock = self.peers.read().unwrap();
+
+ let mut events_generated = self.message_handler.chan_handler.get_and_clear_pending_msg_events();
+ events_generated.append(&mut self.message_handler.route_handler.get_and_clear_pending_msg_events());
+
let peers = &*peers_lock;
macro_rules! get_peer_for_forwarding {
($node_id: expr) => {
use crate::ln::functional_test_utils::*;
-use bitcoin::blockdata::constants::genesis_block;
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
#[test]
}]);
let last_hops = vec![route_hint];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), TEST_FINAL_CLTV)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(last_hops).unwrap();
let (route, our_payment_hash, our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints.clone()).unwrap();
let (route, payment_hash, payment_preimage, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 100_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, last_hop[0].inbound_scid_alias.unwrap());
hop_hints[0].0[0].short_channel_id = last_hop[0].short_channel_id.unwrap();
let payment_params_2 = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
let (route_2, payment_hash_2, _, payment_secret_2) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params_2, 100_000);
assert_eq!(route_2.paths[0].hops[1].short_channel_id, last_hop[0].short_channel_id.unwrap());
htlc_minimum_msat: None,
}])];
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42)
- .with_bolt11_features(nodes[2].node.invoice_features()).unwrap()
+ .with_bolt11_features(nodes[2].node.bolt11_invoice_features()).unwrap()
.with_route_hints(hop_hints).unwrap();
let (mut route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[2], payment_params, 10_000);
assert_eq!(route.paths[0].hops[1].short_channel_id, nodes[2].node.list_usable_channels()[0].inbound_scid_alias.unwrap());
// Build the expected channel update
let contents = msgs::UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: last_hop[0].inbound_scid_alias.unwrap(),
timestamp: 21,
flags: 1,
if let MessageSendEvent::BroadcastChannelUpdate { .. } = msg {
} else if let MessageSendEvent::HandleError { ref action, .. } = msg {
match action {
- &ErrorAction::SendErrorMessage { ref msg } => {
- assert_eq!(msg.data, "Channel force-closed");
+ &ErrorAction::DisconnectPeer { ref msg } => {
+ assert_eq!(msg.as_ref().unwrap().data, "Channel force-closed");
},
_ => panic!("Unexpected event!"),
}
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &retry_reestablish[0]);
let mut err_msgs_0 = Vec::with_capacity(1);
- for msg in nodes[0].node.get_and_clear_pending_msg_events() {
- if let MessageSendEvent::HandleError { ref action, .. } = msg {
- match action {
- &ErrorAction::SendErrorMessage { ref msg } => {
- assert_eq!(msg.data, format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()));
- err_msgs_0.push(msg.clone());
- },
- _ => panic!("Unexpected event!"),
- }
- } else {
- panic!("Unexpected event!");
+ if let MessageSendEvent::HandleError { ref action, .. } = nodes[0].node.get_and_clear_pending_msg_events()[1] {
+ match action {
+ &ErrorAction::SendErrorMessage { ref msg } => {
+ assert_eq!(msg.data, format!("Got a message for a channel from the wrong node! No such channel for the passed counterparty_node_id {}", &nodes[1].node.get_our_node_id()));
+ err_msgs_0.push(msg.clone());
+ },
+ _ => panic!("Unexpected event!"),
}
+ } else {
+ panic!("Unexpected event!");
}
assert_eq!(err_msgs_0.len(), 1);
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), &err_msgs_0[0]);
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[0]);
- let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV).with_bolt11_features(nodes[1].node.invoice_features()).unwrap();
+ let payment_params_1 = PaymentParameters::from_node_id(nodes[1].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[1].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params_1, 100_000);
let route_1 = get_route(&nodes[0].node.get_our_node_id(), &route_params,
&nodes[0].network_graph.read_only(), None, &logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
- let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(),
- TEST_FINAL_CLTV).with_bolt11_features(nodes[0].node.invoice_features()).unwrap();
+ let payment_params_2 = PaymentParameters::from_node_id(nodes[0].node.get_our_node_id(), TEST_FINAL_CLTV)
+ .with_bolt11_features(nodes[0].node.bolt11_invoice_features()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(payment_params_2, 100_000);
let route_2 = get_route(&nodes[1].node.get_our_node_id(), &route_params,
&nodes[1].network_graph.read_only(), None, &logger, &scorer, &Default::default(), &random_seed_bytes).unwrap();
nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &node_1_2nd_reestablish);
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(msg_events.len(), 1);
- if let MessageSendEvent::HandleError { ref action, .. } = msg_events[0] {
+ assert_eq!(msg_events.len(), 2);
+ if let MessageSendEvent::HandleError { ref action, .. } = msg_events[1] {
match action {
&ErrorAction::SendErrorMessage { ref msg } => {
nodes[1].node.handle_error(&nodes[0].node.get_our_node_id(), &msg);
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
created_at: Duration, payment_hash: PaymentHash
) -> Result<Self, Bolt12SemanticError> {
- let amount_msats = Self::check_amount_msats(invoice_request)?;
+ let amount_msats = Self::amount_msats(invoice_request)?;
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
let contents = InvoiceContents::ForOffer {
invoice_request: invoice_request.contents.clone(),
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
) -> Result<Self, Bolt12SemanticError> {
- let amount_msats = Self::check_amount_msats(invoice_request)?;
+ let amount_msats = Self::amount_msats(invoice_request)?;
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
let contents = InvoiceContents::ForOffer {
invoice_request: invoice_request.contents.clone(),
}
impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
- fn check_amount_msats(invoice_request: &InvoiceRequest) -> Result<u64, Bolt12SemanticError> {
+ pub(crate) fn amount_msats(
+ invoice_request: &InvoiceRequest
+ ) -> Result<u64, Bolt12SemanticError> {
match invoice_request.amount_msats() {
Some(amount_msats) => Ok(amount_msats),
None => match invoice_request.contents.inner.offer.amount() {
}
}
+ #[cfg(not(feature = "std"))] {
+ if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
let InvoiceBuilder { invreq_bytes, invoice, .. } = self;
Ok(UnsignedBolt12Invoice::new(invreq_bytes, invoice))
}
}
}
+ #[cfg(not(feature = "std"))] {
+ if self.invoice.is_offer_or_refund_expired_no_std(self.invoice.created_at()) {
+ return Err(Bolt12SemanticError::AlreadyExpired);
+ }
+ }
+
let InvoiceBuilder {
invreq_bytes, invoice, signing_pubkey_strategy: DerivedSigningPubkey(keys)
} = self;
}
}
+ #[cfg(not(feature = "std"))]
+ fn is_offer_or_refund_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+ match self {
+ InvoiceContents::ForOffer { invoice_request, .. } =>
+ invoice_request.inner.offer.is_expired_no_std(duration_since_epoch),
+ InvoiceContents::ForRefund { refund, .. } =>
+ refund.is_expired_no_std(duration_since_epoch),
+ }
+ }
+
fn offer_chains(&self) -> Option<Vec<ChainHash>> {
match self {
InvoiceContents::ForOffer { invoice_request, .. } =>
pub suggested_value: Option<Vec<u8>>,
}
+impl InvoiceError {
+ /// Creates an [`InvoiceError`] with the given message.
+ pub fn from_str(s: &str) -> Self {
+ Self {
+ erroneous_field: None,
+ message: UntrustedString(s.to_string()),
+ }
+ }
+}
+
impl core::fmt::Display for InvoiceError {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
self.message.fmt(f)
/// by the offer.
///
/// Successive calls to this method will override the previous setting.
- pub fn chain(mut self, network: Network) -> Result<Self, Bolt12SemanticError> {
- let chain = ChainHash::using_genesis_block(network);
+ pub fn chain(self, network: Network) -> Result<Self, Bolt12SemanticError> {
+ self.chain_hash(ChainHash::using_genesis_block(network))
+ }
+
+ /// Sets the [`InvoiceRequest::chain`] for paying an invoice. If not called, the chain hash of
+ /// [`Network::Bitcoin`] is assumed. Errors if the chain for `network` is not supported by the
+ /// offer.
+ ///
+ /// Successive calls to this method will override the previous setting.
+ pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Result<Self, Bolt12SemanticError> {
if !self.offer.supports_chain(chain) {
return Err(Bolt12SemanticError::UnsupportedChain);
}
//! published as a QR code to be scanned by a customer. The customer uses the offer to request an
//! invoice from the merchant to be paid.
//!
+//! # Example
+//!
//! ```
//! extern crate bitcoin;
//! extern crate core;
//! # Ok(())
//! # }
//! ```
+//!
+//! # Note
+//!
+//! If constructing an [`Offer`] for use with a [`ChannelManager`], use
+//! [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`].
+//!
+//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+//! [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
/// while the offer is valid.
///
/// Use a different pubkey per offer to avoid correlating offers.
+ ///
+ /// # Note
+ ///
+ /// If constructing an [`Offer`] for use with a [`ChannelManager`], use
+ /// [`ChannelManager::create_offer_builder`] instead of [`OfferBuilder::new`].
+ ///
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+ /// [`ChannelManager::create_offer_builder`]: crate::ln::channelmanager::ChannelManager::create_offer_builder
pub fn new(description: String, signing_pubkey: PublicKey) -> Self {
OfferBuilder {
offer: OfferContents {
/// See [`Offer::chains`] on how this relates to the payment currency.
///
/// Successive calls to this method will add another chain hash.
- pub fn chain(mut self, network: Network) -> Self {
+ pub fn chain(self, network: Network) -> Self {
+ self.chain_hash(ChainHash::using_genesis_block(network))
+ }
+
+ /// Adds the [`ChainHash`] to [`Offer::chains`]. If not called, the chain hash of
+ /// [`Network::Bitcoin`] is assumed to be the only one supported.
+ ///
+ /// See [`Offer::chains`] on how this relates to the payment currency.
+ ///
+ /// Successive calls to this method will add another chain hash.
+ pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
let chains = self.offer.chains.get_or_insert_with(Vec::new);
- let chain = ChainHash::using_genesis_block(network);
if !chains.contains(&chain) {
chains.push(chain);
}
#[cfg(feature = "std")]
pub(super) fn is_expired(&self) -> bool {
- match self.absolute_expiry {
- Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
- Ok(elapsed) => elapsed > seconds_from_epoch,
- Err(_) => false,
- },
- None => false,
- }
+ SystemTime::UNIX_EPOCH
+ .elapsed()
+ .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
+ .unwrap_or(false)
+ }
+
+ pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+ self.absolute_expiry
+ .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
+ .unwrap_or(false)
}
pub fn issuer(&self) -> Option<PrintableString> {
MissingPayerMetadata,
/// A payer id was expected but was missing.
MissingPayerId,
+ /// The payment id for a refund or request is already in use.
+ DuplicatePaymentId,
/// Blinded paths were expected but were missing.
MissingPaths,
/// The blinded payinfo given does not match the number of blinded path hops.
//! [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
//! [`Offer`]: crate::offers::offer::Offer
//!
+//! # Example
+//!
//! ```
//! extern crate bitcoin;
//! extern crate core;
//! # Ok(())
//! # }
//! ```
+//!
+//! # Note
+//!
+//! If constructing a [`Refund`] for use with a [`ChannelManager`], use
+//! [`ChannelManager::create_refund_builder`] instead of [`RefundBuilder::new`].
+//!
+//! [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+//! [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
///
/// Additionally, sets the required [`Refund::description`], [`Refund::payer_metadata`], and
/// [`Refund::amount_msats`].
+ ///
+ /// # Note
+ ///
+ /// If constructing a [`Refund`] for use with a [`ChannelManager`], use
+ /// [`ChannelManager::create_refund_builder`] instead of [`RefundBuilder::new`].
+ ///
+ /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
+ /// [`ChannelManager::create_refund_builder`]: crate::ln::channelmanager::ChannelManager::create_refund_builder
pub fn new(
description: String, metadata: Vec<u8>, payer_id: PublicKey, amount_msats: u64
) -> Result<Self, Bolt12SemanticError> {
/// called, [`Network::Bitcoin`] is assumed.
///
/// Successive calls to this method will override the previous setting.
- pub fn chain(mut self, network: Network) -> Self {
- self.refund.chain = Some(ChainHash::using_genesis_block(network));
+ pub fn chain(self, network: Network) -> Self {
+ self.chain_hash(ChainHash::using_genesis_block(network))
+ }
+
+ /// Sets the [`Refund::chain`] of the given [`ChainHash`] for paying an invoice. If not called,
+ /// [`Network::Bitcoin`] is assumed.
+ ///
+ /// Successive calls to this method will override the previous setting.
+ pub(crate) fn chain_hash(mut self, chain: ChainHash) -> Self {
+ self.refund.chain = Some(chain);
self
}
#[cfg(feature = "std")]
pub(super) fn is_expired(&self) -> bool {
- match self.absolute_expiry {
- Some(seconds_from_epoch) => match SystemTime::UNIX_EPOCH.elapsed() {
- Ok(elapsed) => elapsed > seconds_from_epoch,
- Err(_) => false,
- },
- None => false,
- }
+ SystemTime::UNIX_EPOCH
+ .elapsed()
+ .map(|duration_since_epoch| self.is_expired_no_std(duration_since_epoch))
+ .unwrap_or(false)
+ }
+
+ pub(super) fn is_expired_no_std(&self, duration_since_epoch: Duration) -> bool {
+ self.absolute_expiry
+ .map(|absolute_expiry| duration_since_epoch > absolute_expiry)
+ .unwrap_or(false)
}
pub fn issuer(&self) -> Option<PrintableString> {
//! Onion message testing and test utilities live here.
use crate::blinded_path::BlindedPath;
-use crate::events::OnionMessageProvider;
use crate::ln::features::InitFeatures;
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
use crate::sign::{NodeSigner, Recipient};
use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
use crate::util::test_utils;
-use super::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError};
+use super::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage, SendError};
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
const CUSTOM_REQUEST_MESSAGE_CONTENTS: [u8; 32] = [42; 32];
const CUSTOM_RESPONSE_MESSAGE_CONTENTS: [u8; 32] = [43; 32];
-impl CustomOnionMessageContents for TestCustomMessage {
+impl OnionMessageContents for TestCustomMessage {
fn tlv_type(&self) -> u64 {
match self {
TestCustomMessage::Request => CUSTOM_REQUEST_MESSAGE_TYPE,
_ => Ok(None),
}
}
+ fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>> {
+ vec![]
+ }
}
fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
}
#[test]
-fn one_hop() {
+fn one_unblinded_hop() {
let nodes = create_nodes(2);
- let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
+ let test_msg = TestCustomMessage::Response;
let path = OnionMessagePath {
intermediate_nodes: vec![],
#[test]
fn two_unblinded_hops() {
let nodes = create_nodes(3);
- let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
+ let test_msg = TestCustomMessage::Response;
let path = OnionMessagePath {
intermediate_nodes: vec![nodes[1].get_node_pk()],
pass_along_path(&nodes);
}
+#[test]
+fn one_blinded_hop() {
+ let nodes = create_nodes(2);
+ let test_msg = TestCustomMessage::Response;
+
+ let secp_ctx = Secp256k1::new();
+ let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk()], &*nodes[1].keys_manager, &secp_ctx).unwrap();
+ let path = OnionMessagePath {
+ intermediate_nodes: vec![],
+ destination: Destination::BlindedPath(blinded_path),
+ };
+ nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap();
+ nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
+ pass_along_path(&nodes);
+}
+
#[test]
fn two_unblinded_two_blinded() {
let nodes = create_nodes(5);
- let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
+ let test_msg = TestCustomMessage::Response;
let secp_ctx = Secp256k1::new();
let blinded_path = BlindedPath::new_for_message(&[nodes[3].get_node_pk(), nodes[4].get_node_pk()], &*nodes[4].keys_manager, &secp_ctx).unwrap();
#[test]
fn three_blinded_hops() {
let nodes = create_nodes(4);
- let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
+ let test_msg = TestCustomMessage::Response;
let secp_ctx = Secp256k1::new();
let blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk(), nodes[3].get_node_pk()], &*nodes[3].keys_manager, &secp_ctx).unwrap();
fn too_big_packet_error() {
// Make sure we error as expected if a packet is too big to send.
let nodes = create_nodes(2);
- let test_msg = OnionMessageContents::Custom(TestCustomMessage::Response);
+ let test_msg = TestCustomMessage::Response;
let hop_node_id = nodes[1].get_node_pk();
let hops = vec![hop_node_id; 400];
destination: Destination::BlindedPath(blinded_path),
};
- nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), None).unwrap();
+ nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap();
nodes[2].custom_message_handler.expect_message(TestCustomMessage::Response);
pass_along_path(&nodes);
intermediate_nodes: vec![],
destination: Destination::BlindedPath(blinded_path),
};
- nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap();
+ nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap();
nodes[1].custom_message_handler.expect_message(TestCustomMessage::Response);
nodes.remove(2);
pass_along_path(&nodes);
intermediate_nodes: vec![],
destination: Destination::BlindedPath(blinded_path),
};
- let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), None).unwrap_err();
- assert_eq!(err, SendError::TooFewBlindedHops);
-
- // 1 hop
- let mut blinded_path = BlindedPath::new_for_message(&[nodes[1].get_node_pk(), nodes[2].get_node_pk()], &*nodes[2].keys_manager, &secp_ctx).unwrap();
- blinded_path.blinded_hops.remove(0);
- assert_eq!(blinded_path.blinded_hops.len(), 1);
- let path = OnionMessagePath {
- intermediate_nodes: vec![],
- destination: Destination::BlindedPath(blinded_path),
- };
- let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap_err();
+ let err = nodes[0].messenger.send_onion_message(path, test_msg.clone(), None).unwrap_err();
assert_eq!(err, SendError::TooFewBlindedHops);
}
destination: Destination::Node(nodes[3].get_node_pk()),
};
let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
- nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg.clone()), Some(reply_path)).unwrap();
+ nodes[0].messenger.send_onion_message(path, test_msg.clone(), Some(reply_path)).unwrap();
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
pass_along_path(&nodes);
// Make sure the last node successfully decoded the reply path.
};
let reply_path = BlindedPath::new_for_message(&[nodes[2].get_node_pk(), nodes[1].get_node_pk(), nodes[0].get_node_pk()], &*nodes[0].keys_manager, &secp_ctx).unwrap();
- nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), Some(reply_path)).unwrap();
+ nodes[0].messenger.send_onion_message(path, test_msg, Some(reply_path)).unwrap();
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Request);
pass_along_path(&nodes);
let nodes = create_nodes(2);
struct InvalidCustomMessage{}
- impl CustomOnionMessageContents for InvalidCustomMessage {
+ impl OnionMessageContents for InvalidCustomMessage {
fn tlv_type(&self) -> u64 {
// Onion message contents must have a TLV >= 64.
63
fn write<W: Writer>(&self, _w: &mut W) -> Result<(), io::Error> { unreachable!() }
}
- let test_msg = OnionMessageContents::Custom(InvalidCustomMessage {});
+ let test_msg = InvalidCustomMessage {};
let path = OnionMessagePath {
intermediate_nodes: vec![],
destination: Destination::Node(nodes[1].get_node_pk()),
destination: Destination::Node(nodes[1].get_node_pk()),
};
for _ in 0..188 { // Based on MAX_PER_PEER_BUFFER_SIZE in OnionMessenger
- nodes[0].messenger.send_onion_message(path.clone(), OnionMessageContents::Custom(test_msg.clone()), None).unwrap();
+ nodes[0].messenger.send_onion_message(path.clone(), test_msg.clone(), None).unwrap();
}
- let err = nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap_err();
+ let err = nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap_err();
assert_eq!(err, SendError::BufferFull);
}
intermediate_nodes,
destination: Destination::Node(nodes[num_nodes-1].get_node_pk()),
};
- nodes[0].messenger.send_onion_message(path, OnionMessageContents::Custom(test_msg), None).unwrap();
+ nodes[0].messenger.send_onion_message(path, test_msg, None).unwrap();
nodes[num_nodes-1].custom_message_handler.expect_message(TestCustomMessage::Response);
pass_along_path(&nodes);
}
use crate::blinded_path::message::{advance_path_by_one, ForwardTlvs, ReceiveTlvs};
use crate::blinded_path::utils;
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient};
-use crate::events::OnionMessageProvider;
+use crate::ln::channelmanager::{SimpleArcChannelManager, SimpleRefChannelManager};
use crate::ln::features::{InitFeatures, NodeFeatures};
-use crate::ln::msgs::{self, OnionMessageHandler};
+use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler};
use crate::ln::onion_utils;
use crate::ln::peer_handler::IgnoringMessageHandler;
-pub use super::packet::{CustomOnionMessageContents, OnionMessageContents};
+pub use super::packet::OnionMessageContents;
+use super::packet::ParsedOnionMessageContents;
use super::offers::OffersMessageHandler;
use super::packet::{BIG_PACKET_HOP_DATA_LEN, ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, SMALL_PACKET_HOP_DATA_LEN};
use crate::util::logger::Logger;
use crate::util::ser::Writeable;
+use core::fmt;
use core::ops::Deref;
use crate::io;
use crate::sync::{Arc, Mutex};
use crate::prelude::*;
-/// A sender, receiver and forwarder of onion messages. In upcoming releases, this object will be
-/// used to retrieve invoices and fulfill invoice requests from [offers]. Currently, only sending
-/// and receiving custom onion messages is supported.
+/// A sender, receiver and forwarder of [`OnionMessage`]s.
+///
+/// # Handling Messages
+///
+/// `OnionMessenger` implements [`OnionMessageHandler`], making it responsible for either forwarding
+/// messages to peers or delegating to the appropriate handler for the message type. Currently, the
+/// available handlers are:
+/// * [`OffersMessageHandler`], for responding to [`InvoiceRequest`]s and paying [`Bolt12Invoice`]s
+/// * [`CustomOnionMessageHandler`], for handling user-defined message types
+///
+/// # Sending Messages
+///
+/// [`OnionMessage`]s are sent initially using [`OnionMessenger::send_onion_message`]. When handling
+/// a message, the matched handler may return a response message which `OnionMessenger` will send
+/// on its behalf.
///
/// # Example
///
/// # use lightning::blinded_path::BlindedPath;
/// # use lightning::sign::KeysManager;
/// # use lightning::ln::peer_handler::IgnoringMessageHandler;
-/// # use lightning::onion_message::{CustomOnionMessageContents, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger};
+/// # use lightning::onion_message::{OnionMessageContents, Destination, MessageRouter, OnionMessagePath, OnionMessenger};
/// # use lightning::util::logger::{Logger, Record};
/// # use lightning::util::ser::{Writeable, Writer};
/// # use lightning::io;
/// // Write your custom onion message to `w`
/// }
/// }
-/// impl CustomOnionMessageContents for YourCustomMessage {
+/// impl OnionMessageContents for YourCustomMessage {
/// fn tlv_type(&self) -> u64 {
/// # let your_custom_message_type = 42;
/// your_custom_message_type
/// destination: Destination::Node(destination_node_id),
/// };
/// let reply_path = None;
-/// # let your_custom_message = YourCustomMessage {};
-/// let message = OnionMessageContents::Custom(your_custom_message);
+/// # let message = YourCustomMessage {};
/// onion_messenger.send_onion_message(path, message, reply_path);
///
/// // Create a blinded path to yourself, for someone to send an onion message to.
/// destination: Destination::BlindedPath(blinded_path),
/// };
/// let reply_path = None;
-/// # let your_custom_message = YourCustomMessage {};
-/// let message = OnionMessageContents::Custom(your_custom_message);
+/// # let message = YourCustomMessage {};
/// onion_messenger.send_onion_message(path, message, reply_path);
/// ```
///
-/// [offers]: <https://github.com/lightning/bolts/pull/798>
-/// [`OnionMessenger`]: crate::onion_message::OnionMessenger
+/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
+/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
pub struct OnionMessenger<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
where
ES::Target: EntropySource,
entropy_source: ES,
node_signer: NS,
logger: L,
- pending_messages: Mutex<HashMap<PublicKey, VecDeque<msgs::OnionMessage>>>,
+ pending_messages: Mutex<HashMap<PublicKey, VecDeque<OnionMessage>>>,
secp_ctx: Secp256k1<secp256k1::All>,
message_router: MR,
offers_handler: OMH,
custom_handler: CMH,
}
-/// A trait defining behavior for routing an [`OnionMessage`].
+/// An [`OnionMessage`] for [`OnionMessenger`] to send.
///
-/// [`OnionMessage`]: msgs::OnionMessage
+/// These are obtained when released from [`OnionMessenger`]'s handlers after which they are
+/// enqueued for sending.
+pub struct PendingOnionMessage<T: OnionMessageContents> {
+ /// The message contents to send in an [`OnionMessage`].
+ pub contents: T,
+
+ /// The destination of the message.
+ pub destination: Destination,
+
+ /// A reply path to include in the [`OnionMessage`] for a response.
+ pub reply_path: Option<BlindedPath>,
+}
+
+/// A trait defining behavior for routing an [`OnionMessage`].
pub trait MessageRouter {
/// Returns a route for sending an [`OnionMessage`] to the given [`Destination`].
- ///
- /// [`OnionMessage`]: msgs::OnionMessage
fn find_path(
&self, sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
) -> Result<OnionMessagePath, ()>;
}
-/// A [`MessageRouter`] that always fails.
+/// A [`MessageRouter`] that can only route to a directly connected [`Destination`].
pub struct DefaultMessageRouter;
impl MessageRouter for DefaultMessageRouter {
fn find_path(
- &self, _sender: PublicKey, _peers: Vec<PublicKey>, _destination: Destination
+ &self, _sender: PublicKey, peers: Vec<PublicKey>, destination: Destination
) -> Result<OnionMessagePath, ()> {
- Err(())
+ if peers.contains(&destination.first_node()) {
+ Ok(OnionMessagePath { intermediate_nodes: vec![], destination })
+ } else {
+ Err(())
+ }
}
}
-/// A path for sending an [`msgs::OnionMessage`].
+/// A path for sending an [`OnionMessage`].
#[derive(Clone)]
pub struct OnionMessagePath {
/// Nodes on the path between the sender and the destination.
Destination::BlindedPath(BlindedPath { blinded_hops, .. }) => blinded_hops.len(),
}
}
+
+ fn first_node(&self) -> PublicKey {
+ match self {
+ Destination::Node(node_id) => *node_id,
+ Destination::BlindedPath(BlindedPath { introduction_node_id: node_id, .. }) => *node_id,
+ }
+ }
}
/// Errors that may occur when [sending an onion message].
/// Because implementations such as Eclair will drop onion messages where the message packet
/// exceeds 32834 bytes, we refuse to send messages where the packet exceeds this size.
TooBigPacket,
- /// The provided [`Destination`] was an invalid [`BlindedPath`], due to having fewer than two
- /// blinded hops.
+ /// The provided [`Destination`] was an invalid [`BlindedPath`] due to not having any blinded
+ /// hops.
TooFewBlindedHops,
/// Our next-hop peer was offline or does not support onion message forwarding.
InvalidFirstHop,
pub trait CustomOnionMessageHandler {
/// The message known to the handler. To support multiple message types, you may want to make this
/// an enum with a variant for each supported message.
- type CustomMessage: CustomOnionMessageContents;
+ type CustomMessage: OnionMessageContents;
/// Called with the custom message that was received, returning a response to send, if any.
+ ///
+ /// The returned [`Self::CustomMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
fn handle_custom_message(&self, msg: Self::CustomMessage) -> Option<Self::CustomMessage>;
/// Read a custom message of type `message_type` from `buffer`, returning `Ok(None)` if the
/// message type is unknown.
fn read_custom_message<R: io::Read>(&self, message_type: u64, buffer: &mut R) -> Result<Option<Self::CustomMessage>, msgs::DecodeError>;
+
+ /// Releases any [`Self::CustomMessage`]s that need to be sent.
+ ///
+ /// Typically, this is used for messages initiating a message flow rather than in response to
+ /// another message. The latter should use the return value of [`Self::handle_custom_message`].
+ fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>>;
}
+/// A processed incoming onion message, containing either a Forward (another onion message)
+/// or a Receive payload with decrypted contents.
+pub enum PeeledOnion<T: OnionMessageContents> {
+ /// Forwarded onion, with the next node id and a new onion
+ Forward(PublicKey, OnionMessage),
+ /// Received onion message, with decrypted contents, path_id, and reply path
+ Receive(ParsedOnionMessageContents<T>, Option<[u8; 32]>, Option<BlindedPath>)
+}
-/// Create an onion message with contents `message` to the destination of `path`.
-/// Returns (introduction_node_id, onion_msg)
-pub fn create_onion_message<ES: Deref, NS: Deref, T: CustomOnionMessageContents>(
+/// Creates an [`OnionMessage`] with the given `contents` for sending to the destination of
+/// `path`.
+///
+/// Returns both the node id of the peer to send the message to and the message itself.
+pub fn create_onion_message<ES: Deref, NS: Deref, T: OnionMessageContents>(
entropy_source: &ES, node_signer: &NS, secp_ctx: &Secp256k1<secp256k1::All>,
- path: OnionMessagePath, message: OnionMessageContents<T>, reply_path: Option<BlindedPath>,
-) -> Result<(PublicKey, msgs::OnionMessage), SendError>
+ path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>,
+) -> Result<(PublicKey, OnionMessage), SendError>
where
ES::Target: EntropySource,
NS::Target: NodeSigner,
{
let OnionMessagePath { intermediate_nodes, mut destination } = path;
if let Destination::BlindedPath(BlindedPath { ref blinded_hops, .. }) = destination {
- if blinded_hops.len() < 2 {
+ if blinded_hops.is_empty() {
return Err(SendError::TooFewBlindedHops);
}
}
- if message.tlv_type() < 64 { return Err(SendError::InvalidMessage) }
+ if contents.tlv_type() < 64 { return Err(SendError::InvalidMessage) }
// If we are sending straight to a blinded path and we are the introduction node, we need to
// advance the blinded path by 1 hop so the second hop is the new introduction node.
let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
- let (introduction_node_id, blinding_point) = if intermediate_nodes.len() != 0 {
- (intermediate_nodes[0], PublicKey::from_secret_key(&secp_ctx, &blinding_secret))
+ let (first_node_id, blinding_point) = if let Some(first_node_id) = intermediate_nodes.first() {
+ (*first_node_id, PublicKey::from_secret_key(&secp_ctx, &blinding_secret))
} else {
match destination {
Destination::Node(pk) => (pk, PublicKey::from_secret_key(&secp_ctx, &blinding_secret)),
}
};
let (packet_payloads, packet_keys) = packet_payloads_and_keys(
- &secp_ctx, &intermediate_nodes, destination, message, reply_path, &blinding_secret)
+ &secp_ctx, &intermediate_nodes, destination, contents, reply_path, &blinding_secret)
.map_err(|e| SendError::Secp256k1(e))?;
let prng_seed = entropy_source.get_secure_random_bytes();
let onion_routing_packet = construct_onion_message_packet(
packet_payloads, packet_keys, prng_seed).map_err(|()| SendError::TooBigPacket)?;
- Ok((introduction_node_id, msgs::OnionMessage {
+ Ok((first_node_id, OnionMessage {
blinding_point,
onion_routing_packet
}))
}
+/// Decode one layer of an incoming [`OnionMessage`].
+///
+/// Returns either the next layer of the onion for forwarding or the decrypted content for the
+/// receiver.
+pub fn peel_onion_message<NS: Deref, L: Deref, CMH: Deref>(
+ msg: &OnionMessage, secp_ctx: &Secp256k1<secp256k1::All>, node_signer: NS, logger: L,
+ custom_handler: CMH,
+) -> Result<PeeledOnion<<<CMH>::Target as CustomOnionMessageHandler>::CustomMessage>, ()>
+where
+ NS::Target: NodeSigner,
+ L::Target: Logger,
+ CMH::Target: CustomOnionMessageHandler,
+{
+ let control_tlvs_ss = match node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
+ Ok(ss) => ss,
+ Err(e) => {
+ log_error!(logger, "Failed to retrieve node secret: {:?}", e);
+ return Err(());
+ }
+ };
+ let onion_decode_ss = {
+ let blinding_factor = {
+ let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
+ hmac.input(control_tlvs_ss.as_ref());
+ Hmac::from_engine(hmac).into_inner()
+ };
+ match node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
+ Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
+ {
+ Ok(ss) => ss.secret_bytes(),
+ Err(()) => {
+ log_trace!(logger, "Failed to compute onion packet shared secret");
+ return Err(());
+ }
+ }
+ };
+ match onion_utils::decode_next_untagged_hop(
+ onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac,
+ (control_tlvs_ss, custom_handler.deref(), logger.deref())
+ ) {
+ Ok((Payload::Receive::<ParsedOnionMessageContents<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage>> {
+ message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
+ }, None)) => {
+ Ok(PeeledOnion::Receive(message, path_id, reply_path))
+ },
+ Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
+ next_node_id, next_blinding_override
+ })), Some((next_hop_hmac, new_packet_bytes)))) => {
+ // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
+ // blinded hop and this onion message is destined for us. In this situation, we should keep
+ // unwrapping the onion layers to get to the final payload. Since we don't have the option
+ // of creating blinded paths with dummy hops currently, we should be ok to not handle this
+ // for now.
+ let new_pubkey = match onion_utils::next_hop_pubkey(&secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
+ Ok(pk) => pk,
+ Err(e) => {
+ log_trace!(logger, "Failed to compute next hop packet pubkey: {}", e);
+ return Err(())
+ }
+ };
+ let outgoing_packet = Packet {
+ version: 0,
+ public_key: new_pubkey,
+ hop_data: new_packet_bytes,
+ hmac: next_hop_hmac,
+ };
+ let onion_message = OnionMessage {
+ blinding_point: match next_blinding_override {
+ Some(blinding_point) => blinding_point,
+ None => {
+ match onion_utils::next_hop_pubkey(
+ &secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
+ ) {
+ Ok(bp) => bp,
+ Err(e) => {
+ log_trace!(logger, "Failed to compute next blinding point: {}", e);
+ return Err(())
+ }
+ }
+ }
+ },
+ onion_routing_packet: outgoing_packet,
+ };
+
+ Ok(PeeledOnion::Forward(next_node_id, onion_message))
+ },
+ Err(e) => {
+ log_trace!(logger, "Errored decoding onion message packet: {:?}", e);
+ Err(())
+ },
+ _ => {
+ log_trace!(logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
+ Err(())
+ },
+ }
+}
+
impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref>
OnionMessenger<ES, NS, L, MR, OMH, CMH>
where
}
}
- /// Send an onion message with contents `message` to the destination of `path`.
+ /// Sends an [`OnionMessage`] with the given `contents` for sending to the destination of
+ /// `path`.
///
/// See [`OnionMessenger`] for example usage.
- pub fn send_onion_message<T: CustomOnionMessageContents>(
- &self, path: OnionMessagePath, message: OnionMessageContents<T>,
- reply_path: Option<BlindedPath>
+ pub fn send_onion_message<T: OnionMessageContents>(
+ &self, path: OnionMessagePath, contents: T, reply_path: Option<BlindedPath>
) -> Result<(), SendError> {
- let (introduction_node_id, onion_msg) = create_onion_message(
- &self.entropy_source, &self.node_signer, &self.secp_ctx,
- path, message, reply_path
+ let (first_node_id, onion_msg) = create_onion_message(
+ &self.entropy_source, &self.node_signer, &self.secp_ctx, path, contents, reply_path
)?;
let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
- if outbound_buffer_full(&introduction_node_id, &pending_per_peer_msgs) { return Err(SendError::BufferFull) }
- match pending_per_peer_msgs.entry(introduction_node_id) {
+ if outbound_buffer_full(&first_node_id, &pending_per_peer_msgs) { return Err(SendError::BufferFull) }
+ match pending_per_peer_msgs.entry(first_node_id) {
hash_map::Entry::Vacant(_) => Err(SendError::InvalidFirstHop),
hash_map::Entry::Occupied(mut e) => {
e.get_mut().push_back(onion_msg);
}
}
- fn respond_with_onion_message<T: CustomOnionMessageContents>(
- &self, response: OnionMessageContents<T>, path_id: Option<[u8; 32]>,
- reply_path: Option<BlindedPath>
+ fn handle_onion_message_response<T: OnionMessageContents>(
+ &self, response: Option<T>, reply_path: Option<BlindedPath>, log_suffix: fmt::Arguments
+ ) {
+ if let Some(response) = response {
+ match reply_path {
+ Some(reply_path) => {
+ self.find_path_and_enqueue_onion_message(
+ response, Destination::BlindedPath(reply_path), None, log_suffix
+ );
+ },
+ None => {
+ log_trace!(self.logger, "Missing reply path {}", log_suffix);
+ },
+ }
+ }
+ }
+
+ fn find_path_and_enqueue_onion_message<T: OnionMessageContents>(
+ &self, contents: T, destination: Destination, reply_path: Option<BlindedPath>,
+ log_suffix: fmt::Arguments
) {
let sender = match self.node_signer.get_node_id(Recipient::Node) {
Ok(node_id) => node_id,
Err(_) => {
- log_warn!(
- self.logger, "Unable to retrieve node id when responding to onion message with \
- path_id {:02x?}", path_id
- );
+ log_warn!(self.logger, "Unable to retrieve node id {}", log_suffix);
return;
}
};
let peers = self.pending_messages.lock().unwrap().keys().copied().collect();
-
- let destination = match reply_path {
- Some(reply_path) => Destination::BlindedPath(reply_path),
- None => {
- log_trace!(
- self.logger, "Missing reply path when responding to onion message with path_id \
- {:02x?}", path_id
- );
- return;
- },
- };
-
let path = match self.message_router.find_path(sender, peers, destination) {
Ok(path) => path,
Err(()) => {
- log_trace!(
- self.logger, "Failed to find path when responding to onion message with \
- path_id {:02x?}", path_id
- );
+ log_trace!(self.logger, "Failed to find path {}", log_suffix);
return;
},
};
- log_trace!(self.logger, "Responding to onion message with path_id {:02x?}", path_id);
+ log_trace!(self.logger, "Sending onion message {}", log_suffix);
- if let Err(e) = self.send_onion_message(path, response, None) {
- log_trace!(
- self.logger, "Failed responding to onion message with path_id {:02x?}: {:?}",
- path_id, e
- );
+ if let Err(e) = self.send_onion_message(path, contents, reply_path) {
+ log_trace!(self.logger, "Failed sending onion message {}: {:?}", log_suffix, e);
return;
}
}
#[cfg(test)]
- pub(super) fn release_pending_msgs(&self) -> HashMap<PublicKey, VecDeque<msgs::OnionMessage>> {
+ pub(super) fn release_pending_msgs(&self) -> HashMap<PublicKey, VecDeque<OnionMessage>> {
let mut pending_msgs = self.pending_messages.lock().unwrap();
let mut msgs = HashMap::new();
// We don't want to disconnect the peers by removing them entirely from the original map, so we
}
}
-fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap<PublicKey, VecDeque<msgs::OnionMessage>>) -> bool {
+fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap<PublicKey, VecDeque<OnionMessage>>) -> bool {
const MAX_TOTAL_BUFFER_SIZE: usize = (1 << 20) * 128;
const MAX_PER_PEER_BUFFER_SIZE: usize = (1 << 10) * 256;
let mut total_buffered_bytes = 0;
OMH::Target: OffersMessageHandler,
CMH::Target: CustomOnionMessageHandler,
{
- /// Handle an incoming onion message. Currently, if a message was destined for us we will log, but
- /// soon we'll delegate the onion message to a handler that can generate invoices or send
- /// payments.
- fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &msgs::OnionMessage) {
- let control_tlvs_ss = match self.node_signer.ecdh(Recipient::Node, &msg.blinding_point, None) {
- Ok(ss) => ss,
- Err(e) => {
- log_error!(self.logger, "Failed to retrieve node secret: {:?}", e);
- return
- }
- };
- let onion_decode_ss = {
- let blinding_factor = {
- let mut hmac = HmacEngine::<Sha256>::new(b"blinded_node_id");
- hmac.input(control_tlvs_ss.as_ref());
- Hmac::from_engine(hmac).into_inner()
- };
- match self.node_signer.ecdh(Recipient::Node, &msg.onion_routing_packet.public_key,
- Some(&Scalar::from_be_bytes(blinding_factor).unwrap()))
- {
- Ok(ss) => ss.secret_bytes(),
- Err(()) => {
- log_trace!(self.logger, "Failed to compute onion packet shared secret");
- return
- }
- }
- };
- match onion_utils::decode_next_untagged_hop(
- onion_decode_ss, &msg.onion_routing_packet.hop_data[..], msg.onion_routing_packet.hmac,
- (control_tlvs_ss, &*self.custom_handler, &*self.logger)
+ fn handle_onion_message(&self, _peer_node_id: &PublicKey, msg: &OnionMessage) {
+ match peel_onion_message(
+ msg, &self.secp_ctx, &*self.node_signer, &*self.logger, &*self.custom_handler
) {
- Ok((Payload::Receive::<<<CMH as Deref>::Target as CustomOnionMessageHandler>::CustomMessage> {
- message, control_tlvs: ReceiveControlTlvs::Unblinded(ReceiveTlvs { path_id }), reply_path,
- }, None)) => {
+ Ok(PeeledOnion::Receive(message, path_id, reply_path)) => {
log_trace!(self.logger,
"Received an onion message with path_id {:02x?} and {} reply_path",
path_id, if reply_path.is_some() { "a" } else { "no" });
- let response = match message {
- OnionMessageContents::Offers(msg) => {
- self.offers_handler.handle_message(msg)
- .map(|msg| OnionMessageContents::Offers(msg))
+ match message {
+ ParsedOnionMessageContents::Offers(msg) => {
+ let response = self.offers_handler.handle_message(msg);
+ self.handle_onion_message_response(
+ response, reply_path, format_args!(
+ "when responding to Offers onion message with path_id {:02x?}",
+ path_id
+ )
+ );
},
- OnionMessageContents::Custom(msg) => {
- self.custom_handler.handle_custom_message(msg)
- .map(|msg| OnionMessageContents::Custom(msg))
+ ParsedOnionMessageContents::Custom(msg) => {
+ let response = self.custom_handler.handle_custom_message(msg);
+ self.handle_onion_message_response(
+ response, reply_path, format_args!(
+ "when responding to Custom onion message with path_id {:02x?}",
+ path_id
+ )
+ );
},
- };
-
- if let Some(response) = response {
- self.respond_with_onion_message(response, path_id, reply_path);
}
},
- Ok((Payload::Forward(ForwardControlTlvs::Unblinded(ForwardTlvs {
- next_node_id, next_blinding_override
- })), Some((next_hop_hmac, new_packet_bytes)))) => {
- // TODO: we need to check whether `next_node_id` is our node, in which case this is a dummy
- // blinded hop and this onion message is destined for us. In this situation, we should keep
- // unwrapping the onion layers to get to the final payload. Since we don't have the option
- // of creating blinded paths with dummy hops currently, we should be ok to not handle this
- // for now.
- let new_pubkey = match onion_utils::next_hop_pubkey(&self.secp_ctx, msg.onion_routing_packet.public_key, &onion_decode_ss) {
- Ok(pk) => pk,
- Err(e) => {
- log_trace!(self.logger, "Failed to compute next hop packet pubkey: {}", e);
- return
- }
- };
- let outgoing_packet = Packet {
- version: 0,
- public_key: new_pubkey,
- hop_data: new_packet_bytes,
- hmac: next_hop_hmac,
- };
- let onion_message = msgs::OnionMessage {
- blinding_point: match next_blinding_override {
- Some(blinding_point) => blinding_point,
- None => {
- match onion_utils::next_hop_pubkey(
- &self.secp_ctx, msg.blinding_point, control_tlvs_ss.as_ref()
- ) {
- Ok(bp) => bp,
- Err(e) => {
- log_trace!(self.logger, "Failed to compute next blinding point: {}", e);
- return
- }
- }
- }
- },
- onion_routing_packet: outgoing_packet,
- };
-
+ Ok(PeeledOnion::Forward(next_node_id, onion_message)) => {
let mut pending_per_peer_msgs = self.pending_messages.lock().unwrap();
if outbound_buffer_full(&next_node_id, &pending_per_peer_msgs) {
log_trace!(self.logger, "Dropping forwarded onion message to peer {:?}: outbound buffer full", next_node_id);
e.get_mut().push_back(onion_message);
log_trace!(self.logger, "Forwarding an onion message to peer {}", next_node_id);
}
- };
+ }
},
Err(e) => {
- log_trace!(self.logger, "Errored decoding onion message packet: {:?}", e);
- },
- _ => {
- log_trace!(self.logger, "Received bogus onion message packet, either the sender encoded a final hop as a forwarding hop or vice versa");
- },
- };
+ log_error!(self.logger, "Failed to process onion message {:?}", e);
+ }
+ }
}
fn peer_connected(&self, their_node_id: &PublicKey, init: &msgs::Init, _inbound: bool) -> Result<(), ()> {
features.set_onion_messages_optional();
features
}
-}
-impl<ES: Deref, NS: Deref, L: Deref, MR: Deref, OMH: Deref, CMH: Deref> OnionMessageProvider
-for OnionMessenger<ES, NS, L, MR, OMH, CMH>
-where
- ES::Target: EntropySource,
- NS::Target: NodeSigner,
- L::Target: Logger,
- MR::Target: MessageRouter,
- OMH::Target: OffersMessageHandler,
- CMH::Target: CustomOnionMessageHandler,
-{
- fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<msgs::OnionMessage> {
+ // Before returning any messages to send for the peer, this method will see if any messages were
+ // enqueued in the handler by users, find a path to the corresponding blinded path's introduction
+ // node, and then enqueue the message for sending to the first peer in the full path.
+ fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<OnionMessage> {
+ // Enqueue any initiating `OffersMessage`s to send.
+ for message in self.offers_handler.release_pending_messages() {
+ let PendingOnionMessage { contents, destination, reply_path } = message;
+ self.find_path_and_enqueue_onion_message(
+ contents, destination, reply_path, format_args!("when sending OffersMessage")
+ );
+ }
+
+ // Enqueue any initiating `CustomMessage`s to send.
+ for message in self.custom_handler.release_pending_custom_messages() {
+ let PendingOnionMessage { contents, destination, reply_path } = message;
+ self.find_path_and_enqueue_onion_message(
+ contents, destination, reply_path, format_args!("when sending CustomMessage")
+ );
+ }
+
let mut pending_msgs = self.pending_messages.lock().unwrap();
if let Some(msgs) = pending_msgs.get_mut(&peer_node_id) {
return msgs.pop_front()
///
/// [`SimpleArcChannelManager`]: crate::ln::channelmanager::SimpleArcChannelManager
/// [`SimpleArcPeerManager`]: crate::ln::peer_handler::SimpleArcPeerManager
-pub type SimpleArcOnionMessenger<L> = OnionMessenger<
+pub type SimpleArcOnionMessenger<M, T, F, L> = OnionMessenger<
Arc<KeysManager>,
Arc<KeysManager>,
Arc<L>,
Arc<DefaultMessageRouter>,
- IgnoringMessageHandler,
+ Arc<SimpleArcChannelManager<M, T, F, L>>,
IgnoringMessageHandler
>;
///
/// [`SimpleRefChannelManager`]: crate::ln::channelmanager::SimpleRefChannelManager
/// [`SimpleRefPeerManager`]: crate::ln::peer_handler::SimpleRefPeerManager
-pub type SimpleRefOnionMessenger<'a, 'b, 'c, L> = OnionMessenger<
+pub type SimpleRefOnionMessenger<
+ 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, M, T, F, L
+> = OnionMessenger<
&'a KeysManager,
&'a KeysManager,
&'b L,
- &'c DefaultMessageRouter,
- IgnoringMessageHandler,
+ &'i DefaultMessageRouter,
+ &'j SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
IgnoringMessageHandler
>;
/// Construct onion packet payloads and keys for sending an onion message along the given
/// `unblinded_path` to the given `destination`.
-fn packet_payloads_and_keys<T: CustomOnionMessageContents, S: secp256k1::Signing + secp256k1::Verification>(
- secp_ctx: &Secp256k1<S>, unblinded_path: &[PublicKey], destination: Destination,
- message: OnionMessageContents<T>, mut reply_path: Option<BlindedPath>, session_priv: &SecretKey
+fn packet_payloads_and_keys<T: OnionMessageContents, S: secp256k1::Signing + secp256k1::Verification>(
+ secp_ctx: &Secp256k1<S>, unblinded_path: &[PublicKey], destination: Destination, message: T,
+ mut reply_path: Option<BlindedPath>, session_priv: &SecretKey
) -> Result<(Vec<(Payload<T>, [u8; 32])>, Vec<onion_utils::OnionKeys>), secp256k1::Error> {
let num_hops = unblinded_path.len() + destination.num_hops();
let mut payloads = Vec::with_capacity(num_hops);
}
/// Errors if the serialized payload size exceeds onion_message::BIG_PACKET_HOP_DATA_LEN
-fn construct_onion_message_packet<T: CustomOnionMessageContents>(payloads: Vec<(Payload<T>, [u8; 32])>, onion_keys: Vec<onion_utils::OnionKeys>, prng_seed: [u8; 32]) -> Result<Packet, ()> {
+fn construct_onion_message_packet<T: OnionMessageContents>(payloads: Vec<(Payload<T>, [u8; 32])>, onion_keys: Vec<onion_utils::OnionKeys>, prng_seed: [u8; 32]) -> Result<Packet, ()> {
// Spec rationale:
// "`len` allows larger messages to be sent than the standard 1300 bytes allowed for an HTLC
// onion, but this should be used sparingly as it is reduces anonymity set, hence the
mod functional_tests;
// Re-export structs so they can be imported with just the `onion_message::` module prefix.
-pub use self::messenger::{CustomOnionMessageContents, CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
+pub use self::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, MessageRouter, OnionMessageContents, OnionMessagePath, OnionMessenger, PeeledOnion, PendingOnionMessage, SendError, SimpleArcOnionMessenger, SimpleRefOnionMessenger};
pub use self::offers::{OffersMessage, OffersMessageHandler};
-pub use self::packet::Packet;
+pub use self::packet::{Packet, ParsedOnionMessageContents};
pub(crate) use self::packet::ControlTlvs;
use crate::offers::invoice_request::InvoiceRequest;
use crate::offers::invoice::Bolt12Invoice;
use crate::offers::parse::Bolt12ParseError;
+use crate::onion_message::OnionMessageContents;
+use crate::onion_message::messenger::PendingOnionMessage;
use crate::util::logger::Logger;
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
pub trait OffersMessageHandler {
/// Handles the given message by either responding with an [`Bolt12Invoice`], sending a payment,
/// or replying with an error.
+ ///
+ /// The returned [`OffersMessage`], if any, is enqueued to be sent by [`OnionMessenger`].
+ ///
+ /// [`OnionMessenger`]: crate::onion_message::OnionMessenger
fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage>;
+
+ /// Releases any [`OffersMessage`]s that need to be sent.
+ ///
+ /// Typically, this is used for messages initiating a payment flow rather than in response to
+ /// another message. The latter should use the return value of [`Self::handle_message`].
+ fn release_pending_messages(&self) -> Vec<PendingOnionMessage<OffersMessage>> { vec![] }
}
/// Possible BOLT 12 Offers messages sent and received via an [`OnionMessage`].
}
}
- /// The TLV record type for the message as used in an `onionmsg_tlv` TLV stream.
- pub fn tlv_type(&self) -> u64 {
- match self {
- OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE,
- OffersMessage::Invoice(_) => INVOICE_TLV_TYPE,
- OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE,
- }
- }
-
fn parse(tlv_type: u64, bytes: Vec<u8>) -> Result<Self, Bolt12ParseError> {
match tlv_type {
INVOICE_REQUEST_TLV_TYPE => Ok(Self::InvoiceRequest(InvoiceRequest::try_from(bytes)?)),
}
}
+impl OnionMessageContents for OffersMessage {
+ fn tlv_type(&self) -> u64 {
+ match self {
+ OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE,
+ OffersMessage::Invoice(_) => INVOICE_TLV_TYPE,
+ OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE,
+ }
+ }
+}
+
impl Writeable for OffersMessage {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
match self {
/// Onion message payloads contain "control" TLVs and "data" TLVs. Control TLVs are used to route
/// the onion message from hop to hop and for path verification, whereas data TLVs contain the onion
/// message content itself, such as an invoice request.
-pub(super) enum Payload<T: CustomOnionMessageContents> {
+pub(super) enum Payload<T: OnionMessageContents> {
/// This payload is for an intermediate hop.
Forward(ForwardControlTlvs),
/// This payload is for the final hop.
Receive {
control_tlvs: ReceiveControlTlvs,
reply_path: Option<BlindedPath>,
- message: OnionMessageContents<T>,
+ message: T,
}
}
+/// The contents of an [`OnionMessage`] as read from the wire.
+///
+/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
#[derive(Debug)]
-/// The contents of an onion message. In the context of offers, this would be the invoice, invoice
-/// request, or invoice error.
-pub enum OnionMessageContents<T: CustomOnionMessageContents> {
+pub enum ParsedOnionMessageContents<T: OnionMessageContents> {
/// A message related to BOLT 12 Offers.
Offers(OffersMessage),
/// A custom onion message specified by the user.
Custom(T),
}
-impl<T: CustomOnionMessageContents> OnionMessageContents<T> {
+impl<T: OnionMessageContents> OnionMessageContents for ParsedOnionMessageContents<T> {
/// Returns the type that was used to decode the message payload.
///
/// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable
- pub fn tlv_type(&self) -> u64 {
+ fn tlv_type(&self) -> u64 {
match self {
- &OnionMessageContents::Offers(ref msg) => msg.tlv_type(),
- &OnionMessageContents::Custom(ref msg) => msg.tlv_type(),
+ &ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(),
+ &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(),
}
}
}
/// This is not exported to bindings users as methods on non-cloneable enums are not currently exportable
-impl<T: CustomOnionMessageContents> Writeable for OnionMessageContents<T> {
+impl<T: OnionMessageContents> Writeable for ParsedOnionMessageContents<T> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
match self {
- OnionMessageContents::Offers(msg) => Ok(msg.write(w)?),
- OnionMessageContents::Custom(msg) => Ok(msg.write(w)?),
+ ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?),
+ ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?),
}
}
}
-/// The contents of a custom onion message.
-pub trait CustomOnionMessageContents: Writeable {
+/// The contents of an onion message.
+pub trait OnionMessageContents: Writeable {
/// Returns the TLV type identifying the message contents. MUST be >= 64.
fn tlv_type(&self) -> u64;
}
}
// Uses the provided secret to simultaneously encode and encrypt the unblinded control TLVs.
-impl<T: CustomOnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
+impl<T: OnionMessageContents> Writeable for (Payload<T>, [u8; 32]) {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
match &self.0 {
Payload::Forward(ForwardControlTlvs::Blinded(encrypted_bytes)) => {
}
// Uses the provided secret to simultaneously decode and decrypt the control TLVs and data TLV.
-impl<H: CustomOnionMessageHandler + ?Sized, L: Logger + ?Sized>
-ReadableArgs<(SharedSecret, &H, &L)> for Payload<<H as CustomOnionMessageHandler>::CustomMessage> {
+impl<H: CustomOnionMessageHandler + ?Sized, L: Logger + ?Sized> ReadableArgs<(SharedSecret, &H, &L)>
+for Payload<ParsedOnionMessageContents<<H as CustomOnionMessageHandler>::CustomMessage>> {
fn read<R: Read>(r: &mut R, args: (SharedSecret, &H, &L)) -> Result<Self, DecodeError> {
let (encrypted_tlvs_ss, handler, logger) = args;
match msg_type {
tlv_type if OffersMessage::is_known_type(tlv_type) => {
let msg = OffersMessage::read(msg_reader, (tlv_type, logger))?;
- message = Some(OnionMessageContents::Offers(msg));
+ message = Some(ParsedOnionMessageContents::Offers(msg));
Ok(true)
},
_ => match handler.read_custom_message(msg_type, msg_reader)? {
Some(msg) => {
- message = Some(OnionMessageContents::Custom(msg));
+ message = Some(ParsedOnionMessageContents::Custom(msg));
Ok(true)
},
None => Ok(false),
//! The [`NetworkGraph`] stores the network gossip and [`P2PGossipSync`] fetches it from peers
+use bitcoin::blockdata::constants::ChainHash;
+
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
use bitcoin::secp256k1::{PublicKey, Verification};
use bitcoin::secp256k1::Secp256k1;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash;
use bitcoin::hashes::hex::FromHex;
-use bitcoin::hash_types::BlockHash;
use bitcoin::network::constants::Network;
-use bitcoin::blockdata::constants::genesis_block;
use crate::events::{MessageSendEvent, MessageSendEventsProvider};
use crate::ln::ChannelId;
pub struct NetworkGraph<L: Deref> where L::Target: Logger {
secp_ctx: Secp256k1<secp256k1::VerifyOnly>,
last_rapid_gossip_sync_timestamp: Mutex<Option<u32>>,
- genesis_hash: BlockHash,
+ chain_hash: ChainHash,
logger: L,
// Lock order: channels -> nodes
channels: RwLock<IndexedMap<u64, ChannelInfo>>,
impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
/// Handles any network updates originating from [`Event`]s.
+ //
+ /// Note that this will skip applying any [`NetworkUpdate::ChannelUpdateMessage`] to avoid
+ /// leaking possibly identifying information of the sender to the public network.
///
/// [`Event`]: crate::events::Event
pub fn handle_network_update(&self, network_update: &NetworkUpdate) {
let short_channel_id = msg.contents.short_channel_id;
let is_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
let status = if is_enabled { "enabled" } else { "disabled" };
- log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}.", short_channel_id, status);
- let _ = self.update_channel(msg);
+ log_debug!(self.logger, "Skipping application of a channel update from a payment failure. Channel {} is {}.", short_channel_id, status);
},
NetworkUpdate::ChannelFailure { short_channel_id, is_permanent } => {
if is_permanent {
}
}
- /// Gets the genesis hash for this network graph.
- pub fn get_genesis_hash(&self) -> BlockHash {
- self.genesis_hash
+ /// Gets the chain hash for this network graph.
+ pub fn get_chain_hash(&self) -> ChainHash {
+ self.chain_hash
}
}
pending_events.push(MessageSendEvent::SendGossipTimestampFilter {
node_id: their_node_id.clone(),
msg: GossipTimestampFilter {
- chain_hash: self.network_graph.genesis_hash,
+ chain_hash: self.network_graph.chain_hash,
first_timestamp: gossip_start_time as u32, // 2106 issue!
timestamp_range: u32::max_value(),
},
let exclusive_end_scid = scid_from_parts(cmp::min(msg.end_blocknum() as u64, MAX_SCID_BLOCK), 0, 0);
// Per spec, we must reply to a query. Send an empty message when things are invalid.
- if msg.chain_hash != self.network_graph.genesis_hash || inclusive_start_scid.is_err() || exclusive_end_scid.is_err() || msg.number_of_blocks == 0 {
+ if msg.chain_hash != self.network_graph.chain_hash || inclusive_start_scid.is_err() || exclusive_end_scid.is_err() || msg.number_of_blocks == 0 {
let mut pending_events = self.pending_events.lock().unwrap();
pending_events.push(MessageSendEvent::SendReplyChannelRange {
node_id: their_node_id.clone(),
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
- self.genesis_hash.write(writer)?;
+ self.chain_hash.write(writer)?;
let channels = self.channels.read().unwrap();
(channels.len() as u64).write(writer)?;
for (ref chan_id, ref chan_info) in channels.unordered_iter() {
fn read<R: io::Read>(reader: &mut R, logger: L) -> Result<NetworkGraph<L>, DecodeError> {
let _ver = read_ver_prefix!(reader, SERIALIZATION_VERSION);
- let genesis_hash: BlockHash = Readable::read(reader)?;
+ let chain_hash: ChainHash = Readable::read(reader)?;
let channels_count: u64 = Readable::read(reader)?;
let mut channels = IndexedMap::new();
for _ in 0..channels_count {
Ok(NetworkGraph {
secp_ctx: Secp256k1::verification_only(),
- genesis_hash,
+ chain_hash,
logger,
channels: RwLock::new(channels),
nodes: RwLock::new(nodes),
let b = if ord { (&other.channels, &other.nodes) } else { (&self.channels, &self.nodes) };
let (channels_a, channels_b) = (a.0.unsafe_well_ordered_double_lock_self(), b.0.unsafe_well_ordered_double_lock_self());
let (nodes_a, nodes_b) = (a.1.unsafe_well_ordered_double_lock_self(), b.1.unsafe_well_ordered_double_lock_self());
- self.genesis_hash.eq(&other.genesis_hash) && channels_a.eq(&channels_b) && nodes_a.eq(&nodes_b)
+ self.chain_hash.eq(&other.chain_hash) && channels_a.eq(&channels_b) && nodes_a.eq(&nodes_b)
}
}
pub fn new(network: Network, logger: L) -> NetworkGraph<L> {
Self {
secp_ctx: Secp256k1::verification_only(),
- genesis_hash: genesis_block(network).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(network),
logger,
channels: RwLock::new(IndexedMap::new()),
nodes: RwLock::new(IndexedMap::new()),
return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
}
- if msg.chain_hash != self.genesis_hash {
+ if msg.chain_hash != self.chain_hash {
return Err(LightningError {
err: "Channel announcement chain hash does not match genesis hash".to_owned(),
action: ErrorAction::IgnoreAndLog(Level::Debug),
/// For an already known (from announcement) channel, update info about one of the directions
/// of the channel.
///
- /// You probably don't want to call this directly, instead relying on a P2PGossipSync's
- /// RoutingMessageHandler implementation to call it indirectly. This may be useful to accept
+ /// You probably don't want to call this directly, instead relying on a [`P2PGossipSync`]'s
+ /// [`RoutingMessageHandler`] implementation to call it indirectly. This may be useful to accept
/// routing messages from a source using a protocol other than the lightning P2P protocol.
///
/// If built with `no-std`, any updates with a timestamp more than two weeks in the past or
/// materially in the future will be rejected.
pub fn update_channel(&self, msg: &msgs::ChannelUpdate) -> Result<(), LightningError> {
- self.update_channel_intern(&msg.contents, Some(&msg), Some(&msg.signature))
+ self.update_channel_internal(&msg.contents, Some(&msg), Some(&msg.signature), false)
}
/// For an already known (from announcement) channel, update info about one of the directions
/// If built with `no-std`, any updates with a timestamp more than two weeks in the past or
/// materially in the future will be rejected.
pub fn update_channel_unsigned(&self, msg: &msgs::UnsignedChannelUpdate) -> Result<(), LightningError> {
- self.update_channel_intern(msg, None, None)
+ self.update_channel_internal(msg, None, None, false)
}
- fn update_channel_intern(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig: Option<&secp256k1::ecdsa::Signature>) -> Result<(), LightningError> {
+ /// For an already known (from announcement) channel, verify the given [`ChannelUpdate`].
+ ///
+ /// This checks whether the update currently is applicable by [`Self::update_channel`].
+ ///
+ /// If built with `no-std`, any updates with a timestamp more than two weeks in the past or
+ /// materially in the future will be rejected.
+ pub fn verify_channel_update(&self, msg: &msgs::ChannelUpdate) -> Result<(), LightningError> {
+ self.update_channel_internal(&msg.contents, Some(&msg), Some(&msg.signature), true)
+ }
+
+ fn update_channel_internal(&self, msg: &msgs::UnsignedChannelUpdate,
+ full_msg: Option<&msgs::ChannelUpdate>, sig: Option<&secp256k1::ecdsa::Signature>,
+ only_verify: bool) -> Result<(), LightningError>
+ {
let chan_enabled = msg.flags & (1 << 1) != (1 << 1);
- if msg.chain_hash != self.genesis_hash {
+ if msg.chain_hash != self.chain_hash {
return Err(LightningError {
err: "Channel update chain hash does not match genesis hash".to_owned(),
action: ErrorAction::IgnoreAndLog(Level::Debug),
action: ErrorAction::IgnoreAndLog(Level::Debug)
})?, "channel_update");
}
- channel.two_to_one = get_new_channel_info!();
+ if !only_verify {
+ channel.two_to_one = get_new_channel_info!();
+ }
} else {
check_update_latest!(channel.one_to_two);
if let Some(sig) = sig {
action: ErrorAction::IgnoreAndLog(Level::Debug)
})?, "channel_update");
}
- channel.one_to_two = get_new_channel_info!();
+ if !only_verify {
+ channel.one_to_two = get_new_channel_info!();
+ }
}
}
}
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
- use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::Script;
use bitcoin::blockdata::transaction::TxOut;
let mut unsigned_announcement = UnsignedChannelAnnouncement {
features: channelmanager::provided_channel_features(&UserConfig::default()),
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 0,
node_id_1: NodeId::from_pubkey(&node_id_1),
node_id_2: NodeId::from_pubkey(&node_id_2),
pub(crate) fn get_signed_channel_update<F: Fn(&mut UnsignedChannelUpdate)>(f: F, node_key: &SecretKey, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelUpdate {
let mut unsigned_channel_update = UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 0,
timestamp: 100,
flags: 0,
// Test that channel announcements with the wrong chain hash are ignored (network graph is testnet,
// announcement is mainnet).
let incorrect_chain_announcement = get_signed_channel_announcement(|unsigned_announcement| {
- unsigned_announcement.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
+ unsigned_announcement.chain_hash = ChainHash::using_genesis_block(Network::Bitcoin);
}, node_1_privkey, node_2_privkey, &secp_ctx);
match gossip_sync.handle_channel_announcement(&incorrect_chain_announcement) {
Ok(_) => panic!(),
}
let valid_channel_update = get_signed_channel_update(|_| {}, node_1_privkey, &secp_ctx);
+ network_graph.verify_channel_update(&valid_channel_update).unwrap();
match gossip_sync.handle_channel_update(&valid_channel_update) {
Ok(res) => assert!(res),
_ => panic!(),
// Test that channel updates with the wrong chain hash are ignored (network graph is testnet, channel
// update is mainet).
let incorrect_chain_update = get_signed_channel_update(|unsigned_channel_update| {
- unsigned_channel_update.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
+ unsigned_channel_update.chain_hash = ChainHash::using_genesis_block(Network::Bitcoin);
}, node_1_privkey, &secp_ctx);
match gossip_sync.handle_channel_update(&incorrect_chain_update) {
let short_channel_id;
{
- // Announce a channel we will update
+ // Check we won't apply an update via `handle_network_update` for privacy reasons, but
+ // can continue fine if we manually apply it.
let valid_channel_announcement = get_signed_channel_announcement(|_| {}, node_1_privkey, node_2_privkey, &secp_ctx);
short_channel_id = valid_channel_announcement.contents.short_channel_id;
let chain_source: Option<&test_utils::TestChainSource> = None;
assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
network_graph.handle_network_update(&NetworkUpdate::ChannelUpdateMessage {
- msg: valid_channel_update,
+ msg: valid_channel_update.clone(),
});
- assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_some());
+ assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
+ network_graph.update_channel(&valid_channel_update).unwrap();
}
// Non-permanent failure doesn't touch the channel at all
let node_privkey_1 = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_privkey_1);
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
+ let chain_hash = ChainHash::using_genesis_block(Network::Testnet);
// It should ignore if gossip_queries feature is not enabled
{
let network_graph = create_network_graph();
let (secp_ctx, gossip_sync) = create_gossip_sync(&network_graph);
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
+ let chain_hash = ChainHash::using_genesis_block(Network::Testnet);
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey);
&gossip_sync,
&node_id_2,
QueryChannelRange {
- chain_hash: genesis_block(Network::Bitcoin).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
first_blocknum: 0,
number_of_blocks: 0xffff_ffff,
},
false,
vec![ReplyChannelRange {
- chain_hash: genesis_block(Network::Bitcoin).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Bitcoin),
first_blocknum: 0,
number_of_blocks: 0xffff_ffff,
sync_complete: true,
let node_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey);
- let chain_hash = genesis_block(Network::Testnet).header.block_hash();
+ let chain_hash = ChainHash::using_genesis_block(Network::Testnet);
let result = gossip_sync.handle_query_short_channel_ids(&node_id, QueryShortChannelIds {
chain_hash,
&self, payer: &PublicKey, route_params: &RouteParameters,
first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
) -> Result<Route, LightningError>;
+
/// Finds a [`Route`] for a payment between the given `payer` and a payee.
///
/// The `payee` and the payment's value are given in [`RouteParameters::payment_params`]
use crate::chain::transaction::OutPoint;
use crate::sign::EntropySource;
use crate::ln::ChannelId;
- use crate::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures, ChannelFeatures, InitFeatures, NodeFeatures};
+ use crate::ln::features::{BlindedHopFeatures, ChannelFeatures, InitFeatures, NodeFeatures};
use crate::ln::msgs::{ErrorAction, LightningError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
use crate::ln::channelmanager;
use crate::offers::invoice::BlindedPayInfo;
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
- use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::opcodes;
use bitcoin::blockdata::transaction::TxOut;
// Disable other paths
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2, // to disable
// Check against amount_to_transfer_over_msat.
// Set minimal HTLC of 200_000_000 msat.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 3,
flags: 0,
// Second hop only allows to forward 199_999_999 at most, thus not allowing the first hop to
// be used.
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 3,
flags: 0,
// Lift the restriction on the first hop.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 4,
flags: 0,
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// One path allows transferring 35-40 sats, another one also allows 35-40 sats.
// Thus, they can't send 60 without overpaying.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 3,
flags: 0,
// Make 0 fee.
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
// Disable other paths
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 3,
flags: 2, // to disable
// Now, test that if there are 2 paths, a "cheaper" by fee path wouldn't be prioritized
// while taking even more fee to match htlc_minimum_msat.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 4,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 3,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 4,
flags: 0,
let (secp_ctx, network_graph, gossip_sync, _, logger) = build_graph();
let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap();
let scorer = ln_test_utils::TestScorer::new();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// First disable all paths except the us -> node1 -> node2 path
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 3,
// Set channel 4 to free but with a high htlc_minimum_msat
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
// // Disable channels 4 and 12 by flags=2
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 2, // to disable
// Disabling channels 6 & 7 by flags=2
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2, // to disable
// Disabling channels 6 & 7 by flags=2
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 2, // to disable
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2, // to disable
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We will use a simple single-path route from
// our node to node2 via node0: channels {1, 3}.
// First disable all other paths.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 2,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 2,
// Make the first channel (#1) very permissive,
// and we will be testing all limits on the second channel.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
// First, let's see if routing works if we have absolutely no idea about the available amount.
// In this case, it should be set to 250_000 sats.
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
// Check that setting next_outbound_htlc_limit_msat in first_hops limits the channels.
// Disable channel #1 and use another first hop.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 3,
flags: 2,
// Enable channel #1 back.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 4,
flags: 0,
// Now let's see if routing works if we know only htlc_maximum_msat.
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 3,
flags: 0,
// We can't change UTXO capacity on the fly, so we'll disable
// the existing channel and add another one with the capacity we need.
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 4,
flags: 2,
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 333);
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 333,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 333,
timestamp: 1,
flags: 1,
// Now let's see if routing chooses htlc_maximum_msat over UTXO capacity.
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 333,
timestamp: 6,
flags: 0,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// Path via {node7, node2, node4} is channels {12, 13, 6, 11}.
// {12, 13, 11} have the capacities of 100, {6} has a capacity of 50.
// Disable other potential paths.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 2,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2,
// Limit capacities
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 11,
timestamp: 2,
flags: 0,
// Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50).
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
let (_, _, _, nodes) = get_nodes(&secp_ctx);
let config = UserConfig::default();
let clear_payment_params = PaymentParameters::from_node_id(nodes[2], 42)
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
do_simple_mpp_route_test(clear_payment_params);
// MPP to a 1-hop blinded path for nodes[2]
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let blinded_path = BlindedPath {
introduction_node_id: nodes[2],
blinding_point: ln_test_utils::pubkey(42),
// Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50).
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
// Path via node7 is channels {12, 13}. Limit them to 60 and 60 sats
// (total limit 60).
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
// Path via node1 is channels {2, 4}. Limit them to 200 and 180 sats
// (total capacity 180 sats).
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We need a route consisting of 3 paths:
// From our node to node3 via {node0, node2}, {node7, node2, node4} and {node7, node2}.
// Disable other potential paths.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 2,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2,
// Path via {node0, node2} is channels {1, 3, 5}.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
// Capacity of 200 sats because this channel will be used by 3rd path as well.
add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5);
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 5,
timestamp: 2,
flags: 0,
// Add 100 sats to the capacities of {12, 13}, because these channels
// are also used for 3rd path. 100 sats for the rest. Total capacity: 100 sats.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 11,
timestamp: 2,
flags: 0,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// This test checks that if we have two cheaper paths and one more expensive path,
// so that liquidity-wise any 2 of 3 combination is sufficient,
// Disable other potential paths.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 2,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2,
// Path via {node0, node2} is channels {1, 3, 5}.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
// Capacity of 200 sats because this channel will be used by 3rd path as well.
add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5);
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 5,
timestamp: 2,
flags: 0,
// Add 100 sats to the capacities of {12, 13}, because these channels
// are also used for 3rd path. 100 sats for the rest. Total capacity: 100 sats.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 11,
timestamp: 2,
flags: 0,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[3], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[3], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We need a route consisting of 2 paths:
// From our node to node3 via {node0, node2} and {node7, node2, node4}.
// Disable other potential paths.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 2,
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 2,
flags: 2,
// Path via {node0, node2} is channels {1, 3, 5}.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5);
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 5,
timestamp: 2,
flags: 0,
// - fee for channel 6 is 150 sats
// Let's test this by enforcing these 2 conditions and removing other limits.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 11,
timestamp: 2,
flags: 0,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ let payment_params = PaymentParameters::from_node_id(PublicKey::from_slice(&[02; 33]).unwrap(), 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap()
.with_route_hints(vec![RouteHint(vec![RouteHintHop {
src_node_id: nodes[2],
short_channel_id: 42,
// we think we can only send up to 1 additional sat over the last-hop but refuse to as its
// under 5% of our payment amount.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0|2, // Channel disabled
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap()
.with_max_channel_saturation_power_of_half(0);
// We need a route consisting of 3 paths:
// Path via node0 is channels {1, 3}. Limit them to 100 and 50 sats (total limit 50);
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 2,
flags: 0,
// Path via node7 is channels {12, 13}. Limit them to 60 and 60 sats (total limit 60);
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
// Path via node1 is channels {2, 4}. Limit them to 20 and 20 sats (total capacity 20 sats).
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 1,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &privkeys[1], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(5)), 5);
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 5,
timestamp: 1,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4);
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 1,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &privkeys[3], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3);
update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 1,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2);
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 1,
flags: 0,
add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1);
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 1,
flags: 0,
// We modify the graph to set the htlc_maximum of channel 2 to below the value we wish to
// send.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
});
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 2,
flags: 0,
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// We modify the graph to set the htlc_minimum of channel 2 and 4 as needed - channel 2
// gets an htlc_maximum_msat of 80_000 and channel 4 an htlc_minimum_msat of 90_000. We
// then try to send 90_000.
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
assert_eq!(route.paths[0].hops[1].short_channel_id, 13);
assert_eq!(route.paths[0].hops[1].fee_msat, 90_000);
assert_eq!(route.paths[0].hops[1].cltv_expiry_delta, 42);
- assert_eq!(route.paths[0].hops[1].node_features.le_flags(), channelmanager::provided_invoice_features(&config).le_flags());
+ assert_eq!(route.paths[0].hops[1].node_features.le_flags(), channelmanager::provided_bolt11_invoice_features(&config).le_flags());
assert_eq!(route.paths[0].hops[1].channel_features.le_flags(), &id_to_feature_flags(13));
}
}
let network_graph = NetworkGraph::new(Network::Testnet, Arc::clone(&logger));
let scorer = ln_test_utils::TestScorer::new();
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[0], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[0], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// Set the fee on channel 13 to 100% to match channel 4 giving us two equivalent paths (us
// -> node 7 -> node2 and us -> node 1 -> node 2) which we should balance over.
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 2,
flags: 0,
});
let config = UserConfig::default();
- let payment_params = PaymentParameters::from_node_id(nodes[2], 42).with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ let payment_params = PaymentParameters::from_node_id(nodes[2], 42)
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let keys_manager = ln_test_utils::TestKeysInterface::new(&[0u8; 32], Network::Testnet);
let random_seed_bytes = keys_manager.get_secure_random_bytes();
// 100,000 sats is less than the available liquidity on each channel, set above.
let params = ProbabilisticScoringFeeParameters::default();
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
- let features = channelmanager::provided_invoice_features(&UserConfig::default());
+ let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 0, 2);
}
let params = ProbabilisticScoringFeeParameters::default();
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &graph, &logger);
- let features = channelmanager::provided_invoice_features(&UserConfig::default());
+ let features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
super::bench_utils::generate_test_routes(&graph, &mut scorer, ¶ms, features, random_init_seed(), 1_000_000, 2);
}
let dest_node_id = ln_test_utils::pubkey(42);
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint_1.clone()]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
// Make sure we'll error if our route hints don't have enough liquidity according to their
// htlc_maximum_msat.
route_hint_2.0[0].short_channel_id = 43;
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint_1, route_hint_2]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let mut route_params = RouteParameters::from_payment_params_and_value(
payment_params, max_htlc_msat + 1);
route_params.max_total_routing_fee_msat = Some(max_htlc_msat * 2);
let dest_node_id = ln_test_utils::pubkey(44);
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint_1, route_hint_2]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config))
+ .unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, amt_msat);
cltv_expiry_delta: 10,
features: BlindedHopFeatures::empty(),
};
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(vec![
(blinded_payinfo.clone(), blinded_path.clone()),
(blinded_payinfo.clone(), blinded_path.clone())])
let random_seed_bytes = keys_manager.get_secure_random_bytes();
let config = UserConfig::default();
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let blinded_path_1 = BlindedPath {
introduction_node_id: nodes[2],
blinding_point: ln_test_utils::pubkey(42),
(blinded_payinfo_2.clone(), blinded_path_2.clone()),
];
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
- .with_bolt12_features(bolt12_features.clone()).unwrap();
+ .with_bolt12_features(bolt12_features).unwrap();
let mut route_params = RouteParameters::from_payment_params_and_value(payment_params, 100_000);
route_params.max_total_routing_fee_msat = Some(100_000);
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[1],
ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1);
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 1,
flags: 1,
blinded_hints[1].0.htlc_maximum_msat = 2_8089_0861_1584_0000;
blinded_hints[1].0.cltv_expiry_delta = 0;
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
- .with_bolt12_features(bolt12_features.clone()).unwrap();
+ .with_bolt12_features(bolt12_features).unwrap();
let netgraph = network_graph.read_only();
let route_params = RouteParameters::from_payment_params_and_value(
];
blinded_hints[1].1.introduction_node_id = nodes[6];
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap();
blinded_hints[2].1.introduction_node_id = nodes[6];
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap();
cltv_expiry_delta: 0,
features: BlindedHopFeatures::empty(),
};
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
PaymentParameters::blinded(vec![(blinded_payinfo, blinded_path)])
.with_bolt12_features(bolt12_features.clone()).unwrap()
} else {
PaymentParameters::from_node_id(nodes[1], 42)
.with_route_hints(vec![route_hint]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap()
};
let netgraph = network_graph.read_only();
features: BlindedHopFeatures::empty(),
}, blinded_path.clone()));
}
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap()
} else {
}
PaymentParameters::from_node_id(nodes[1], 42)
.with_route_hints(route_hints).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap()
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap()
};
let netgraph = network_graph.read_only();
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[6], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 1,
flags: 0,
],
})
];
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
let payment_params = PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
features: BlindedHopFeatures::empty(),
}, blinded_path.clone()));
}
- let bolt12_features: Bolt12InvoiceFeatures = channelmanager::provided_invoice_features(&config).to_context();
+ let bolt12_features = channelmanager::provided_bolt12_invoice_features(&config);
PaymentParameters::blinded(blinded_hints.clone())
.with_bolt12_features(bolt12_features.clone()).unwrap()
};
add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[0],
ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1);
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[1],
ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 2);
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 2,
flags: 1,
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, amt_msat);
let payment_params = PaymentParameters::from_node_id(dest_node_id, 42)
.with_route_hints(vec![route_hint]).unwrap()
- .with_bolt11_features(channelmanager::provided_invoice_features(&config)).unwrap();
+ .with_bolt11_features(channelmanager::provided_bolt11_invoice_features(&config)).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, amt_msat);
// Generate fail/success paths for a wider range of potential amounts with
// MPP enabled to give us a chance to apply penalties for more potential
// routes.
- let mpp_features = channelmanager::provided_invoice_features(&UserConfig::default());
+ let mpp_features = channelmanager::provided_bolt11_invoice_features(&UserConfig::default());
let params = PaymentParameters::from_node_id(dst, 42)
.with_bolt11_features(mpp_features).unwrap();
let route_params = RouteParameters::from_payment_params_and_value(
let network_graph = bench_utils::read_network_graph(&logger).unwrap();
let scorer = FixedPenaltyScorer::with_penalty(0);
generate_routes(bench, &network_graph, scorer, &Default::default(),
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_mpp_routes_with_zero_penalty_scorer");
}
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_mpp_routes_with_probabilistic_scorer");
}
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 100_000_000,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 100_000_000,
"generate_large_mpp_routes_with_probabilistic_scorer");
}
let scorer = ProbabilisticScorer::new(
ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_routes_with_nonlinear_probabilistic_scorer");
}
let scorer = ProbabilisticScorer::new(
ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 0,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 0,
"generate_mpp_routes_with_nonlinear_probabilistic_scorer");
}
let scorer = ProbabilisticScorer::new(
ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
generate_routes(bench, &network_graph, scorer, ¶ms,
- channelmanager::provided_invoice_features(&UserConfig::default()), 100_000_000,
+ channelmanager::provided_bolt11_invoice_features(&UserConfig::default()), 100_000_000,
"generate_large_mpp_routes_with_nonlinear_probabilistic_scorer");
}
use crate::util::ser::{ReadableArgs, Writeable};
use crate::util::test_utils::{self, TestLogger};
- use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::network::constants::Network;
network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_1_key: SecretKey,
node_2_key: SecretKey
) {
- let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+ let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
let node_1_secret = &SecretKey::from_slice(&[39; 32]).unwrap();
let node_2_secret = &SecretKey::from_slice(&[40; 32]).unwrap();
let secp_ctx = Secp256k1::new();
network_graph: &mut NetworkGraph<&TestLogger>, short_channel_id: u64, node_key: SecretKey,
flags: u8, htlc_maximum_msat: u64, timestamp: u32,
) {
- let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+ let genesis_hash = ChainHash::using_genesis_block(Network::Testnet);
let secp_ctx = Secp256k1::new();
let unsigned_update = UnsignedChannelUpdate {
chain_hash: genesis_hash,
use crate::util::test_utils;
use crate::util::ser::Writeable;
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash;
use bitcoin::network::constants::Network;
-use bitcoin::blockdata::constants::genesis_block;
use hex;
let unsigned_announcement = UnsignedChannelAnnouncement {
features,
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id,
node_id_1,
node_id_2,
add_channel(&gossip_sync, &secp_ctx, &cur_privkey, &next_privkey,
ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), cur_short_channel_id);
update_channel(&gossip_sync, &secp_ctx, &cur_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: cur_short_channel_id,
timestamp: idx as u32,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &next_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: cur_short_channel_id,
timestamp: (idx as u32)+1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[0], ChannelFeatures::from_le_bytes(id_to_feature_flags(1)), 1);
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 1,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(2)), 2);
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 2,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &our_privkey, &privkeys[7], ChannelFeatures::from_le_bytes(id_to_feature_flags(12)), 12);
update_channel(&gossip_sync, &secp_ctx, &our_privkey, UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 12,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[0], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(3)), 3);
update_channel(&gossip_sync, &secp_ctx, &privkeys[0], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 3,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[1], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(4)), 4);
update_channel(&gossip_sync, &secp_ctx, &privkeys[1], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 4,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[7], &privkeys[2], ChannelFeatures::from_le_bytes(id_to_feature_flags(13)), 13);
update_channel(&gossip_sync, &secp_ctx, &privkeys[7], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 13,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[4], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 6,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[4], &privkeys[3], ChannelFeatures::from_le_bytes(id_to_feature_flags(11)), 11);
update_channel(&gossip_sync, &secp_ctx, &privkeys[4], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 11,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[3], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 11,
timestamp: 1,
flags: 1,
add_channel(&gossip_sync, &secp_ctx, &privkeys[2], &privkeys[5], ChannelFeatures::from_le_bytes(id_to_feature_flags(7)), 7);
update_channel(&gossip_sync, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 1,
flags: 0,
excess_data: Vec::new()
});
update_channel(&gossip_sync, &secp_ctx, &privkeys[5], UnsignedChannelUpdate {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
short_channel_id: 7,
timestamp: 1,
flags: 1,
//! channel matches a UTXO on-chain, requiring at least some marginal on-chain transacting in
//! order to announce a channel. This module handles that checking.
-use bitcoin::{BlockHash, TxOut};
+use bitcoin::TxOut;
+use bitcoin::blockdata::constants::ChainHash;
use bitcoin::hashes::hex::ToHex;
use crate::events::MessageSendEvent;
/// The `UtxoLookup` trait defines behavior for accessing on-chain UTXOs.
pub trait UtxoLookup {
/// Returns the transaction output of a funding transaction encoded by [`short_channel_id`].
- /// Returns an error if `genesis_hash` is for a different chain or if such a transaction output
- /// is unknown.
+ /// Returns an error if `chain_hash` is for a different chain or if such a transaction output is
+ /// unknown.
///
/// [`short_channel_id`]: https://github.com/lightning/bolts/blob/master/07-routing-gossip.md#definition-of-short_channel_id
- fn get_utxo(&self, genesis_hash: &BlockHash, short_channel_id: u64) -> UtxoResult;
+ fn get_utxo(&self, chain_hash: &ChainHash, short_channel_id: u64) -> UtxoResult;
}
enum ChannelAnnouncement {
/// once we have a concrete resolution of a request.
pub(crate) struct UtxoResolver(Result<TxOut, UtxoLookupError>);
impl UtxoLookup for UtxoResolver {
- fn get_utxo(&self, _genesis_hash: &BlockHash, _short_channel_id: u64) -> UtxoResult {
+ fn get_utxo(&self, _chain_hash: &ChainHash, _short_channel_id: u64) -> UtxoResult {
UtxoResult::Sync(self.0.clone())
}
}
/// funder/initiator.
///
/// When we are the funder, because we have to pay the channel closing fee, we bound the
- /// acceptable fee by our [`Background`] and [`Normal`] fees, with the upper bound increased by
+ /// acceptable fee by our [`ChannelCloseMinimum`] and [`NonAnchorChannelFee`] fees, with the upper bound increased by
/// this value. Because the on-chain fee we'd pay to force-close the channel is kept near our
- /// [`Normal`] feerate during normal operation, this value represents the additional fee we're
+ /// [`NonAnchorChannelFee`] feerate during normal operation, this value represents the additional fee we're
/// willing to pay in order to avoid waiting for our counterparty's to_self_delay to reclaim our
/// funds.
///
/// When we are not the funder, we require the closing transaction fee pay at least our
- /// [`Background`] fee estimate, but allow our counterparty to pay as much fee as they like.
+ /// [`ChannelCloseMinimum`] fee estimate, but allow our counterparty to pay as much fee as they like.
/// Thus, this value is ignored when we are not the funder.
///
/// Default value: 1000 satoshis.
///
- /// [`Normal`]: crate::chain::chaininterface::ConfirmationTarget::Normal
- /// [`Background`]: crate::chain::chaininterface::ConfirmationTarget::Background
+ /// [`NonAnchorChannelFee`]: crate::chain::chaininterface::ConfirmationTarget::NonAnchorChannelFee
+ /// [`ChannelCloseMinimum`]: crate::chain::chaininterface::ConfirmationTarget::ChannelCloseMinimum
pub force_close_avoidance_max_fee_satoshis: u64,
/// If set, allows this channel's counterparty to skim an additional fee off this node's inbound
/// HTLCs. Useful for liquidity providers to offload on-chain channel costs to end users.
/// 3) payments intended to be intercepted will route using a fake scid (this is typically used so
/// the forwarding node can open a JIT channel to the next hop)
pub(crate) mod fake_scid {
- use bitcoin::hash_types::BlockHash;
- use bitcoin::hashes::hex::FromHex;
+ use bitcoin::blockdata::constants::ChainHash;
+ use bitcoin::network::constants::Network;
use crate::sign::EntropySource;
use crate::util::chacha20::ChaCha20;
use crate::util::scid_utils;
/// between segwit activation and the current best known height, and the tx index and output
/// index are also selected from a "reasonable" range. We add this logic because it makes it
/// non-obvious at a glance that the scid is fake, e.g. if it appears in invoice route hints.
- pub(crate) fn get_fake_scid<ES: Deref>(&self, highest_seen_blockheight: u32, genesis_hash: &BlockHash, fake_scid_rand_bytes: &[u8; 32], entropy_source: &ES) -> u64
+ pub(crate) fn get_fake_scid<ES: Deref>(&self, highest_seen_blockheight: u32, chain_hash: &ChainHash, fake_scid_rand_bytes: &[u8; 32], entropy_source: &ES) -> u64
where ES::Target: EntropySource,
{
// Ensure we haven't created a namespace that doesn't fit into the 3 bits we've allocated for
assert!((*self as u8) < MAX_NAMESPACES);
let rand_bytes = entropy_source.get_secure_random_bytes();
- let segwit_activation_height = segwit_activation_height(genesis_hash);
+ let segwit_activation_height = segwit_activation_height(chain_hash);
let mut blocks_since_segwit_activation = highest_seen_blockheight.saturating_sub(segwit_activation_height);
// We want to ensure that this fake channel won't conflict with any transactions we haven't
}
}
- fn segwit_activation_height(genesis: &BlockHash) -> u32 {
- const MAINNET_GENESIS_STR: &'static str = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f";
- if BlockHash::from_hex(MAINNET_GENESIS_STR).unwrap() == *genesis {
+ fn segwit_activation_height(chain_hash: &ChainHash) -> u32 {
+ if *chain_hash == ChainHash::using_genesis_block(Network::Bitcoin) {
MAINNET_SEGWIT_ACTIVATION_HEIGHT
} else {
TEST_SEGWIT_ACTIVATION_HEIGHT
}
/// Returns whether the given fake scid falls into the phantom namespace.
- pub fn is_valid_phantom(fake_scid_rand_bytes: &[u8; 32], scid: u64, genesis_hash: &BlockHash) -> bool {
+ pub fn is_valid_phantom(fake_scid_rand_bytes: &[u8; 32], scid: u64, chain_hash: &ChainHash) -> bool {
let block_height = scid_utils::block_from_scid(&scid);
let tx_index = scid_utils::tx_index_from_scid(&scid);
let namespace = Namespace::Phantom;
let valid_vout = namespace.get_encrypted_vout(block_height, tx_index, fake_scid_rand_bytes);
- block_height >= segwit_activation_height(genesis_hash)
+ block_height >= segwit_activation_height(chain_hash)
&& valid_vout == scid_utils::vout_from_scid(&scid) as u8
}
/// Returns whether the given fake scid falls into the intercept namespace.
- pub fn is_valid_intercept(fake_scid_rand_bytes: &[u8; 32], scid: u64, genesis_hash: &BlockHash) -> bool {
+ pub fn is_valid_intercept(fake_scid_rand_bytes: &[u8; 32], scid: u64, chain_hash: &ChainHash) -> bool {
let block_height = scid_utils::block_from_scid(&scid);
let tx_index = scid_utils::tx_index_from_scid(&scid);
let namespace = Namespace::Intercept;
let valid_vout = namespace.get_encrypted_vout(block_height, tx_index, fake_scid_rand_bytes);
- block_height >= segwit_activation_height(genesis_hash)
+ block_height >= segwit_activation_height(chain_hash)
&& valid_vout == scid_utils::vout_from_scid(&scid) as u8
}
#[cfg(test)]
mod tests {
- use bitcoin::blockdata::constants::genesis_block;
+ use bitcoin::blockdata::constants::ChainHash;
use bitcoin::network::constants::Network;
use crate::util::scid_utils::fake_scid::{is_valid_intercept, is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
use crate::util::scid_utils;
#[test]
fn test_segwit_activation_height() {
- let mainnet_genesis = genesis_block(Network::Bitcoin).header.block_hash();
+ let mainnet_genesis = ChainHash::using_genesis_block(Network::Bitcoin);
assert_eq!(segwit_activation_height(&mainnet_genesis), MAINNET_SEGWIT_ACTIVATION_HEIGHT);
- let testnet_genesis = genesis_block(Network::Testnet).header.block_hash();
+ let testnet_genesis = ChainHash::using_genesis_block(Network::Testnet);
assert_eq!(segwit_activation_height(&testnet_genesis), TEST_SEGWIT_ACTIVATION_HEIGHT);
- let signet_genesis = genesis_block(Network::Signet).header.block_hash();
+ let signet_genesis = ChainHash::using_genesis_block(Network::Signet);
assert_eq!(segwit_activation_height(&signet_genesis), TEST_SEGWIT_ACTIVATION_HEIGHT);
- let regtest_genesis = genesis_block(Network::Regtest).header.block_hash();
+ let regtest_genesis = ChainHash::using_genesis_block(Network::Regtest);
assert_eq!(segwit_activation_height(®test_genesis), TEST_SEGWIT_ACTIVATION_HEIGHT);
}
fn test_is_valid_phantom() {
let namespace = Namespace::Phantom;
let fake_scid_rand_bytes = [0; 32];
- let testnet_genesis = genesis_block(Network::Testnet).header.block_hash();
+ let testnet_genesis = ChainHash::using_genesis_block(Network::Testnet);
let valid_encrypted_vout = namespace.get_encrypted_vout(0, 0, &fake_scid_rand_bytes);
let valid_fake_scid = scid_utils::scid_from_parts(1, 0, valid_encrypted_vout as u64).unwrap();
assert!(is_valid_phantom(&fake_scid_rand_bytes, valid_fake_scid, &testnet_genesis));
fn test_is_valid_intercept() {
let namespace = Namespace::Intercept;
let fake_scid_rand_bytes = [0; 32];
- let testnet_genesis = genesis_block(Network::Testnet).header.block_hash();
+ let testnet_genesis = ChainHash::using_genesis_block(Network::Testnet);
let valid_encrypted_vout = namespace.get_encrypted_vout(0, 0, &fake_scid_rand_bytes);
let valid_fake_scid = scid_utils::scid_from_parts(1, 0, valid_encrypted_vout as u64).unwrap();
assert!(is_valid_intercept(&fake_scid_rand_bytes, valid_fake_scid, &testnet_genesis));
#[test]
fn test_get_fake_scid() {
- let mainnet_genesis = genesis_block(Network::Bitcoin).header.block_hash();
+ let mainnet_genesis = ChainHash::using_genesis_block(Network::Bitcoin);
let seed = [0; 32];
let fake_scid_rand_bytes = [1; 32];
let keys_manager = Arc::new(test_utils::TestKeysInterface::new(&seed, Network::Testnet));
(&self.0).len() as u8
}
}
+
+impl core::fmt::Display for Hostname {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ write!(f, "{}", self.0)?;
+ Ok(())
+ }
+}
impl Deref for Hostname {
type Target = String;
pub sat_per_kw: Mutex<u32>,
}
impl chaininterface::FeeEstimator for TestFeeEstimator {
- fn get_est_sat_per_1000_weight(&self, _confirmation_target: ConfirmationTarget) -> u32 {
- *self.sat_per_kw.lock().unwrap()
+ fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 {
+ match confirmation_target {
+ ConfirmationTarget::MaxAllowedNonAnchorChannelRemoteFee => {
+ core::cmp::max(25 * 250, *self.sat_per_kw.lock().unwrap() * 10)
+ }
+ _ => *self.sat_per_kw.lock().unwrap(),
+ }
}
}
expected_recv_msgs: Mutex<Option<Vec<wire::Message<()>>>>,
connected_peers: Mutex<HashSet<PublicKey>>,
pub message_fetch_counter: AtomicUsize,
- genesis_hash: ChainHash,
+ chain_hash: ChainHash,
}
impl TestChannelMessageHandler {
- pub fn new(genesis_hash: ChainHash) -> Self {
+ pub fn new(chain_hash: ChainHash) -> Self {
TestChannelMessageHandler {
pending_events: Mutex::new(Vec::new()),
expected_recv_msgs: Mutex::new(None),
connected_peers: Mutex::new(HashSet::new()),
message_fetch_counter: AtomicUsize::new(0),
- genesis_hash,
+ chain_hash,
}
}
channelmanager::provided_init_features(&UserConfig::default())
}
- fn get_genesis_hashes(&self) -> Option<Vec<ChainHash>> {
- Some(vec![self.genesis_hash])
+ fn get_chain_hashes(&self) -> Option<Vec<ChainHash>> {
+ Some(vec![self.chain_hash])
}
fn handle_open_channel_v2(&self, _their_node_id: &PublicKey, msg: &msgs::OpenChannelV2) {
let node_2_btckey = SecretKey::from_slice(&[39; 32]).unwrap();
let unsigned_ann = msgs::UnsignedChannelAnnouncement {
features: ChannelFeatures::empty(),
- chain_hash: genesis_block(network).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(network),
short_channel_id: short_chan_id,
node_id_1: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_1_privkey)),
node_id_2: NodeId::from_pubkey(&PublicKey::from_secret_key(&secp_ctx, &node_2_privkey)),
msgs::ChannelUpdate {
signature: Signature::from(unsafe { FFISignature::new() }),
contents: msgs::UnsignedChannelUpdate {
- chain_hash: genesis_block(network).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(network),
short_channel_id: short_chan_id,
timestamp: 0,
flags: 0,
pending_events.push(events::MessageSendEvent::SendGossipTimestampFilter {
node_id: their_node_id.clone(),
msg: msgs::GossipTimestampFilter {
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ chain_hash: ChainHash::using_genesis_block(Network::Testnet),
first_timestamp: gossip_start_time as u32,
timestamp_range: u32::max_value(),
},
fn log(&self, record: &Record) {
*self.lines.lock().unwrap().entry((record.module_path.to_string(), format!("{}", record.args))).or_insert(0) += 1;
if record.level >= self.level {
- #[cfg(all(not(ldk_bench), feature = "std"))]
- println!("{:<5} {} [{} : {}, {}] {}", record.level.to_string(), self.id, record.module_path, record.file, record.line, record.args);
+ #[cfg(all(not(ldk_bench), feature = "std"))] {
+ let pfx = format!("{} {} [{}:{}]", self.id, record.level.to_string(), record.module_path, record.line);
+ println!("{:<55}{}", pfx, record.args);
+ }
}
}
}
}
pub struct TestChainSource {
- pub genesis_hash: BlockHash,
+ pub chain_hash: ChainHash,
pub utxo_ret: Mutex<UtxoResult>,
pub get_utxo_call_count: AtomicUsize,
pub watched_txn: Mutex<HashSet<(Txid, Script)>>,
pub fn new(network: Network) -> Self {
let script_pubkey = Builder::new().push_opcode(opcodes::OP_TRUE).into_script();
Self {
- genesis_hash: genesis_block(network).block_hash(),
+ chain_hash: ChainHash::using_genesis_block(network),
utxo_ret: Mutex::new(UtxoResult::Sync(Ok(TxOut { value: u64::max_value(), script_pubkey }))),
get_utxo_call_count: AtomicUsize::new(0),
watched_txn: Mutex::new(HashSet::new()),
}
impl UtxoLookup for TestChainSource {
- fn get_utxo(&self, genesis_hash: &BlockHash, _short_channel_id: u64) -> UtxoResult {
+ fn get_utxo(&self, chain_hash: &ChainHash, _short_channel_id: u64) -> UtxoResult {
self.get_utxo_call_count.fetch_add(1, Ordering::Relaxed);
- if self.genesis_hash != *genesis_hash {
+ if self.chain_hash != *chain_hash {
return UtxoResult::Sync(Err(UtxoLookupError::UnknownChain));
}