-# 0.0.109 - 2022-06-30
+# 0.0.109 - 2022-07-01
## API Updates
* `ChannelManager::update_channel_config` has been added to allow the fields
--- /dev/null
+# Security Policy
+
+## Reporting a Vulnerability
+
+Please report security vulnerabilities, including denial-of-service
+vulnerabilities such as spurious panics, via email to
+security@lightningdevkit.org.
+
+If possible, please PGP-encrypt such emails to the following keys (available on
+keyservers and WKD via `gpg --auto-key-locate wkd --locate-external-keys
+ldk-security-1@mattcorallo.com ldk-security-2@mattcorallo.com`). Please include
+your own public key as an attachment or inline for replies.
+
+ * 07DF3E57A548CCFB7530709189BBB8663E2E65CE (Matt Corallo)
+ * 5DBC576CCCF546CA72AB06CE912EF12EA67705F5 (Jeffrey Czyz)
+ * 729E9D9D92C75A5FBFEEE057B5DD717BEF7CA5B1 (Wilmer Paulino)
+ * BD6EED4D339EDBF7E7CE7F8836153082BDF676FD (Elias Rohrer)
+ * 6E0287D8849AE741E47CC586FD3E106A2CE099B4 (Valentine Wallace)
+ * 69CFEA635D0E6E6F13FD9D9136D932FCAC0305F0 (Arik Sosman)
+ * A5A6868D7AA91DD00AC1A67F817FFA028EF61C94 (Antoine Riard)
GEN_TEST msg_onion_hop_data msg_targets::
GEN_TEST msg_ping msg_targets::
GEN_TEST msg_pong msg_targets::
+
+GEN_TEST msg_channel_details msg_targets::
events::Event::PaymentClaimed { .. } => {},
events::Event::PaymentPathSuccessful { .. } => {},
events::Event::PaymentPathFailed { .. } => {},
+ events::Event::ProbeSuccessful { .. } | events::Event::ProbeFailed { .. } => {
+ // Even though we don't explicitly send probes, because probes are
+ // detected based on hashing the payment hash+preimage, its rather
+ // trivial for the fuzzer to build payments that accidentally end up
+ // looking like probes.
+ },
events::Event::PaymentForwarded { .. } if $node == 1 => {},
events::Event::PendingHTLCsForwardable { .. } => {
nodes[$node].process_pending_htlc_forwards();
// Adding new calls to `KeysInterface::get_secure_random_bytes` during startup can change all the
// keys subsequently generated in this test. Rather than regenerating all the messages manually,
// it's easier to just increment the counter here so the keys don't change.
- keys_manager.counter.fetch_sub(1, Ordering::AcqRel);
+ keys_manager.counter.fetch_sub(2, Ordering::AcqRel);
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret(Recipient::Node).unwrap());
let network_graph = Arc::new(NetworkGraph::new(genesis_block(network).block_hash(), Arc::clone(&logger)));
let gossip_sync = Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));
#!/bin/sh
GEN_TEST() {
- tn=msg_$(echo $1 | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\2/g' | tr '[:upper:]' '[:lower:]')
+ tn=msg_$(echo $1 | sed s'/.*:://g' | sed 's/\([a-z0-9]\)\([A-Z]\)/\1_\2/g' | tr '[:upper:]' '[:lower:]')
fn=${tn}.rs
cat msg_target_template.txt | sed s/MSG_TARGET/$1/ | sed "s/TARGET_NAME/$tn/" | sed "s/TEST_MSG/$2/" | sed "s/EXTRA_ARGS/$3/" > $fn
echo "pub mod $tn;" >> mod.rs
# Note when adding new targets here you should add a similar line in src/bin/gen_target.sh
-GEN_TEST AcceptChannel test_msg_simple ""
-GEN_TEST AnnouncementSignatures test_msg_simple ""
-GEN_TEST ClosingSigned test_msg_simple ""
-GEN_TEST CommitmentSigned test_msg_simple ""
-GEN_TEST FundingCreated test_msg_simple ""
-GEN_TEST ChannelReady test_msg_simple ""
-GEN_TEST FundingSigned test_msg_simple ""
-GEN_TEST GossipTimestampFilter test_msg_simple ""
-GEN_TEST Init test_msg_simple ""
-GEN_TEST OnionHopData test_msg_simple ""
-GEN_TEST OpenChannel test_msg_simple ""
-GEN_TEST Ping test_msg_simple ""
-GEN_TEST Pong test_msg_simple ""
-GEN_TEST QueryChannelRange test_msg_simple ""
-GEN_TEST ReplyShortChannelIdsEnd test_msg_simple ""
-GEN_TEST RevokeAndACK test_msg_simple ""
-GEN_TEST Shutdown test_msg_simple ""
-GEN_TEST UpdateAddHTLC test_msg_simple ""
-GEN_TEST UpdateFailHTLC test_msg_simple ""
-GEN_TEST UpdateFailMalformedHTLC test_msg_simple ""
-GEN_TEST UpdateFee test_msg_simple ""
-GEN_TEST UpdateFulfillHTLC test_msg_simple ""
-
-GEN_TEST ChannelReestablish test_msg ""
-GEN_TEST DecodedOnionErrorPacket test_msg ""
-
-GEN_TEST ChannelAnnouncement test_msg_exact ""
-GEN_TEST NodeAnnouncement test_msg_exact ""
-GEN_TEST QueryShortChannelIds test_msg ""
-GEN_TEST ReplyChannelRange test_msg ""
-
-GEN_TEST ErrorMessage test_msg_hole ", 32, 2"
-GEN_TEST WarningMessage test_msg_hole ", 32, 2"
-GEN_TEST ChannelUpdate test_msg_hole ", 108, 1"
+GEN_TEST lightning::ln::msgs::AcceptChannel test_msg_simple ""
+GEN_TEST lightning::ln::msgs::AnnouncementSignatures test_msg_simple ""
+GEN_TEST lightning::ln::msgs::ClosingSigned test_msg_simple ""
+GEN_TEST lightning::ln::msgs::CommitmentSigned test_msg_simple ""
+GEN_TEST lightning::ln::msgs::FundingCreated test_msg_simple ""
+GEN_TEST lightning::ln::msgs::ChannelReady test_msg_simple ""
+GEN_TEST lightning::ln::msgs::FundingSigned test_msg_simple ""
+GEN_TEST lightning::ln::msgs::GossipTimestampFilter test_msg_simple ""
+GEN_TEST lightning::ln::msgs::Init test_msg_simple ""
+GEN_TEST lightning::ln::msgs::OnionHopData test_msg_simple ""
+GEN_TEST lightning::ln::msgs::OpenChannel test_msg_simple ""
+GEN_TEST lightning::ln::msgs::Ping test_msg_simple ""
+GEN_TEST lightning::ln::msgs::Pong test_msg_simple ""
+GEN_TEST lightning::ln::msgs::QueryChannelRange test_msg_simple ""
+GEN_TEST lightning::ln::msgs::ReplyShortChannelIdsEnd test_msg_simple ""
+GEN_TEST lightning::ln::msgs::RevokeAndACK test_msg_simple ""
+GEN_TEST lightning::ln::msgs::Shutdown test_msg_simple ""
+GEN_TEST lightning::ln::msgs::UpdateAddHTLC test_msg_simple ""
+GEN_TEST lightning::ln::msgs::UpdateFailHTLC test_msg_simple ""
+GEN_TEST lightning::ln::msgs::UpdateFailMalformedHTLC test_msg_simple ""
+GEN_TEST lightning::ln::msgs::UpdateFee test_msg_simple ""
+GEN_TEST lightning::ln::msgs::UpdateFulfillHTLC test_msg_simple ""
+
+GEN_TEST lightning::ln::msgs::ChannelReestablish test_msg ""
+GEN_TEST lightning::ln::msgs::DecodedOnionErrorPacket test_msg ""
+
+GEN_TEST lightning::ln::msgs::ChannelAnnouncement test_msg_exact ""
+GEN_TEST lightning::ln::msgs::NodeAnnouncement test_msg_exact ""
+GEN_TEST lightning::ln::msgs::QueryShortChannelIds test_msg ""
+GEN_TEST lightning::ln::msgs::ReplyChannelRange test_msg ""
+
+GEN_TEST lightning::ln::msgs::ErrorMessage test_msg_hole ", 32, 2"
+GEN_TEST lightning::ln::msgs::WarningMessage test_msg_hole ", 32, 2"
+GEN_TEST lightning::ln::msgs::ChannelUpdate test_msg_hole ", 108, 1"
+
+GEN_TEST lightning::ln::channelmanager::ChannelDetails test_msg_simple ""
pub mod msg_error_message;
pub mod msg_warning_message;
pub mod msg_channel_update;
+pub mod msg_channel_details;
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_accept_channel_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::AcceptChannel, data);
+ test_msg_simple!(lightning::ln::msgs::AcceptChannel, data);
}
#[no_mangle]
pub extern "C" fn msg_accept_channel_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::AcceptChannel, data);
+ test_msg_simple!(lightning::ln::msgs::AcceptChannel, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_announcement_signatures_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::AnnouncementSignatures, data);
+ test_msg_simple!(lightning::ln::msgs::AnnouncementSignatures, data);
}
#[no_mangle]
pub extern "C" fn msg_announcement_signatures_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::AnnouncementSignatures, data);
+ test_msg_simple!(lightning::ln::msgs::AnnouncementSignatures, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_channel_announcement_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_exact!(msgs::ChannelAnnouncement, data);
+ test_msg_exact!(lightning::ln::msgs::ChannelAnnouncement, data);
}
#[no_mangle]
pub extern "C" fn msg_channel_announcement_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_exact!(msgs::ChannelAnnouncement, data);
+ test_msg_exact!(lightning::ln::msgs::ChannelAnnouncement, data);
}
--- /dev/null
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on msg_target_template.txt
+// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
+
+use msg_targets::utils::VecWriter;
+use utils::test_logger;
+
+#[inline]
+pub fn msg_channel_details_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+ test_msg_simple!(lightning::ln::channelmanager::ChannelDetails, data);
+}
+
+#[no_mangle]
+pub extern "C" fn msg_channel_details_run(data: *const u8, datalen: usize) {
+ let data = unsafe { std::slice::from_raw_parts(data, datalen) };
+ test_msg_simple!(lightning::ln::channelmanager::ChannelDetails, data);
+}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_channel_ready_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::ChannelReady, data);
+ test_msg_simple!(lightning::ln::msgs::ChannelReady, data);
}
#[no_mangle]
pub extern "C" fn msg_channel_ready_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::ChannelReady, data);
+ test_msg_simple!(lightning::ln::msgs::ChannelReady, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_channel_reestablish_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::ChannelReestablish, data);
+ test_msg!(lightning::ln::msgs::ChannelReestablish, data);
}
#[no_mangle]
pub extern "C" fn msg_channel_reestablish_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg!(msgs::ChannelReestablish, data);
+ test_msg!(lightning::ln::msgs::ChannelReestablish, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_channel_update_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_hole!(msgs::ChannelUpdate, data, 108, 1);
+ test_msg_hole!(lightning::ln::msgs::ChannelUpdate, data, 108, 1);
}
#[no_mangle]
pub extern "C" fn msg_channel_update_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_hole!(msgs::ChannelUpdate, data, 108, 1);
+ test_msg_hole!(lightning::ln::msgs::ChannelUpdate, data, 108, 1);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_closing_signed_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::ClosingSigned, data);
+ test_msg_simple!(lightning::ln::msgs::ClosingSigned, data);
}
#[no_mangle]
pub extern "C" fn msg_closing_signed_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::ClosingSigned, data);
+ test_msg_simple!(lightning::ln::msgs::ClosingSigned, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_commitment_signed_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::CommitmentSigned, data);
+ test_msg_simple!(lightning::ln::msgs::CommitmentSigned, data);
}
#[no_mangle]
pub extern "C" fn msg_commitment_signed_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::CommitmentSigned, data);
+ test_msg_simple!(lightning::ln::msgs::CommitmentSigned, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_decoded_onion_error_packet_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::DecodedOnionErrorPacket, data);
+ test_msg!(lightning::ln::msgs::DecodedOnionErrorPacket, data);
}
#[no_mangle]
pub extern "C" fn msg_decoded_onion_error_packet_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg!(msgs::DecodedOnionErrorPacket, data);
+ test_msg!(lightning::ln::msgs::DecodedOnionErrorPacket, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_error_message_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_hole!(msgs::ErrorMessage, data, 32, 2);
+ test_msg_hole!(lightning::ln::msgs::ErrorMessage, data, 32, 2);
}
#[no_mangle]
pub extern "C" fn msg_error_message_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_hole!(msgs::ErrorMessage, data, 32, 2);
+ test_msg_hole!(lightning::ln::msgs::ErrorMessage, data, 32, 2);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_funding_created_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::FundingCreated, data);
+ test_msg_simple!(lightning::ln::msgs::FundingCreated, data);
}
#[no_mangle]
pub extern "C" fn msg_funding_created_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::FundingCreated, data);
+ test_msg_simple!(lightning::ln::msgs::FundingCreated, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_funding_signed_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::FundingSigned, data);
+ test_msg_simple!(lightning::ln::msgs::FundingSigned, data);
}
#[no_mangle]
pub extern "C" fn msg_funding_signed_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::FundingSigned, data);
+ test_msg_simple!(lightning::ln::msgs::FundingSigned, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_gossip_timestamp_filter_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::GossipTimestampFilter, data);
+ test_msg_simple!(lightning::ln::msgs::GossipTimestampFilter, data);
}
#[no_mangle]
pub extern "C" fn msg_gossip_timestamp_filter_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::GossipTimestampFilter, data);
+ test_msg_simple!(lightning::ln::msgs::GossipTimestampFilter, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_init_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::Init, data);
+ test_msg_simple!(lightning::ln::msgs::Init, data);
}
#[no_mangle]
pub extern "C" fn msg_init_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::Init, data);
+ test_msg_simple!(lightning::ln::msgs::Init, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_node_announcement_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_exact!(msgs::NodeAnnouncement, data);
+ test_msg_exact!(lightning::ln::msgs::NodeAnnouncement, data);
}
#[no_mangle]
pub extern "C" fn msg_node_announcement_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_exact!(msgs::NodeAnnouncement, data);
+ test_msg_exact!(lightning::ln::msgs::NodeAnnouncement, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_onion_hop_data_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::OnionHopData, data);
+ test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
}
#[no_mangle]
pub extern "C" fn msg_onion_hop_data_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::OnionHopData, data);
+ test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_open_channel_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::OpenChannel, data);
+ test_msg_simple!(lightning::ln::msgs::OpenChannel, data);
}
#[no_mangle]
pub extern "C" fn msg_open_channel_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::OpenChannel, data);
+ test_msg_simple!(lightning::ln::msgs::OpenChannel, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_ping_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::Ping, data);
+ test_msg_simple!(lightning::ln::msgs::Ping, data);
}
#[no_mangle]
pub extern "C" fn msg_ping_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::Ping, data);
+ test_msg_simple!(lightning::ln::msgs::Ping, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_pong_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::Pong, data);
+ test_msg_simple!(lightning::ln::msgs::Pong, data);
}
#[no_mangle]
pub extern "C" fn msg_pong_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::Pong, data);
+ test_msg_simple!(lightning::ln::msgs::Pong, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_query_channel_range_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::QueryChannelRange, data);
+ test_msg_simple!(lightning::ln::msgs::QueryChannelRange, data);
}
#[no_mangle]
pub extern "C" fn msg_query_channel_range_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::QueryChannelRange, data);
+ test_msg_simple!(lightning::ln::msgs::QueryChannelRange, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_query_short_channel_ids_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::QueryShortChannelIds, data);
+ test_msg!(lightning::ln::msgs::QueryShortChannelIds, data);
}
#[no_mangle]
pub extern "C" fn msg_query_short_channel_ids_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg!(msgs::QueryShortChannelIds, data);
+ test_msg!(lightning::ln::msgs::QueryShortChannelIds, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_reply_channel_range_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::ReplyChannelRange, data);
+ test_msg!(lightning::ln::msgs::ReplyChannelRange, data);
}
#[no_mangle]
pub extern "C" fn msg_reply_channel_range_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg!(msgs::ReplyChannelRange, data);
+ test_msg!(lightning::ln::msgs::ReplyChannelRange, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_reply_short_channel_ids_end_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::ReplyShortChannelIdsEnd, data);
+ test_msg_simple!(lightning::ln::msgs::ReplyShortChannelIdsEnd, data);
}
#[no_mangle]
pub extern "C" fn msg_reply_short_channel_ids_end_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::ReplyShortChannelIdsEnd, data);
+ test_msg_simple!(lightning::ln::msgs::ReplyShortChannelIdsEnd, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_revoke_and_ack_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::RevokeAndACK, data);
+ test_msg_simple!(lightning::ln::msgs::RevokeAndACK, data);
}
#[no_mangle]
pub extern "C" fn msg_revoke_and_ack_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::RevokeAndACK, data);
+ test_msg_simple!(lightning::ln::msgs::RevokeAndACK, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_shutdown_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::Shutdown, data);
+ test_msg_simple!(lightning::ln::msgs::Shutdown, data);
}
#[no_mangle]
pub extern "C" fn msg_shutdown_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::Shutdown, data);
+ test_msg_simple!(lightning::ln::msgs::Shutdown, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn TARGET_NAME_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- TEST_MSG!(msgs::MSG_TARGET, dataEXTRA_ARGS);
+ TEST_MSG!(MSG_TARGET, dataEXTRA_ARGS);
}
#[no_mangle]
pub extern "C" fn TARGET_NAME_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- TEST_MSG!(msgs::MSG_TARGET, dataEXTRA_ARGS);
+ TEST_MSG!(MSG_TARGET, dataEXTRA_ARGS);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_update_add_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::UpdateAddHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateAddHTLC, data);
}
#[no_mangle]
pub extern "C" fn msg_update_add_htlc_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::UpdateAddHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateAddHTLC, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_update_fail_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::UpdateFailHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFailHTLC, data);
}
#[no_mangle]
pub extern "C" fn msg_update_fail_htlc_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::UpdateFailHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFailHTLC, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_update_fail_malformed_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::UpdateFailMalformedHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFailMalformedHTLC, data);
}
#[no_mangle]
pub extern "C" fn msg_update_fail_malformed_htlc_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::UpdateFailMalformedHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFailMalformedHTLC, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_update_fee_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::UpdateFee, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFee, data);
}
#[no_mangle]
pub extern "C" fn msg_update_fee_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::UpdateFee, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFee, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_update_fulfill_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_simple!(msgs::UpdateFulfillHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFulfillHTLC, data);
}
#[no_mangle]
pub extern "C" fn msg_update_fulfill_htlc_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_simple!(msgs::UpdateFulfillHTLC, data);
+ test_msg_simple!(lightning::ln::msgs::UpdateFulfillHTLC, data);
}
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
-use lightning::ln::msgs;
-
use msg_targets::utils::VecWriter;
use utils::test_logger;
#[inline]
pub fn msg_warning_message_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_hole!(msgs::WarningMessage, data, 32, 2);
+ test_msg_hole!(lightning::ln::msgs::WarningMessage, data, 32, 2);
}
#[no_mangle]
pub extern "C" fn msg_warning_message_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg_hole!(msgs::WarningMessage, data, 32, 2);
+ test_msg_hole!(lightning::ln::msgs::WarningMessage, data, 32, 2);
}
// Imports that need to be added manually
-use lightning::util::logger::Logger;
use lightning_rapid_gossip_sync::RapidGossipSync;
use utils::test_logger;
-use std::sync::Arc;
-
/// Actual fuzz test, method signature and name are fixed
fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
let block_hash = bitcoin::BlockHash::default();
use lightning::routing::gossip::{NetworkGraph, RoutingFees};
use lightning::routing::router::{find_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
use lightning::routing::scoring::FixedPenaltyScorer;
-use lightning::util::logger::Logger;
use lightning::util::ser::Readable;
use bitcoin::hashes::Hash;
void msg_onion_hop_data_run(const unsigned char* data, size_t data_len);
void msg_ping_run(const unsigned char* data, size_t data_len);
void msg_pong_run(const unsigned char* data, size_t data_len);
+void msg_channel_details_run(const unsigned char* data, size_t data_len);
//! # ) -> u64 { 0 }
//! # fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
//! # fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+//! # fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+//! # fn probe_successful(&mut self, _path: &[&RouteHop]) {}
//! # }
//! #
//! # struct FakeLogger {}
.map_or(1, |attempts| attempts.count + 1);
log_trace!(self.logger, "Payment {} succeeded (attempts: {})", log_bytes!(payment_hash.0), attempts);
},
+ Event::ProbeSuccessful { payment_hash, path, .. } => {
+ log_trace!(self.logger, "Probe payment {} of {}msat was successful", log_bytes!(payment_hash.0), path.last().unwrap().fee_msat);
+ let path = path.iter().collect::<Vec<_>>();
+ self.scorer.lock().probe_successful(&path);
+ },
+ Event::ProbeFailed { payment_hash, path, short_channel_id, .. } => {
+ if let Some(short_channel_id) = short_channel_id {
+ log_trace!(self.logger, "Probe payment {} of {}msat failed at channel {}", log_bytes!(payment_hash.0), path.last().unwrap().fee_msat, *short_channel_id);
+ let path = path.iter().collect::<Vec<_>>();
+ self.scorer.lock().probe_failed(&path, *short_channel_id);
+ }
+ },
_ => {},
}
.expect_send(Amount::ForInvoice(final_value_msat))
.expect_send(Amount::OnRetry(final_value_msat / 2));
let router = TestRouter {};
- let scorer = RefCell::new(TestScorer::new().expect(PaymentPath::Failure {
+ let scorer = RefCell::new(TestScorer::new().expect(TestResult::PaymentFailure {
path: path.clone(), short_channel_id: path[0].short_channel_id,
}));
let logger = TestLogger::new();
let payer = TestPayer::new().expect_send(Amount::ForInvoice(final_value_msat));
let router = TestRouter {};
let scorer = RefCell::new(TestScorer::new()
- .expect(PaymentPath::Success { path: route.paths[0].clone() })
- .expect(PaymentPath::Success { path: route.paths[1].clone() })
+ .expect(TestResult::PaymentSuccess { path: route.paths[0].clone() })
+ .expect(TestResult::PaymentSuccess { path: route.paths[1].clone() })
);
let logger = TestLogger::new();
let invoice_payer =
}
struct TestScorer {
- expectations: Option<VecDeque<PaymentPath>>,
+ expectations: Option<VecDeque<TestResult>>,
}
#[derive(Debug)]
- enum PaymentPath {
- Failure { path: Vec<RouteHop>, short_channel_id: u64 },
- Success { path: Vec<RouteHop> },
+ enum TestResult {
+ PaymentFailure { path: Vec<RouteHop>, short_channel_id: u64 },
+ PaymentSuccess { path: Vec<RouteHop> },
+ ProbeFailure { path: Vec<RouteHop>, short_channel_id: u64 },
+ ProbeSuccess { path: Vec<RouteHop> },
}
impl TestScorer {
}
}
- fn expect(mut self, expectation: PaymentPath) -> Self {
+ fn expect(mut self, expectation: TestResult) -> Self {
self.expectations.get_or_insert_with(|| VecDeque::new()).push_back(expectation);
self
}
fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
if let Some(expectations) = &mut self.expectations {
match expectations.pop_front() {
- Some(PaymentPath::Failure { path, short_channel_id }) => {
+ Some(TestResult::PaymentFailure { path, short_channel_id }) => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
assert_eq!(actual_short_channel_id, short_channel_id);
},
- Some(PaymentPath::Success { path }) => {
+ Some(TestResult::PaymentSuccess { path }) => {
panic!("Unexpected successful payment path: {:?}", path)
},
+ Some(TestResult::ProbeFailure { path, .. }) => {
+ panic!("Unexpected failed payment probe: {:?}", path)
+ },
+ Some(TestResult::ProbeSuccess { path }) => {
+ panic!("Unexpected successful payment probe: {:?}", path)
+ },
None => panic!("Unexpected payment_path_failed call: {:?}", actual_path),
}
}
fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) {
if let Some(expectations) = &mut self.expectations {
match expectations.pop_front() {
- Some(PaymentPath::Failure { path, .. }) => {
+ Some(TestResult::PaymentFailure { path, .. }) => {
panic!("Unexpected payment path failure: {:?}", path)
},
- Some(PaymentPath::Success { path }) => {
+ Some(TestResult::PaymentSuccess { path }) => {
+ assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
+ },
+ Some(TestResult::ProbeFailure { path, .. }) => {
+ panic!("Unexpected failed payment probe: {:?}", path)
+ },
+ Some(TestResult::ProbeSuccess { path }) => {
+ panic!("Unexpected successful payment probe: {:?}", path)
+ },
+ None => panic!("Unexpected payment_path_successful call: {:?}", actual_path),
+ }
+ }
+ }
+
+ fn probe_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
+ if let Some(expectations) = &mut self.expectations {
+ match expectations.pop_front() {
+ Some(TestResult::PaymentFailure { path, .. }) => {
+ panic!("Unexpected failed payment path: {:?}", path)
+ },
+ Some(TestResult::PaymentSuccess { path }) => {
+ panic!("Unexpected successful payment path: {:?}", path)
+ },
+ Some(TestResult::ProbeFailure { path, short_channel_id }) => {
+ assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
+ assert_eq!(actual_short_channel_id, short_channel_id);
+ },
+ Some(TestResult::ProbeSuccess { path }) => {
+ panic!("Unexpected successful payment probe: {:?}", path)
+ },
+ None => panic!("Unexpected payment_path_failed call: {:?}", actual_path),
+ }
+ }
+ }
+ fn probe_successful(&mut self, actual_path: &[&RouteHop]) {
+ if let Some(expectations) = &mut self.expectations {
+ match expectations.pop_front() {
+ Some(TestResult::PaymentFailure { path, .. }) => {
+ panic!("Unexpected payment path failure: {:?}", path)
+ },
+ Some(TestResult::PaymentSuccess { path }) => {
+ panic!("Unexpected successful payment path: {:?}", path)
+ },
+ Some(TestResult::ProbeFailure { path, .. }) => {
+ panic!("Unexpected failed payment probe: {:?}", path)
+ },
+ Some(TestResult::ProbeSuccess { path }) => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
},
None => panic!("Unexpected payment_path_successful call: {:?}", actual_path),
/// [fake scids]: crate::util::scid_utils::fake_scid
fake_scid_rand_bytes: [u8; 32],
+ /// When we send payment probes, we generate the [`PaymentHash`] based on this cookie secret
+ /// and a random [`PaymentId`]. This allows us to discern probes from real payments, without
+ /// keeping additional state.
+ probing_cookie_secret: [u8; 32],
+
/// Used to track the last value sent in a node_announcement "timestamp" field. We ensure this
/// value increases strictly since we don't assume access to a time source.
last_node_announcement_serial: AtomicUsize,
inbound_payment_key: expanded_inbound_key,
fake_scid_rand_bytes: keys_manager.get_secure_random_bytes(),
+ probing_cookie_secret: keys_manager.get_secure_random_bytes(),
+
last_node_announcement_serial: AtomicUsize::new(0),
highest_seen_timestamp: AtomicUsize::new(0),
}
}
+ /// Send a payment that is probing the given route for liquidity. We calculate the
+ /// [`PaymentHash`] of probes based on a static secret and a random [`PaymentId`], which allows
+ /// us to easily discern them from real payments.
+ pub fn send_probe(&self, hops: Vec<RouteHop>) -> Result<(PaymentHash, PaymentId), PaymentSendFailure> {
+ let payment_id = PaymentId(self.keys_manager.get_secure_random_bytes());
+
+ let payment_hash = self.probing_cookie_from_id(&payment_id);
+
+ if hops.len() < 2 {
+ return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
+ err: "No need probing a path with less than two hops".to_string()
+ }))
+ }
+
+ let route = Route { paths: vec![hops], payment_params: None };
+
+ match self.send_payment_internal(&route, payment_hash, &None, None, Some(payment_id), None) {
+ Ok(payment_id) => Ok((payment_hash, payment_id)),
+ Err(e) => Err(e)
+ }
+ }
+
+ /// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
+ /// payment probe.
+ pub(crate) fn payment_is_probe(&self, payment_hash: &PaymentHash, payment_id: &PaymentId) -> bool {
+ let target_payment_hash = self.probing_cookie_from_id(payment_id);
+ target_payment_hash == *payment_hash
+ }
+
+ /// Returns the 'probing cookie' for the given [`PaymentId`].
+ fn probing_cookie_from_id(&self, payment_id: &PaymentId) -> PaymentHash {
+ let mut preimage = [0u8; 64];
+ preimage[..32].copy_from_slice(&self.probing_cookie_secret);
+ preimage[32..].copy_from_slice(&payment_id.0);
+ PaymentHash(Sha256::hash(&preimage).into_inner())
+ }
+
/// Handles the generation of a funding transaction, optionally (for tests) with a function
/// which checks the correctness of the funding transaction given the associated channel.
fn funding_transaction_generated_intern<FundingOutput: Fn(&Channel<Signer>, &Transaction) -> Result<OutPoint, APIError>>(
#[allow(dead_code)]
// Messages of up to 64KB should never end up more than half full with addresses, as that would
- // be absurd. We ensure this by checking that at least 500 (our stated public contract on when
+ // be absurd. We ensure this by checking that at least 100 (our stated public contract on when
// broadcast_node_announcement panics) of the maximum-length addresses would fit in a 64KB
// message...
const HALF_MESSAGE_IS_ADDRS: u32 = ::core::u16::MAX as u32 / (NetAddress::MAX_LEN as u32 + 1) / 2;
#[deny(const_err)]
#[allow(dead_code)]
// ...by failing to compile if the number of addresses that would be half of a message is
- // smaller than 500:
- const STATIC_ASSERT: u32 = Self::HALF_MESSAGE_IS_ADDRS - 500;
+ // smaller than 100:
+ const STATIC_ASSERT: u32 = Self::HALF_MESSAGE_IS_ADDRS - 100;
/// Regenerates channel_announcements and generates a signed node_announcement from the given
/// arguments, providing them in corresponding events via
/// tying these addresses together and to this node. If you wish to preserve user privacy,
/// addresses should likely contain only Tor Onion addresses.
///
- /// Panics if `addresses` is absurdly large (more than 500).
+ /// Panics if `addresses` is absurdly large (more than 100).
///
/// [`get_and_clear_pending_msg_events`]: MessageSendEventsProvider::get_and_clear_pending_msg_events
pub fn broadcast_node_announcement(&self, rgb: [u8; 3], alias: [u8; 32], mut addresses: Vec<NetAddress>) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- if addresses.len() > 500 {
+ if addresses.len() > 100 {
panic!("More than half the message size was taken up by public addresses!");
}
let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
#[cfg(not(test))]
let (network_update, short_channel_id, payment_retryable, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
- // TODO: If we decided to blame ourselves (or one of our channels) in
- // process_onion_failure we should close that channel as it implies our
- // next-hop is needlessly blaming us!
- events::Event::PaymentPathFailed {
- payment_id: Some(payment_id),
- payment_hash: payment_hash.clone(),
- rejected_by_dest: !payment_retryable,
- network_update,
- all_paths_failed,
- path: path.clone(),
- short_channel_id,
- retry,
-#[cfg(test)]
- error_code: onion_error_code,
-#[cfg(test)]
- error_data: onion_error_data
+
+ if self.payment_is_probe(payment_hash, &payment_id) {
+ if !payment_retryable {
+ events::Event::ProbeSuccessful {
+ payment_id,
+ payment_hash: payment_hash.clone(),
+ path: path.clone(),
+ }
+ } else {
+ events::Event::ProbeFailed {
+ payment_id: payment_id,
+ payment_hash: payment_hash.clone(),
+ path: path.clone(),
+ short_channel_id,
+ }
+ }
+ } else {
+ // TODO: If we decided to blame ourselves (or one of our channels) in
+ // process_onion_failure we should close that channel as it implies our
+ // next-hop is needlessly blaming us!
+ events::Event::PaymentPathFailed {
+ payment_id: Some(payment_id),
+ payment_hash: payment_hash.clone(),
+ rejected_by_dest: !payment_retryable,
+ network_update,
+ all_paths_failed,
+ path: path.clone(),
+ short_channel_id,
+ retry,
+ #[cfg(test)]
+ error_code: onion_error_code,
+ #[cfg(test)]
+ error_data: onion_error_data
+ }
}
},
&HTLCFailReason::Reason {
(18, outbound_capacity_msat, required),
// Note that by the time we get past the required read above, outbound_capacity_msat will be
// filled in, so we can safely unwrap it here.
- (19, next_outbound_htlc_limit_msat, (default_value, outbound_capacity_msat.0.unwrap())),
+ (19, next_outbound_htlc_limit_msat, (default_value, outbound_capacity_msat.0.unwrap() as u64)),
(20, inbound_capacity_msat, required),
(22, confirmations_required, option),
(24, force_close_spend_delay, option),
(5, self.our_network_pubkey, required),
(7, self.fake_scid_rand_bytes, required),
(9, htlc_purposes, vec_type),
+ (11, self.probing_cookie_secret, required),
});
Ok(())
let mut pending_outbound_payments = None;
let mut received_network_pubkey: Option<PublicKey> = None;
let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
+ let mut probing_cookie_secret: Option<[u8; 32]> = None;
let mut claimable_htlc_purposes = None;
read_tlv_fields!(reader, {
(1, pending_outbound_payments_no_retry, option),
(5, received_network_pubkey, option),
(7, fake_scid_rand_bytes, option),
(9, claimable_htlc_purposes, vec_type),
+ (11, probing_cookie_secret, option),
});
if fake_scid_rand_bytes.is_none() {
fake_scid_rand_bytes = Some(args.keys_manager.get_secure_random_bytes());
}
+ if probing_cookie_secret.is_none() {
+ probing_cookie_secret = Some(args.keys_manager.get_secure_random_bytes());
+ }
+
if pending_outbound_payments.is_none() && pending_outbound_payments_no_retry.is_none() {
pending_outbound_payments = Some(pending_outbound_payments_compat);
} else if pending_outbound_payments.is_none() {
outbound_scid_aliases: Mutex::new(outbound_scid_aliases),
fake_scid_rand_bytes: fake_scid_rand_bytes.unwrap(),
+ probing_cookie_secret: probing_cookie_secret.unwrap(),
+
our_network_key,
our_network_pubkey,
secp_ctx,
/// payment_hash type, use to cross-lock hop
/// (C-not exported) as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
-pub struct PaymentHash(pub [u8;32]);
+pub struct PaymentHash(pub [u8; 32]);
/// payment_preimage type, use to route payment between hop
/// (C-not exported) as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
-pub struct PaymentPreimage(pub [u8;32]);
+pub struct PaymentPreimage(pub [u8; 32]);
/// payment_secret type, use to authenticate sender to the receiver and tie MPP HTLCs together
/// (C-not exported) as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
-pub struct PaymentSecret(pub [u8;32]);
+pub struct PaymentSecret(pub [u8; 32]);
use prelude::*;
use bitcoin::bech32;
use util::events::MessageSendEventsProvider;
use util::logger;
-use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt};
+use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
/// The port on which the node is listening
port: u16,
},
+ /// A hostname/port on which the peer is listening.
+ Hostname {
+ /// The hostname on which the node is listening.
+ hostname: Hostname,
+ /// The port on which the node is listening.
+ port: u16,
+ },
}
impl NetAddress {
/// Gets the ID of this address type. Addresses in node_announcement messages should be sorted
&NetAddress::IPv6 {..} => { 2 },
&NetAddress::OnionV2(_) => { 3 },
&NetAddress::OnionV3 {..} => { 4 },
+ &NetAddress::Hostname {..} => { 5 },
}
}
&NetAddress::IPv6 { .. } => { 18 },
&NetAddress::OnionV2(_) => { 12 },
&NetAddress::OnionV3 { .. } => { 37 },
+ // Consists of 1-byte hostname length, hostname bytes, and 2-byte port.
+ &NetAddress::Hostname { ref hostname, .. } => { u16::from(hostname.len()) + 3 },
}
}
- /// The maximum length of any address descriptor, not including the 1-byte type
- pub(crate) const MAX_LEN: u16 = 37;
+ /// The maximum length of any address descriptor, not including the 1-byte type.
+ /// This maximum length is reached by a hostname address descriptor:
+ /// a hostname with a maximum length of 255, its 1-byte length and a 2-byte port.
+ pub(crate) const MAX_LEN: u16 = 258;
}
impl Writeable for NetAddress {
checksum.write(writer)?;
version.write(writer)?;
port.write(writer)?;
- }
+ },
+ &NetAddress::Hostname { ref hostname, ref port } => {
+ 5u8.write(writer)?;
+ hostname.write(writer)?;
+ port.write(writer)?;
+ },
}
Ok(())
}
port: Readable::read(reader)?,
}))
},
+ 5 => {
+ Ok(Ok(NetAddress::Hostname {
+ hostname: Readable::read(reader)?,
+ port: Readable::read(reader)?,
+ }))
+ },
_ => return Ok(Err(byte)),
}
}
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
use ln::msgs;
use ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
- use util::ser::{Writeable, Readable};
+ use util::ser::{Writeable, Readable, Hostname};
use bitcoin::hashes::hex::FromHex;
use bitcoin::util::address::Address;
use io::Cursor;
use prelude::*;
+ use core::convert::TryFrom;
#[test]
fn encoding_channel_reestablish_no_secret() {
do_encoding_channel_announcement(true, true);
}
- fn do_encoding_node_announcement(unknown_features_bits: bool, ipv4: bool, ipv6: bool, onionv2: bool, onionv3: bool, excess_address_data: bool, excess_data: bool) {
+ fn do_encoding_node_announcement(unknown_features_bits: bool, ipv4: bool, ipv6: bool, onionv2: bool, onionv3: bool, hostname: bool, excess_address_data: bool, excess_data: bool) {
let secp_ctx = Secp256k1::new();
let (privkey_1, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
port: 9735
});
}
+ if hostname {
+ addresses.push(msgs::NetAddress::Hostname {
+ hostname: Hostname::try_from(String::from("host")).unwrap(),
+ port: 9735,
+ });
+ }
let mut addr_len = 0;
for addr in &addresses {
addr_len += addr.len() + 1;
if onionv3 {
target_value.append(&mut hex::decode("04fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e00020102607").unwrap());
}
+ if hostname {
+ target_value.append(&mut hex::decode("0504686f73742607").unwrap());
+ }
if excess_address_data {
target_value.append(&mut hex::decode("216c280b5395a2546e7e4b2663e04f811622f15a4f92e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d269").unwrap());
}
#[test]
fn encoding_node_announcement() {
- do_encoding_node_announcement(true, true, true, true, true, true, true);
- do_encoding_node_announcement(false, false, false, false, false, false, false);
- do_encoding_node_announcement(false, true, false, false, false, false, false);
- do_encoding_node_announcement(false, false, true, false, false, false, false);
- do_encoding_node_announcement(false, false, false, true, false, false, false);
- do_encoding_node_announcement(false, false, false, false, true, false, false);
- do_encoding_node_announcement(false, false, false, false, false, true, false);
- do_encoding_node_announcement(false, true, false, true, false, true, false);
- do_encoding_node_announcement(false, false, true, false, true, false, false);
+ do_encoding_node_announcement(true, true, true, true, true, true, true, true);
+ do_encoding_node_announcement(false, false, false, false, false, false, false, false);
+ do_encoding_node_announcement(false, true, false, false, false, false, false, false);
+ do_encoding_node_announcement(false, false, true, false, false, false, false, false);
+ do_encoding_node_announcement(false, false, false, true, false, false, false, false);
+ do_encoding_node_announcement(false, false, false, false, true, false, false, false);
+ do_encoding_node_announcement(false, false, false, false, false, true, false, false);
+ do_encoding_node_announcement(false, false, false, false, false, false, true, false);
+ do_encoding_node_announcement(false, true, false, true, false, false, true, false);
+ do_encoding_node_announcement(false, false, true, false, true, false, false, false);
}
fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool, excess_data: bool) {
pass_along_path(&nodes[0], &[&nodes[1]], amt_msat, payment_hash, Some(payment_secret), events.pop().unwrap(), true, Some(payment_preimage));
claim_payment_along_route(&nodes[0], &[&[&nodes[1]]], false, payment_preimage);
}
+
+#[test]
+fn sent_probe_is_probe_of_sending_node() {
+ 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, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+ create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
+
+ // First check we refuse to build a single-hop probe
+ let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[1], 100_000);
+ assert!(nodes[0].node.send_probe(route.paths[0].clone()).is_err());
+
+ // Then build an actual two-hop probing path
+ let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], 100_000);
+
+ match nodes[0].node.send_probe(route.paths[0].clone()) {
+ Ok((payment_hash, payment_id)) => {
+ assert!(nodes[0].node.payment_is_probe(&payment_hash, &payment_id));
+ assert!(!nodes[1].node.payment_is_probe(&payment_hash, &payment_id));
+ assert!(!nodes[2].node.payment_is_probe(&payment_hash, &payment_id));
+ },
+ _ => panic!(),
+ }
+
+ get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ check_added_monitors!(nodes[0], 1);
+}
+
+#[test]
+fn successful_probe_yields_event() {
+ 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, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+ create_announced_chan_between_nodes(&nodes, 1, 2, InitFeatures::known(), InitFeatures::known());
+
+ let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], 100_000);
+
+ let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
+
+ // node[0] -- update_add_htlcs -> node[1]
+ check_added_monitors!(nodes[0], 1);
+ let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ let probe_event = SendEvent::from_commitment_update(nodes[1].node.get_our_node_id(), updates);
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &probe_event.msgs[0]);
+ check_added_monitors!(nodes[1], 0);
+ commitment_signed_dance!(nodes[1], nodes[0], probe_event.commitment_msg, false);
+ expect_pending_htlcs_forwardable!(nodes[1]);
+
+ // node[1] -- update_add_htlcs -> node[2]
+ check_added_monitors!(nodes[1], 1);
+ let updates = get_htlc_update_msgs!(nodes[1], nodes[2].node.get_our_node_id());
+ let probe_event = SendEvent::from_commitment_update(nodes[1].node.get_our_node_id(), updates);
+ nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &probe_event.msgs[0]);
+ check_added_monitors!(nodes[2], 0);
+ commitment_signed_dance!(nodes[2], nodes[1], probe_event.commitment_msg, true, true);
+
+ // node[1] <- update_fail_htlcs -- node[2]
+ let updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_update_fail_htlc(&nodes[2].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
+ check_added_monitors!(nodes[1], 0);
+ commitment_signed_dance!(nodes[1], nodes[2], updates.commitment_signed, true);
+
+ // node[0] <- update_fail_htlcs -- node[1]
+ let updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ 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);
+
+ let mut events = nodes[0].node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events.drain(..).next().unwrap() {
+ crate::util::events::Event::ProbeSuccessful { payment_id: ev_pid, payment_hash: ev_ph, .. } => {
+ assert_eq!(payment_id, ev_pid);
+ assert_eq!(payment_hash, ev_ph);
+ },
+ _ => panic!(),
+ };
+}
+
+#[test]
+fn failed_probe_yields_event() {
+ 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, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+ create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 100000, 90000000, InitFeatures::known(), InitFeatures::known());
+
+ let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id());
+
+ let (route, _, _, _) = get_route_and_payment_hash!(&nodes[0], nodes[2], &payment_params, 9_999_000, 42);
+
+ let (payment_hash, payment_id) = nodes[0].node.send_probe(route.paths[0].clone()).unwrap();
+
+ // node[0] -- update_add_htlcs -> node[1]
+ check_added_monitors!(nodes[0], 1);
+ let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ let probe_event = SendEvent::from_commitment_update(nodes[1].node.get_our_node_id(), updates);
+ nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &probe_event.msgs[0]);
+ check_added_monitors!(nodes[1], 0);
+ commitment_signed_dance!(nodes[1], nodes[0], probe_event.commitment_msg, false);
+ expect_pending_htlcs_forwardable!(nodes[1]);
+
+ // node[0] <- update_fail_htlcs -- node[1]
+ check_added_monitors!(nodes[1], 1);
+ let updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+ // Skip the PendingHTLCsForwardable event
+ let _events = nodes[1].node.get_and_clear_pending_events();
+ 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);
+
+ let mut events = nodes[0].node.get_and_clear_pending_events();
+ assert_eq!(events.len(), 1);
+ match events.drain(..).next().unwrap() {
+ crate::util::events::Event::ProbeFailed { payment_id: ev_pid, payment_hash: ev_ph, .. } => {
+ assert_eq!(payment_id, ev_pid);
+ assert_eq!(payment_hash, ev_ph);
+ },
+ _ => panic!(),
+ };
+}
/// Maximum total CTLV difference we allow for a full payment path.
pub const DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA: u32 = 1008;
-/// Maximum number of paths we allow an MPP payment to have.
+/// Maximum number of paths we allow an (MPP) payment to have.
// The default limit is currently set rather arbitrary - there aren't any real fundamental path-count
// limits, but for now more than 10 paths likely carries too much one-path failure.
-pub const DEFAULT_MAX_MPP_PATH_COUNT: u8 = 10;
+pub const DEFAULT_MAX_PATH_COUNT: u8 = 10;
// The median hop CLTV expiry delta currently seen in the network.
const MEDIAN_HOP_CLTV_EXPIRY_DELTA: u32 = 40;
/// Defaults to [`DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA`].
pub max_total_cltv_expiry_delta: u32,
- /// The maximum number of paths that may be used by MPP payments.
- /// Defaults to [`DEFAULT_MAX_MPP_PATH_COUNT`].
- pub max_mpp_path_count: u8,
+ /// The maximum number of paths that may be used by (MPP) payments.
+ /// Defaults to [`DEFAULT_MAX_PATH_COUNT`].
+ pub max_path_count: u8,
}
impl_writeable_tlv_based!(PaymentParameters, {
(0, payee_pubkey, required),
(1, max_total_cltv_expiry_delta, (default_value, DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA)),
(2, features, option),
- (3, max_mpp_path_count, (default_value, DEFAULT_MAX_MPP_PATH_COUNT)),
+ (3, max_path_count, (default_value, DEFAULT_MAX_PATH_COUNT)),
(4, route_hints, vec_type),
(6, expiry_time, option),
});
route_hints: vec![],
expiry_time: None,
max_total_cltv_expiry_delta: DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA,
- max_mpp_path_count: DEFAULT_MAX_MPP_PATH_COUNT,
+ max_path_count: DEFAULT_MAX_PATH_COUNT,
}
}
Self { max_total_cltv_expiry_delta, ..self }
}
- /// Includes a limit for the maximum number of payment paths that may be used by MPP.
+ /// Includes a limit for the maximum number of payment paths that may be used.
///
/// (C-not exported) since bindings don't support move semantics
- pub fn with_max_mpp_path_count(self, max_mpp_path_count: u8) -> Self {
- Self { max_mpp_path_count, ..self }
+ pub fn with_max_path_count(self, max_path_count: u8) -> Self {
+ Self { max_path_count, ..self }
}
}
let network_channels = network_graph.channels();
let network_nodes = network_graph.nodes();
+ if payment_params.max_path_count == 0 {
+ return Err(LightningError{err: "Can't find a route with no paths allowed.".to_owned(), action: ErrorAction::IgnoreError});
+ }
+
// Allow MPP only if we have a features set from somewhere that indicates the payee supports
// it. If the payee supports it they're supposed to include it in the invoice, so that should
// work reliably.
- let allow_mpp = if let Some(features) = &payment_params.features {
+ let allow_mpp = if payment_params.max_path_count == 1 {
+ false
+ } else if let Some(features) = &payment_params.features {
features.supports_basic_mpp()
} else if let Some(node) = network_nodes.get(&payee_node_id) {
if let Some(node_info) = node.announcement_info.as_ref() {
} else { false }
} else { false };
- if allow_mpp && payment_params.max_mpp_path_count == 0 {
- return Err(LightningError{err: "Can't find an MPP route with no paths allowed.".to_owned(), action: ErrorAction::IgnoreError});
- }
-
log_trace!(logger, "Searching for a route from payer {} to payee {} {} MPP and {} first hops {}overriding the network graph", our_node_pubkey,
payment_params.payee_pubkey, if allow_mpp { "with" } else { "without" },
first_hops.map(|hops| hops.len()).unwrap_or(0), if first_hops.is_some() { "" } else { "not " });
// Taking too many smaller paths also increases the chance of payment failure.
// Thus to avoid this effect, we require from our collected links to provide
// at least a minimal contribution to the recommended value yet-to-be-fulfilled.
- // This requirement is currently set to be 1/max_mpp_path_count of the payment
+ // This requirement is currently set to be 1/max_path_count of the payment
// value to ensure we only ever return routes that do not violate this limit.
let minimal_value_contribution_msat: u64 = if allow_mpp {
- (final_value_msat + (payment_params.max_mpp_path_count as u64 - 1)) / payment_params.max_mpp_path_count as u64
+ (final_value_msat + (payment_params.max_path_count as u64 - 1)) / payment_params.max_path_count as u64
} else {
final_value_msat
};
selected_paths.push(path);
}
// Make sure we would never create a route with more paths than we allow.
- debug_assert!(selected_paths.len() <= payment_params.max_mpp_path_count.into());
+ debug_assert!(selected_paths.len() <= payment_params.max_path_count.into());
if let Some(features) = &payment_params.features {
for path in selected_paths.iter_mut() {
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
impl<'a> Writeable for HopScorer {
}
{
- // Attempt to route while setting max_mpp_path_count to 0 results in a failure.
- let zero_payment_params = payment_params.clone().with_max_mpp_path_count(0);
+ // Attempt to route while setting max_path_count to 0 results in a failure.
+ let zero_payment_params = payment_params.clone().with_max_path_count(0);
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
&our_id, &zero_payment_params, &network_graph.read_only(), None, 100, 42,
Arc::clone(&logger), &scorer, &random_seed_bytes) {
- assert_eq!(err, "Can't find an MPP route with no paths allowed.");
+ assert_eq!(err, "Can't find a route with no paths allowed.");
} else { panic!(); }
}
{
- // Attempt to route while setting max_mpp_path_count to 3 results in a failure.
+ // Attempt to route while setting max_path_count to 3 results in a failure.
// This is the case because the minimal_value_contribution_msat would require each path
// to account for 1/3 of the total value, which is violated by 2 out of 3 paths.
- let fail_payment_params = payment_params.clone().with_max_mpp_path_count(3);
+ let fail_payment_params = payment_params.clone().with_max_path_count(3);
if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(
&our_id, &fail_payment_params, &network_graph.read_only(), None, 250_000, 42,
Arc::clone(&logger), &scorer, &random_seed_bytes) {
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
struct BadNodeScorer {
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
#[test]
/// Handles updating channel penalties after successfully routing along a path.
fn payment_path_successful(&mut self, path: &[&RouteHop]);
+
+ /// Handles updating channel penalties after a probe over the given path failed.
+ fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64);
+
+ /// Handles updating channel penalties after a probe over the given path succeeded.
+ fn probe_successful(&mut self, path: &[&RouteHop]);
}
impl<S: Score, T: DerefMut<Target=S> $(+ $supertrait)*> Score for T {
fn payment_path_successful(&mut self, path: &[&RouteHop]) {
self.deref_mut().payment_path_successful(path)
}
+
+ fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
+ self.deref_mut().probe_failed(path, short_channel_id)
+ }
+
+ fn probe_successful(&mut self, path: &[&RouteHop]) {
+ self.deref_mut().probe_successful(path)
+ }
}
} }
fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
fn payment_path_successful(&mut self, _path: &[&RouteHop]) {}
+
+ fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) {}
+
+ fn probe_successful(&mut self, _path: &[&RouteHop]) {}
}
impl Writeable for FixedPenaltyScorer {
}
}
}
+
+ fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
+ self.payment_path_failed(path, short_channel_id)
+ }
+
+ fn probe_successful(&mut self, path: &[&RouteHop]) {
+ self.payment_path_failed(path, u64::max_value())
+ }
}
mod approx {
let mut forwarding_fee_base_msat = 0;
read_tlv_fields!(reader, {
(0, forwarding_fee_proportional_millionths, required),
- (1, max_dust_htlc_exposure_msat, (default_value, 5_000_000)),
+ (1, max_dust_htlc_exposure_msat, (default_value, 5_000_000u64)),
(2, cltv_expiry_delta, required),
- (3, force_close_avoidance_max_fee_satoshis, (default_value, 1000)),
+ (3, force_close_avoidance_max_fee_satoshis, (default_value, 1000u64)),
(4, announced_channel, required),
(6, commit_upfront_shutdown_pubkey, required),
(8, forwarding_fee_base_msat, required),
#[cfg(test)]
error_data: Option<Vec<u8>>,
},
+ /// Indicates that a probe payment we sent returned successful, i.e., only failed at the destination.
+ ProbeSuccessful {
+ /// The id returned by [`ChannelManager::send_probe`].
+ ///
+ /// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe
+ payment_id: PaymentId,
+ /// The hash generated by [`ChannelManager::send_probe`].
+ ///
+ /// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe
+ payment_hash: PaymentHash,
+ /// The payment path that was successful.
+ path: Vec<RouteHop>,
+ },
+ /// Indicates that a probe payment we sent failed at an intermediary node on the path.
+ ProbeFailed {
+ /// The id returned by [`ChannelManager::send_probe`].
+ ///
+ /// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe
+ payment_id: PaymentId,
+ /// The hash generated by [`ChannelManager::send_probe`].
+ ///
+ /// [`ChannelManager::send_probe`]: crate::ln::channelmanager::ChannelManager::send_probe
+ payment_hash: PaymentHash,
+ /// The payment path that failed.
+ path: Vec<RouteHop>,
+ /// The channel responsible for the failed probe.
+ ///
+ /// Note that for route hints or for the first hop in a path this may be an SCID alias and
+ /// may not refer to a channel in the public network graph. These aliases may also collide
+ /// with channels in the public network graph.
+ short_channel_id: Option<u64>,
+ },
/// Used to indicate that [`ChannelManager::process_pending_htlc_forwards`] should be called at
/// a time in the future.
///
(4, amount_msat, required),
});
},
+ &Event::ProbeSuccessful { ref payment_id, ref payment_hash, ref path } => {
+ 21u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, payment_id, required),
+ (2, payment_hash, required),
+ (4, path, vec_type)
+ })
+ },
+ &Event::ProbeFailed { ref payment_id, ref payment_hash, ref path, ref short_channel_id } => {
+ 23u8.write(writer)?;
+ write_tlv_fields!(writer, {
+ (0, payment_id, required),
+ (2, payment_hash, required),
+ (4, path, vec_type),
+ (6, short_channel_id, option),
+ })
+ },
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
};
f()
},
+ 21u8 => {
+ let f = || {
+ let mut payment_id = PaymentId([0; 32]);
+ let mut payment_hash = PaymentHash([0; 32]);
+ let mut path: Option<Vec<RouteHop>> = Some(vec![]);
+ read_tlv_fields!(reader, {
+ (0, payment_id, required),
+ (2, payment_hash, required),
+ (4, path, vec_type),
+ });
+ Ok(Some(Event::ProbeSuccessful {
+ payment_id,
+ payment_hash,
+ path: path.unwrap(),
+ }))
+ };
+ f()
+ },
+ 23u8 => {
+ let f = || {
+ let mut payment_id = PaymentId([0; 32]);
+ let mut payment_hash = PaymentHash([0; 32]);
+ let mut path: Option<Vec<RouteHop>> = Some(vec![]);
+ let mut short_channel_id = None;
+ read_tlv_fields!(reader, {
+ (0, payment_id, required),
+ (2, payment_hash, required),
+ (4, path, vec_type),
+ (6, short_channel_id, option),
+ });
+ Ok(Some(Event::ProbeFailed{
+ payment_id,
+ payment_hash,
+ path: path.unwrap(),
+ short_channel_id,
+ }))
+ };
+ f()
+ },
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
use core::hash::Hash;
use sync::Mutex;
use core::cmp;
+use core::convert::TryFrom;
+use core::ops::Deref;
use bitcoin::secp256k1::{PublicKey, SecretKey};
use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE};
Ok(Self(Some(Readable::read(reader)?)))
}
}
+/// When handling default_values, we want to map the default-value T directly
+/// to a OptionDeserWrapper<T> in a way that works for `field: T = t;` as
+/// well. Thus, we assume `Into<T> for T` does nothing and use that.
+impl<T: Readable> From<T> for OptionDeserWrapper<T> {
+ fn from(t: T) -> OptionDeserWrapper<T> { OptionDeserWrapper(Some(t)) }
+}
/// Wrapper to write each element of a Vec with no length prefix
pub(crate) struct VecWriteWrapper<'a, T: Writeable>(pub &'a Vec<T>);
}
}
+/// Represents a hostname for serialization purposes.
+/// Only the character set and length will be validated.
+/// The character set consists of ASCII alphanumeric characters, hyphens, and periods.
+/// Its length is guaranteed to be representable by a single byte.
+/// This serialization is used by BOLT 7 hostnames.
+#[derive(Clone, Debug, PartialEq)]
+pub struct Hostname(String);
+impl Hostname {
+ /// Returns the length of the hostname.
+ pub fn len(&self) -> u8 {
+ (&self.0).len() as u8
+ }
+}
+impl Deref for Hostname {
+ type Target = String;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+impl From<Hostname> for String {
+ fn from(hostname: Hostname) -> Self {
+ hostname.0
+ }
+}
+impl TryFrom<Vec<u8>> for Hostname {
+ type Error = ();
+
+ fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
+ if let Ok(s) = String::from_utf8(bytes) {
+ Hostname::try_from(s)
+ } else {
+ Err(())
+ }
+ }
+}
+impl TryFrom<String> for Hostname {
+ type Error = ();
+
+ fn try_from(s: String) -> Result<Self, Self::Error> {
+ if s.len() <= 255 && s.chars().all(|c|
+ c.is_ascii_alphanumeric() ||
+ c == '.' ||
+ c == '-'
+ ) {
+ Ok(Hostname(s))
+ } else {
+ Err(())
+ }
+ }
+}
+impl Writeable for Hostname {
+ #[inline]
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.len().write(w)?;
+ w.write_all(self.as_bytes())
+ }
+}
+impl Readable for Hostname {
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Hostname, DecodeError> {
+ let len: u8 = Readable::read(r)?;
+ let mut vec = Vec::with_capacity(len.into());
+ vec.resize(len.into(), 0);
+ r.read_exact(&mut vec)?;
+ Hostname::try_from(vec).map_err(|_| DecodeError::InvalidValue)
+ }
+}
+
impl Writeable for Duration {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
Ok(Duration::new(secs, nanos))
}
}
+
+#[cfg(test)]
+mod tests {
+ use core::convert::TryFrom;
+ use util::ser::{Readable, Hostname, Writeable};
+
+ #[test]
+ fn hostname_conversion() {
+ assert_eq!(Hostname::try_from(String::from("a-test.com")).unwrap().as_str(), "a-test.com");
+
+ assert!(Hostname::try_from(String::from("\"")).is_err());
+ assert!(Hostname::try_from(String::from("$")).is_err());
+ assert!(Hostname::try_from(String::from("⚡")).is_err());
+ let mut large_vec = Vec::with_capacity(256);
+ large_vec.resize(256, b'A');
+ assert!(Hostname::try_from(String::from_utf8(large_vec).unwrap()).is_err());
+ }
+
+ #[test]
+ fn hostname_serialization() {
+ let hostname = Hostname::try_from(String::from("test")).unwrap();
+ let mut buf: Vec<u8> = Vec::new();
+ hostname.write(&mut buf).unwrap();
+ assert_eq!(Hostname::read(&mut buf.as_slice()).unwrap().as_str(), "test");
+ }
+}
#[allow(unused_comparisons)] // Note that $type may be 0 making the second comparison always true
let invalid_order = ($last_seen_type.is_none() || $last_seen_type.unwrap() < $type) && $typ.0 > $type;
if invalid_order {
- $field = $default;
+ $field = $default.into();
}
}};
($last_seen_type: expr, $typ: expr, $type: expr, $field: ident, required) => {{
#[allow(unused_comparisons)] // Note that $type may be 0 making the second comparison always true
let missing_req_type = $last_seen_type.is_none() || $last_seen_type.unwrap() < $type;
if missing_req_type {
- $field = $default;
+ $field = $default.into();
}
}};
($last_seen_type: expr, $type: expr, $field: ident, required) => {{
macro_rules! init_tlv_based_struct_field {
($field: ident, (default_value, $default: expr)) => {
- $field
+ $field.0.unwrap()
};
($field: ident, option) => {
$field
macro_rules! init_tlv_field_var {
($field: ident, (default_value, $default: expr)) => {
- let mut $field = $default;
+ let mut $field = ::util::ser::OptionDeserWrapper(None);
};
($field: ident, required) => {
let mut $field = ::util::ser::OptionDeserWrapper(None);