**/*.rs.bk
Cargo.lock
.idea
-
+lightning/target
echo $HEX | xxd -r -p > ./test_cases/$TARGET/any_filename_works
export RUST_BACKTRACE=1
+export RUSTFLAGS="--cfg=fuzzing"
cargo test
```
use lightning::ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use lightning::ln::msgs::{CommitmentUpdate, ChannelMessageHandler, DecodeError, UpdateAddHTLC, Init};
use lightning::ln::script::ShutdownScript;
-use lightning::util::enforcing_trait_impls::{EnforcingSigner, INITIAL_REVOKED_COMMITMENT_NUMBER};
+use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::errors::APIError;
use lightning::util::events;
use lightning::util::logger::Logger;
self.0.extend_from_slice(buf);
Ok(())
}
- fn size_hint(&mut self, size: usize) {
- self.0.reserve_exact(size);
- }
}
struct TestChainMonitor {
struct KeyProvider {
node_id: u8,
rand_bytes_id: atomic::AtomicU32,
- revoked_commitments: Mutex<HashMap<[u8;32], Arc<Mutex<u64>>>>,
+ enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
}
impl KeysInterface for KeyProvider {
type Signer = EnforcingSigner;
channel_value_satoshis,
[0; 32],
);
- let revoked_commitment = self.make_revoked_commitment_cell(keys.commitment_seed);
+ let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed);
EnforcingSigner::new_with_revoked(keys, revoked_commitment, false)
}
let mut reader = std::io::Cursor::new(buffer);
let inner: InMemorySigner = Readable::read(&mut reader)?;
- let revoked_commitment = self.make_revoked_commitment_cell(inner.commitment_seed);
-
- let last_commitment_number = Readable::read(&mut reader)?;
+ let state = self.make_enforcement_state_cell(inner.commitment_seed);
Ok(EnforcingSigner {
inner,
- last_commitment_number: Arc::new(Mutex::new(last_commitment_number)),
- revoked_commitment,
+ state,
disable_revocation_policy_check: false,
})
}
}
impl KeyProvider {
- fn make_revoked_commitment_cell(&self, commitment_seed: [u8; 32]) -> Arc<Mutex<u64>> {
- let mut revoked_commitments = self.revoked_commitments.lock().unwrap();
+ fn make_enforcement_state_cell(&self, commitment_seed: [u8; 32]) -> Arc<Mutex<EnforcementState>> {
+ let mut revoked_commitments = self.enforcement_states.lock().unwrap();
if !revoked_commitments.contains_key(&commitment_seed) {
- revoked_commitments.insert(commitment_seed, Arc::new(Mutex::new(INITIAL_REVOKED_COMMITMENT_NUMBER)));
+ revoked_commitments.insert(commitment_seed, Arc::new(Mutex::new(EnforcementState::new())));
}
let cell = revoked_commitments.get(&commitment_seed).unwrap();
Arc::clone(cell)
macro_rules! make_node {
($node_id: expr, $fee_estimator: expr) => { {
let logger: Arc<dyn Logger> = Arc::new(test_logger::TestLogger::new($node_id.to_string(), out.clone()));
- let keys_manager = Arc::new(KeyProvider { node_id: $node_id, rand_bytes_id: atomic::AtomicU32::new(0), revoked_commitments: Mutex::new(HashMap::new()) });
+ let keys_manager = Arc::new(KeyProvider { node_id: $node_id, rand_bytes_id: atomic::AtomicU32::new(0), enforcement_states: Mutex::new(HashMap::new()) });
let monitor = Arc::new(TestChainMonitor::new(broadcast.clone(), logger.clone(), $fee_estimator.clone(), Arc::new(TestPersister{}), Arc::clone(&keys_manager)));
let mut config = UserConfig::default();
},
events::MessageSendEvent::SendFundingLocked { .. } => continue,
events::MessageSendEvent::SendAnnouncementSignatures { .. } => continue,
- events::MessageSendEvent::PaymentFailureNetworkUpdate { .. } => continue,
events::MessageSendEvent::SendChannelUpdate { ref node_id, ref msg } => {
assert_eq!(msg.contents.flags & 2, 0); // The disable bit must never be set!
if Some(*node_id) == expect_drop_id { panic!("peer_disconnected should drop msgs bound for the disconnected peer"); }
events::MessageSendEvent::SendAnnouncementSignatures { .. } => {
// Can be generated as a reestablish response
},
- events::MessageSendEvent::PaymentFailureNetworkUpdate { .. } => {
- // Can be generated due to a payment forward being rejected due to a
- // channel having previously failed a monitor update
- },
events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
// When we reconnect we will resend a channel_update to make sure our
// counterparty has the latest parameters for receiving payments
events::MessageSendEvent::SendChannelReestablish { .. } => {},
events::MessageSendEvent::SendFundingLocked { .. } => {},
events::MessageSendEvent::SendAnnouncementSignatures { .. } => {},
- events::MessageSendEvent::PaymentFailureNetworkUpdate { .. } => {},
events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
assert_eq!(msg.contents.flags & 2, 0); // The disable bit must never be set!
},
events::MessageSendEvent::SendChannelReestablish { .. } => {},
events::MessageSendEvent::SendFundingLocked { .. } => {},
events::MessageSendEvent::SendAnnouncementSignatures { .. } => {},
- events::MessageSendEvent::PaymentFailureNetworkUpdate { .. } => {},
events::MessageSendEvent::SendChannelUpdate { ref msg, .. } => {
assert_eq!(msg.contents.flags & 2, 0); // The disable bit must never be set!
},
self.0.extend_from_slice(buf);
Ok(())
}
- fn size_hint(&mut self, size: usize) {
- self.0.reserve_exact(size);
- }
}
#[inline]
use lightning::ln::msgs::DecodeError;
use lightning::ln::script::ShutdownScript;
use lightning::routing::router::get_route;
-use lightning::routing::network_graph::NetGraphMsgHandler;
+use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
use lightning::util::config::UserConfig;
use lightning::util::errors::APIError;
use lightning::util::events::Event;
-use lightning::util::enforcing_trait_impls::EnforcingSigner;
+use lightning::util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use lightning::util::logger::Logger;
use lightning::util::ser::Readable;
(ctr >> 8*7) as u8, (ctr >> 8*6) as u8, (ctr >> 8*5) as u8, (ctr >> 8*4) as u8, (ctr >> 8*3) as u8, (ctr >> 8*2) as u8, (ctr >> 8*1) as u8, 14, (ctr >> 8*0) as u8]
}
- fn read_chan_signer(&self, data: &[u8]) -> Result<EnforcingSigner, DecodeError> {
- EnforcingSigner::read(&mut std::io::Cursor::new(data))
+ fn read_chan_signer(&self, mut data: &[u8]) -> Result<EnforcingSigner, DecodeError> {
+ let inner: InMemorySigner = Readable::read(&mut data)?;
+ let state = Arc::new(Mutex::new(EnforcementState::new()));
+
+ Ok(EnforcingSigner::new_with_revoked(
+ inner,
+ state,
+ false
+ ))
}
fn sign_invoice(&self, _invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> {
};
let channelmanager = Arc::new(ChannelManager::new(fee_est.clone(), monitor.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config, params));
let our_id = PublicKey::from_secret_key(&Secp256k1::signing_only(), &keys_manager.get_node_secret());
- let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(genesis_block(network).block_hash(), None, Arc::clone(&logger)));
+ let network_graph = NetworkGraph::new(genesis_block(network).block_hash());
+ let net_graph_msg_handler = Arc::new(NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger)));
let peers = RefCell::new([false; 256]);
let mut loss_detector = MoneyLossDetector::new(&peers, channelmanager.clone(), monitor.clone(), PeerManager::new(MessageHandler {
},
4 => {
let value = slice_to_be24(get_slice!(3)) as u64;
- let route = match get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger)) {
+ let route = match get_route(&our_id, &net_graph_msg_handler.network_graph, &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger)) {
Ok(route) => route,
Err(_) => return,
};
},
15 => {
let value = slice_to_be24(get_slice!(3)) as u64;
- let mut route = match get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger)) {
+ let mut route = match get_route(&our_id, &net_graph_msg_handler.network_graph, &get_pubkey!(), None, None, &Vec::new(), value, 42, Arc::clone(&logger)) {
Ok(route) => route,
Err(_) => return,
};
# Note when adding new targets here you should add a similar line in src/bin/gen_target.sh
-GEN_TEST AcceptChannel test_msg ""
-GEN_TEST AnnouncementSignatures test_msg ""
+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 FundingLocked 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 CommitmentSigned test_msg ""
GEN_TEST DecodedOnionErrorPacket test_msg ""
-GEN_TEST FundingCreated test_msg ""
-GEN_TEST FundingLocked test_msg ""
-GEN_TEST FundingSigned test_msg ""
-GEN_TEST OpenChannel test_msg ""
-GEN_TEST RevokeAndACK test_msg ""
-GEN_TEST Shutdown test_msg ""
-GEN_TEST UpdateFailHTLC test_msg ""
-GEN_TEST UpdateFailMalformedHTLC test_msg ""
-GEN_TEST UpdateFee test_msg ""
-GEN_TEST UpdateFulfillHTLC test_msg ""
GEN_TEST ChannelAnnouncement test_msg_exact ""
GEN_TEST NodeAnnouncement test_msg_exact ""
GEN_TEST QueryShortChannelIds test_msg ""
-GEN_TEST ReplyShortChannelIdsEnd test_msg ""
-GEN_TEST QueryChannelRange test_msg ""
GEN_TEST ReplyChannelRange test_msg ""
-GEN_TEST GossipTimestampFilter test_msg ""
-GEN_TEST UpdateAddHTLC test_msg_hole ", 85, 33"
GEN_TEST ErrorMessage test_msg_hole ", 32, 2"
GEN_TEST ChannelUpdate test_msg_hole ", 108, 1"
-
-GEN_TEST ClosingSigned test_msg_simple ""
-GEN_TEST Init test_msg_simple ""
-GEN_TEST OnionHopData test_msg_simple ""
-GEN_TEST Ping test_msg_simple ""
-GEN_TEST Pong test_msg_simple ""
mod utils;
pub mod msg_accept_channel;
pub mod msg_announcement_signatures;
-pub mod msg_channel_reestablish;
+pub mod msg_closing_signed;
pub mod msg_commitment_signed;
-pub mod msg_decoded_onion_error_packet;
pub mod msg_funding_created;
pub mod msg_funding_locked;
pub mod msg_funding_signed;
+pub mod msg_gossip_timestamp_filter;
+pub mod msg_init;
+pub mod msg_onion_hop_data;
pub mod msg_open_channel;
+pub mod msg_ping;
+pub mod msg_pong;
+pub mod msg_query_channel_range;
+pub mod msg_reply_short_channel_ids_end;
pub mod msg_revoke_and_ack;
pub mod msg_shutdown;
+pub mod msg_update_add_htlc;
pub mod msg_update_fail_htlc;
pub mod msg_update_fail_malformed_htlc;
pub mod msg_update_fee;
pub mod msg_update_fulfill_htlc;
+pub mod msg_channel_reestablish;
+pub mod msg_decoded_onion_error_packet;
pub mod msg_channel_announcement;
pub mod msg_node_announcement;
pub mod msg_query_short_channel_ids;
-pub mod msg_reply_short_channel_ids_end;
-pub mod msg_query_channel_range;
pub mod msg_reply_channel_range;
-pub mod msg_gossip_timestamp_filter;
-pub mod msg_update_add_htlc;
pub mod msg_error_message;
pub mod msg_channel_update;
-pub mod msg_closing_signed;
-pub mod msg_init;
-pub mod msg_onion_hop_data;
-pub mod msg_ping;
-pub mod msg_pong;
#[inline]
pub fn msg_accept_channel_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::AcceptChannel, data);
+ test_msg_simple!(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!(msgs::AcceptChannel, data);
+ test_msg_simple!(msgs::AcceptChannel, data);
}
#[inline]
pub fn msg_announcement_signatures_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::AnnouncementSignatures, data);
+ test_msg_simple!(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!(msgs::AnnouncementSignatures, data);
+ test_msg_simple!(msgs::AnnouncementSignatures, data);
}
#[inline]
pub fn msg_commitment_signed_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::CommitmentSigned, data);
+ test_msg_simple!(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!(msgs::CommitmentSigned, data);
+ test_msg_simple!(msgs::CommitmentSigned, data);
}
#[inline]
pub fn msg_funding_created_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::FundingCreated, data);
+ test_msg_simple!(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!(msgs::FundingCreated, data);
+ test_msg_simple!(msgs::FundingCreated, data);
}
#[inline]
pub fn msg_funding_locked_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::FundingLocked, data);
+ test_msg_simple!(msgs::FundingLocked, data);
}
#[no_mangle]
pub extern "C" fn msg_funding_locked_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
- test_msg!(msgs::FundingLocked, data);
+ test_msg_simple!(msgs::FundingLocked, data);
}
#[inline]
pub fn msg_funding_signed_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::FundingSigned, data);
+ test_msg_simple!(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!(msgs::FundingSigned, data);
+ test_msg_simple!(msgs::FundingSigned, data);
}
#[inline]
pub fn msg_gossip_timestamp_filter_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::GossipTimestampFilter, data);
+ test_msg_simple!(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!(msgs::GossipTimestampFilter, data);
+ test_msg_simple!(msgs::GossipTimestampFilter, data);
}
#[inline]
pub fn msg_open_channel_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::OpenChannel, data);
+ test_msg_simple!(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!(msgs::OpenChannel, data);
+ test_msg_simple!(msgs::OpenChannel, data);
}
#[inline]
pub fn msg_query_channel_range_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::QueryChannelRange, data);
+ test_msg_simple!(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!(msgs::QueryChannelRange, data);
+ test_msg_simple!(msgs::QueryChannelRange, data);
}
#[inline]
pub fn msg_reply_short_channel_ids_end_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::ReplyShortChannelIdsEnd, data);
+ test_msg_simple!(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!(msgs::ReplyShortChannelIdsEnd, data);
+ test_msg_simple!(msgs::ReplyShortChannelIdsEnd, data);
}
#[inline]
pub fn msg_revoke_and_ack_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::RevokeAndACK, data);
+ test_msg_simple!(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!(msgs::RevokeAndACK, data);
+ test_msg_simple!(msgs::RevokeAndACK, data);
}
#[inline]
pub fn msg_shutdown_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::Shutdown, data);
+ test_msg_simple!(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!(msgs::Shutdown, data);
+ test_msg_simple!(msgs::Shutdown, data);
}
#[inline]
pub fn msg_update_add_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg_hole!(msgs::UpdateAddHTLC, data, 85, 33);
+ test_msg_simple!(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_hole!(msgs::UpdateAddHTLC, data, 85, 33);
+ test_msg_simple!(msgs::UpdateAddHTLC, data);
}
#[inline]
pub fn msg_update_fail_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::UpdateFailHTLC, data);
+ test_msg_simple!(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!(msgs::UpdateFailHTLC, data);
+ test_msg_simple!(msgs::UpdateFailHTLC, data);
}
#[inline]
pub fn msg_update_fail_malformed_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::UpdateFailMalformedHTLC, data);
+ test_msg_simple!(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!(msgs::UpdateFailMalformedHTLC, data);
+ test_msg_simple!(msgs::UpdateFailMalformedHTLC, data);
}
#[inline]
pub fn msg_update_fee_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::UpdateFee, data);
+ test_msg_simple!(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!(msgs::UpdateFee, data);
+ test_msg_simple!(msgs::UpdateFee, data);
}
#[inline]
pub fn msg_update_fulfill_htlc_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
- test_msg!(msgs::UpdateFulfillHTLC, data);
+ test_msg_simple!(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!(msgs::UpdateFulfillHTLC, data);
+ test_msg_simple!(msgs::UpdateFulfillHTLC, data);
}
pub struct VecWriter(pub Vec<u8>);
impl Writer for VecWriter {
fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
- assert!(self.0.capacity() >= self.0.len() + buf.len());
self.0.extend_from_slice(buf);
Ok(())
}
- fn size_hint(&mut self, size: usize) {
- self.0.reserve_exact(size);
- }
}
// We attempt to test the strictest behavior we can for a given message, however, some messages
msg.write(&mut w).unwrap();
assert_eq!(w.0.len(), p);
+ assert_eq!(msg.serialized_length(), p);
assert_eq!(&r.into_inner()[..p], &w.0[..p]);
}
}
if let Ok(msg) = <$MsgType as Readable>::read(&mut r) {
let mut w = VecWriter(Vec::new());
msg.write(&mut w).unwrap();
+ assert_eq!(msg.serialized_length(), w.0.len());
let msg = <$MsgType as Readable>::read(&mut ::std::io::Cursor::new(&w.0)).unwrap();
let mut w_two = VecWriter(Vec::new());
let mut w = VecWriter(Vec::new());
msg.write(&mut w).unwrap();
assert_eq!(&r.into_inner()[..], &w.0[..]);
+ assert_eq!(msg.serialized_length(), w.0.len());
}
}
}
let mut w = VecWriter(Vec::new());
msg.write(&mut w).unwrap();
let p = w.0.len() as usize;
+ assert_eq!(msg.serialized_length(), p);
assert_eq!(w.0.len(), p);
assert_eq!(&r.get_ref()[..$hole], &w.0[..$hole]);
let logger: Arc<dyn Logger> = Arc::new(test_logger::TestLogger::new("".to_owned(), out));
let our_pubkey = get_pubkey!();
- let mut net_graph = NetworkGraph::new(genesis_block(Network::Bitcoin).header.block_hash());
+ let net_graph = NetworkGraph::new(genesis_block(Network::Bitcoin).header.block_hash());
let mut node_pks = HashSet::new();
let mut scid = 42;
use lightning::ln::msgs::{ChannelMessageHandler, RoutingMessageHandler};
use lightning::ln::peer_handler::{PeerManager, SocketDescriptor};
use lightning::ln::peer_handler::CustomMessageHandler;
-use lightning::util::events::{EventHandler, EventsProvider};
+use lightning::routing::network_graph::NetGraphMsgHandler;
+use lightning::util::events::{Event, EventHandler, EventsProvider};
use lightning::util::logger::Logger;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
use std::ops::Deref;
-/// BackgroundProcessor takes care of tasks that (1) need to happen periodically to keep
+/// `BackgroundProcessor` takes care of tasks that (1) need to happen periodically to keep
/// Rust-Lightning running properly, and (2) either can or should be run in the background. Its
/// responsibilities are:
-/// * Monitoring whether the ChannelManager needs to be re-persisted to disk, and if so,
+/// * Processing [`Event`]s with a user-provided [`EventHandler`].
+/// * Monitoring whether the [`ChannelManager`] needs to be re-persisted to disk, and if so,
/// writing it to disk/backups by invoking the callback given to it at startup.
-/// ChannelManager persistence should be done in the background.
-/// * Calling `ChannelManager::timer_tick_occurred()` and
-/// `PeerManager::timer_tick_occurred()` every minute (can be done in the
-/// background).
+/// [`ChannelManager`] persistence should be done in the background.
+/// * Calling [`ChannelManager::timer_tick_occurred`] and [`PeerManager::timer_tick_occurred`]
+/// at the appropriate intervals.
///
-/// Note that if ChannelManager persistence fails and the persisted manager becomes out-of-date,
-/// then there is a risk of channels force-closing on startup when the manager realizes it's
-/// outdated. However, as long as `ChannelMonitor` backups are sound, no funds besides those used
-/// for unilateral chain closure fees are at risk.
+/// It will also call [`PeerManager::process_events`] periodically though this shouldn't be relied
+/// upon as doing so may result in high latency.
+///
+/// # Note
+///
+/// If [`ChannelManager`] persistence fails and the persisted manager becomes out-of-date, then
+/// there is a risk of channels force-closing on startup when the manager realizes it's outdated.
+/// However, as long as [`ChannelMonitor`] backups are sound, no funds besides those used for
+/// unilateral chain closure fees are at risk.
+///
+/// [`ChannelMonitor`]: lightning::chain::channelmonitor::ChannelMonitor
+/// [`Event`]: lightning::util::events::Event
#[must_use = "BackgroundProcessor will immediately stop on drop. It should be stored until shutdown."]
pub struct BackgroundProcessor {
stop_thread: Arc<AtomicBool>,
}
}
+/// Decorates an [`EventHandler`] with common functionality provided by standard [`EventHandler`]s.
+struct DecoratingEventHandler<
+ E: EventHandler,
+ N: Deref<Target = NetGraphMsgHandler<A, L>>,
+ A: Deref,
+ L: Deref,
+>
+where A::Target: chain::Access, L::Target: Logger {
+ event_handler: E,
+ net_graph_msg_handler: Option<N>,
+}
+
+impl<
+ E: EventHandler,
+ N: Deref<Target = NetGraphMsgHandler<A, L>>,
+ A: Deref,
+ L: Deref,
+> EventHandler for DecoratingEventHandler<E, N, A, L>
+where A::Target: chain::Access, L::Target: Logger {
+ fn handle_event(&self, event: &Event) {
+ if let Some(event_handler) = &self.net_graph_msg_handler {
+ event_handler.handle_event(event);
+ }
+ self.event_handler.handle_event(event);
+ }
+}
+
impl BackgroundProcessor {
/// Start a background thread that takes care of responsibilities enumerated in the [top-level
/// documentation].
/// `persist_manager` returns an error. In case of an error, the error is retrieved by calling
/// either [`join`] or [`stop`].
///
- /// Typically, users should either implement [`ChannelManagerPersister`] to never return an
- /// error or call [`join`] and handle any error that may arise. For the latter case, the
- /// `BackgroundProcessor` must be restarted by calling `start` again after handling the error.
+ /// # Data Persistence
///
/// `persist_manager` is responsible for writing out the [`ChannelManager`] to disk, and/or
/// uploading to one or more backup services. See [`ChannelManager::write`] for writing out a
/// [`ChannelManager`]. See [`FilesystemPersister::persist_manager`] for Rust-Lightning's
/// provided implementation.
///
+ /// Typically, users should either implement [`ChannelManagerPersister`] to never return an
+ /// error or call [`join`] and handle any error that may arise. For the latter case,
+ /// `BackgroundProcessor` must be restarted by calling `start` again after handling the error.
+ ///
+ /// # Event Handling
+ ///
+ /// `event_handler` is responsible for handling events that users should be notified of (e.g.,
+ /// payment failed). [`BackgroundProcessor`] may decorate the given [`EventHandler`] with common
+ /// functionality implemented by other handlers.
+ /// * [`NetGraphMsgHandler`] if given will update the [`NetworkGraph`] based on payment failures.
+ ///
/// [top-level documentation]: Self
/// [`join`]: Self::join
/// [`stop`]: Self::stop
/// [`ChannelManager`]: lightning::ln::channelmanager::ChannelManager
/// [`ChannelManager::write`]: lightning::ln::channelmanager::ChannelManager#impl-Writeable
/// [`FilesystemPersister::persist_manager`]: lightning_persister::FilesystemPersister::persist_manager
+ /// [`NetworkGraph`]: lightning::routing::network_graph::NetworkGraph
pub fn start<
Signer: 'static + Sign,
+ CA: 'static + Deref + Send + Sync,
CF: 'static + Deref + Send + Sync,
CW: 'static + Deref + Send + Sync,
T: 'static + Deref + Send + Sync,
CMP: 'static + Send + ChannelManagerPersister<Signer, CW, T, K, F, L>,
M: 'static + Deref<Target = ChainMonitor<Signer, CF, T, F, L, P>> + Send + Sync,
CM: 'static + Deref<Target = ChannelManager<Signer, CW, T, K, F, L>> + Send + Sync,
+ NG: 'static + Deref<Target = NetGraphMsgHandler<CA, L>> + Send + Sync,
UMH: 'static + Deref + Send + Sync,
PM: 'static + Deref<Target = PeerManager<Descriptor, CMH, RMH, L, UMH>> + Send + Sync,
- >
- (persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM, peer_manager: PM, logger: L) -> Self
+ >(
+ persister: CMP, event_handler: EH, chain_monitor: M, channel_manager: CM,
+ net_graph_msg_handler: Option<NG>, peer_manager: PM, logger: L
+ ) -> Self
where
+ CA::Target: 'static + chain::Access,
CF::Target: 'static + chain::Filter,
CW::Target: 'static + chain::Watch<Signer>,
T::Target: 'static + BroadcasterInterface,
let stop_thread = Arc::new(AtomicBool::new(false));
let stop_thread_clone = stop_thread.clone();
let handle = thread::spawn(move || -> Result<(), std::io::Error> {
+ let event_handler = DecoratingEventHandler { event_handler, net_graph_msg_handler };
+
log_trace!(logger, "Calling ChannelManager's timer_tick_occurred on startup");
channel_manager.timer_tick_occurred();
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs::{ChannelMessageHandler, Init};
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
+ use lightning::routing::network_graph::{NetworkGraph, NetGraphMsgHandler};
use lightning::util::config::UserConfig;
use lightning::util::events::{Event, MessageSendEventsProvider, MessageSendEvent};
use lightning::util::ser::Writeable;
struct Node {
node: Arc<SimpleArcChannelManager<ChainMonitor, test_utils::TestBroadcaster, test_utils::TestFeeEstimator, test_utils::TestLogger>>,
+ net_graph_msg_handler: Option<Arc<NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>>>,
peer_manager: Arc<PeerManager<TestDescriptor, Arc<test_utils::TestChannelMessageHandler>, Arc<test_utils::TestRoutingMessageHandler>, Arc<test_utils::TestLogger>, IgnoringMessageHandler>>,
chain_monitor: Arc<ChainMonitor>,
persister: Arc<FilesystemPersister>,
let persister = Arc::new(FilesystemPersister::new(format!("{}_persister_{}", persist_dir, i)));
let seed = [i as u8; 32];
let network = Network::Testnet;
- let now = Duration::from_secs(genesis_block(network).header.time as u64);
+ let genesis_block = genesis_block(network);
+ let now = Duration::from_secs(genesis_block.header.time as u64);
let keys_manager = Arc::new(KeysManager::new(&seed, now.as_secs(), now.subsec_nanos()));
let chain_monitor = Arc::new(chainmonitor::ChainMonitor::new(Some(chain_source.clone()), tx_broadcaster.clone(), logger.clone(), fee_estimator.clone(), persister.clone()));
let best_block = BestBlock::from_genesis(network);
let params = ChainParameters { network, best_block };
let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), logger.clone(), keys_manager.clone(), UserConfig::default(), params));
+ let network_graph = NetworkGraph::new(genesis_block.header.block_hash());
+ let net_graph_msg_handler = Some(Arc::new(NetGraphMsgHandler::new(network_graph, Some(chain_source.clone()), logger.clone())));
let msg_handler = MessageHandler { chan_handler: Arc::new(test_utils::TestChannelMessageHandler::new()), route_handler: Arc::new(test_utils::TestRoutingMessageHandler::new() )};
let peer_manager = Arc::new(PeerManager::new(msg_handler, keys_manager.get_node_secret(), &seed, logger.clone(), IgnoringMessageHandler{}));
- let node = Node { node: manager, peer_manager, chain_monitor, persister, tx_broadcaster, logger, best_block };
+ let node = Node { node: manager, net_graph_msg_handler, peer_manager, chain_monitor, persister, tx_broadcaster, logger, best_block };
nodes.push(node);
}
begin_open_channel!($node_a, $node_b, $channel_value);
let events = $node_a.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
- let (temporary_channel_id, tx) = handle_funding_generation_ready!(events[0], $channel_value);
+ let (temporary_channel_id, tx) = handle_funding_generation_ready!(&events[0], $channel_value);
end_open_channel!($node_a, $node_b, temporary_channel_id, tx);
tx
}}
macro_rules! handle_funding_generation_ready {
($event: expr, $channel_value: expr) => {{
match $event {
- Event::FundingGenerationReady { ref temporary_channel_id, ref channel_value_satoshis, ref output_script, user_channel_id } => {
- assert_eq!(*channel_value_satoshis, $channel_value);
+ &Event::FundingGenerationReady { temporary_channel_id, channel_value_satoshis, ref output_script, user_channel_id } => {
+ assert_eq!(channel_value_satoshis, $channel_value);
assert_eq!(user_channel_id, 42);
let tx = Transaction { version: 1 as i32, lock_time: 0, input: Vec::new(), output: vec![TxOut {
- value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+ value: channel_value_satoshis, script_pubkey: output_script.clone(),
}]};
- (*temporary_channel_id, tx)
+ (temporary_channel_id, tx)
},
_ => panic!("Unexpected event"),
}
// Initiate the background processors to watch each node.
let data_dir = nodes[0].persister.get_data_dir();
let persister = move |node: &ChannelManager<InMemorySigner, Arc<ChainMonitor>, Arc<test_utils::TestBroadcaster>, Arc<KeysManager>, Arc<test_utils::TestFeeEstimator>, Arc<test_utils::TestLogger>>| FilesystemPersister::persist_manager(data_dir.clone(), node);
- let event_handler = |_| {};
- let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
+ let event_handler = |_: &_| {};
+ let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
macro_rules! check_persisted_data {
($node: expr, $filepath: expr, $expected_bytes: expr) => {
let nodes = create_nodes(1, "test_timer_tick_called".to_string());
let data_dir = nodes[0].persister.get_data_dir();
let persister = move |node: &ChannelManager<InMemorySigner, Arc<ChainMonitor>, Arc<test_utils::TestBroadcaster>, Arc<KeysManager>, Arc<test_utils::TestFeeEstimator>, Arc<test_utils::TestLogger>>| FilesystemPersister::persist_manager(data_dir.clone(), node);
- let event_handler = |_| {};
- let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
+ let event_handler = |_: &_| {};
+ let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
loop {
let log_entries = nodes[0].logger.lines.lock().unwrap();
let desired_log = "Calling ChannelManager's timer_tick_occurred".to_string();
open_channel!(nodes[0], nodes[1], 100000);
let persister = |_: &_| Err(std::io::Error::new(std::io::ErrorKind::Other, "test"));
- let event_handler = |_| {};
- let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
+ let event_handler = |_: &_| {};
+ let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
match bg_processor.join() {
Ok(_) => panic!("Expected error persisting manager"),
Err(e) => {
// Set up a background event handler for FundingGenerationReady events.
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
- let event_handler = move |event| {
+ let event_handler = move |event: &Event| {
sender.send(handle_funding_generation_ready!(event, channel_value)).unwrap();
};
- let bg_processor = BackgroundProcessor::start(persister.clone(), event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
+ let bg_processor = BackgroundProcessor::start(persister.clone(), event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
// Open a channel and check that the FundingGenerationReady event was handled.
begin_open_channel!(nodes[0], nodes[1], channel_value);
// Set up a background event handler for SpendableOutputs events.
let (sender, receiver) = std::sync::mpsc::sync_channel(1);
- let event_handler = move |event| sender.send(event).unwrap();
- let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
+ let event_handler = move |event: &Event| sender.send(event.clone()).unwrap();
+ let bg_processor = BackgroundProcessor::start(persister, event_handler, nodes[0].chain_monitor.clone(), nodes[0].node.clone(), nodes[0].net_graph_msg_handler.clone(), nodes[0].peer_manager.clone(), nodes[0].logger.clone());
// Force close the channel and check that the SpendableOutputs event was handled.
nodes[0].node.force_close_channel(&nodes[0].node.list_channels()[0].channel_id).unwrap();
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
chunked_transfer = { version = "1.4", optional = true }
-futures = { version = "0.3" }
[dev-dependencies]
tokio = { version = "1.0", features = [ "macros", "rt" ] }
bitcoin_hashes = "0.10"
[dev-dependencies]
+hex = "0.3"
lightning = { version = "0.0.100", path = "../lightning", features = ["_test_utils"] }
} else if ['m', 'u', 'n', 'p'].contains(&read_symbol) {
Ok(States::ParseAmountSiPrefix)
} else {
- Err(super::ParseError::MalformedHRP)
+ Err(super::ParseError::UnknownSiPrefix)
}
},
States::ParseAmountSiPrefix => Err(super::ParseError::MalformedHRP),
/// ```
/// use lightning_invoice::Invoice;
///
-/// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
-/// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
-/// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
-/// ky03ylcqca784w";
+///
+/// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
+/// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
+/// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
+/// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
+/// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
+/// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
+/// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
+/// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
+/// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
+/// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
+/// j5r6drg6k6zcqj0fcwg";
///
/// assert!(invoice.parse::<Invoice>().is_ok());
/// ```
/// ```
/// use lightning_invoice::*;
///
-/// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
-/// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
-/// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
-/// ky03ylcqca784w";
+/// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
+/// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
+/// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
+/// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
+/// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
+/// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
+/// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
+/// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
+/// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
+/// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
+/// j5r6drg6k6zcqj0fcwg";
///
/// let parsed_1 = invoice.parse::<Invoice>();
///
Ok(field) => {
parts.push(RawTaggedField::KnownSemantics(field))
},
- Err(ParseError::Skip) => {
+ Err(ParseError::Skip)|Err(ParseError::Bech32Error(bech32::Error::InvalidLength)) => {
parts.push(RawTaggedField::UnknownSemantics(field.into()))
},
Err(e) => {return Err(e)}
///
/// ```
/// extern crate secp256k1;
+/// extern crate lightning;
/// extern crate lightning_invoice;
/// extern crate bitcoin_hashes;
///
/// use secp256k1::Secp256k1;
/// use secp256k1::key::SecretKey;
///
+/// use lightning::ln::PaymentSecret;
+///
/// use lightning_invoice::{Currency, InvoiceBuilder};
///
/// # fn main() {
/// ).unwrap();
///
/// let payment_hash = sha256::Hash::from_slice(&[0; 32][..]).unwrap();
+/// let payment_secret = PaymentSecret([42u8; 32]);
///
/// let invoice = InvoiceBuilder::new(Currency::Bitcoin)
/// .description("Coins pls!".into())
/// .payment_hash(payment_hash)
+/// .payment_secret(payment_secret)
/// .current_timestamp()
/// .min_final_cltv_expiry(144)
/// .build_signed(|hash| {
}
/// Enum representing the crypto currencies (or networks) supported by this library
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum Currency {
/// Bitcoin mainnet
Bitcoin,
/// Tagged field which may have an unknown tag
///
/// (C-not exported) as we don't currently support TaggedField
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum RawTaggedField {
/// Parsed tagged field with known tag
KnownSemantics(TaggedField),
/// (C-not exported) As we don't yet support enum variants with the same name the struct contained
/// in the variant.
#[allow(missing_docs)]
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum TaggedField {
PaymentHash(Sha256),
Description(Description),
}
/// SHA-256 hash
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct Sha256(pub sha256::Hash);
/// Description string
///
/// # Invariants
/// The description can be at most 639 __bytes__ long
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct Description(String);
/// Payee public key
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct PayeePubKey(pub PublicKey);
/// Positive duration that defines when (relatively to the timestamp) in the future the invoice
/// The number of seconds this expiry time represents has to be in the range
/// `0...(SYSTEM_TIME_MAX_UNIX_TIMESTAMP - MAX_EXPIRY_TIME)` to avoid overflows when adding it to a
/// timestamp
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct ExpiryTime(Duration);
/// `min_final_cltv_expiry` to use for the last HTLC in the route
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct MinFinalCltvExpiry(pub u64);
// TODO: better types instead onf byte arrays
/// Fallback address in case no LN payment is possible
#[allow(missing_docs)]
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum Fallback {
SegWitProgram {
version: u5,
}
/// Recoverable signature
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InvoiceSignature(pub RecoverableSignature);
/// Private routing information
/// # Invariants
/// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
///
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct PrivateRoute(RouteHint);
/// Tag constants as specified in BOLT11
}
}
- /// Sets the amount in pico BTC. The optimal SI prefix is choosen automatically.
- pub fn amount_pico_btc(mut self, amount: u64) -> Self {
+ /// Sets the amount in millisatoshis. The optimal SI prefix is chosen automatically.
+ pub fn amount_milli_satoshis(mut self, amount_msat: u64) -> Self {
+ let amount = amount_msat * 10; // Invoices are denominated in "pico BTC"
let biggest_possible_si_prefix = SiPrefix::values_desc()
.iter()
.find(|prefix| amount % prefix.multiplier() == 0)
}
}
-impl<S: tb::Bool> InvoiceBuilder<tb::True, tb::True, tb::True, tb::True, S> {
+impl InvoiceBuilder<tb::True, tb::True, tb::True, tb::True, tb::True> {
/// Builds and signs an invoice using the supplied `sign_function`. This function MAY NOT fail
/// and MUST produce a recoverable signature valid for the given hash and if applicable also for
/// the included payee public key.
invoice.check_field_counts().expect("should be ensured by type signature of builder");
invoice.check_feature_bits().expect("should be ensured by type signature of builder");
+ invoice.check_amount().expect("should be ensured by type signature of builder");
Ok(invoice)
}
return Err(SemanticError::MultipleDescriptions);
}
+ self.check_payment_secret()?;
+
Ok(())
}
- /// Check that feature bits are set as required
- fn check_feature_bits(&self) -> Result<(), SemanticError> {
- // "If the payment_secret feature is set, MUST include exactly one s field."
+ /// Checks that there is exactly one payment secret field
+ fn check_payment_secret(&self) -> Result<(), SemanticError> {
+ // "A writer MUST include exactly one `s` field."
let payment_secret_count = self.tagged_fields().filter(|&tf| match *tf {
TaggedField::PaymentSecret(_) => true,
_ => false,
}).count();
- if payment_secret_count > 1 {
+ if payment_secret_count < 1 {
+ return Err(SemanticError::NoPaymentSecret);
+ } else if payment_secret_count > 1 {
return Err(SemanticError::MultiplePaymentSecrets);
}
+ Ok(())
+ }
+
+ /// Check that amount is a whole number of millisatoshis
+ fn check_amount(&self) -> Result<(), SemanticError> {
+ if let Some(amount_pico_btc) = self.amount_pico_btc() {
+ if amount_pico_btc % 10 != 0 {
+ return Err(SemanticError::ImpreciseAmount);
+ }
+ }
+ Ok(())
+ }
+
+ /// Check that feature bits are set as required
+ fn check_feature_bits(&self) -> Result<(), SemanticError> {
+ self.check_payment_secret()?;
+
// "A writer MUST set an s field if and only if the payment_secret feature is set."
- let has_payment_secret = payment_secret_count == 1;
+ // (this requirement has been since removed, and we now require the payment secret
+ // feature bit always).
let features = self.tagged_fields().find(|&tf| match *tf {
TaggedField::Features(_) => true,
_ => false,
});
match features {
- None if has_payment_secret => Err(SemanticError::InvalidFeatures),
- None => Ok(()),
+ None => Err(SemanticError::InvalidFeatures),
Some(TaggedField::Features(features)) => {
- if features.supports_payment_secret() && has_payment_secret {
- Ok(())
- } else if has_payment_secret {
+ if features.requires_unknown_bits() {
Err(SemanticError::InvalidFeatures)
- } else if features.supports_payment_secret() {
+ } else if !features.supports_payment_secret() {
Err(SemanticError::InvalidFeatures)
} else {
Ok(())
match self.signed_invoice.recover_payee_pub_key() {
Err(secp256k1::Error::InvalidRecoveryId) =>
return Err(SemanticError::InvalidRecoveryId),
- Err(_) => panic!("no other error may occur"),
+ Err(secp256k1::Error::InvalidSignature) =>
+ return Err(SemanticError::InvalidSignature),
+ Err(e) => panic!("no other error may occur, got {:?}", e),
Ok(_) => {},
}
/// ```
/// use lightning_invoice::*;
///
- /// let invoice = "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdp\
- /// l2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d7\
- /// 3gafnh3cax9rn449d9p5uxz9ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ec\
- /// ky03ylcqca784w";
+ /// let invoice = "lnbc100p1psj9jhxdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4q0d3p2sfluzdx45tqcs\
+ /// h2pu5qc7lgq0xs578ngs6s0s68ua4h7cvspp5q6rmq35js88zp5dvwrv9m459tnk2zunwj5jalqtyxqulh0l\
+ /// 5gflssp5nf55ny5gcrfl30xuhzj3nphgj27rstekmr9fw3ny5989s300gyus9qyysgqcqpcrzjqw2sxwe993\
+ /// h5pcm4dxzpvttgza8zhkqxpgffcrf5v25nwpr3cmfg7z54kuqq8rgqqqqqqqq2qqqqq9qq9qrzjqd0ylaqcl\
+ /// j9424x9m8h2vcukcgnm6s56xfgu3j78zyqzhgs4hlpzvznlugqq9vsqqqqqqqlgqqqqqeqq9qrzjqwldmj9d\
+ /// ha74df76zhx6l9we0vjdquygcdt3kssupehe64g6yyp5yz5rhuqqwccqqyqqqqlgqqqqjcqq9qrzjqf9e58a\
+ /// guqr0rcun0ajlvmzq3ek63cw2w282gv3z5uupmuwvgjtq2z55qsqqg6qqqyqqqrtnqqqzq3cqygrzjqvphms\
+ /// ywntrrhqjcraumvc4y6r8v4z5v593trte429v4hredj7ms5z52usqq9ngqqqqqqqlgqqqqqqgq9qrzjq2v0v\
+ /// p62g49p7569ev48cmulecsxe59lvaw3wlxm7r982zxa9zzj7z5l0cqqxusqqyqqqqlgqqqqqzsqygarl9fh3\
+ /// 8s0gyuxjjgux34w75dnc6xp2l35j7es3jd4ugt3lu0xzre26yg5m7ke54n2d5sym4xcmxtl8238xxvw5h5h5\
+ /// j5r6drg6k6zcqj0fcwg";
///
/// let signed = invoice.parse::<SignedRawInvoice>().unwrap();
///
invoice.check_field_counts()?;
invoice.check_feature_bits()?;
invoice.check_signature()?;
+ invoice.check_amount()?;
Ok(invoice)
}
}
/// Get the payment secret if one was included in the invoice
- pub fn payment_secret(&self) -> Option<&PaymentSecret> {
- self.signed_invoice.payment_secret()
+ pub fn payment_secret(&self) -> &PaymentSecret {
+ self.signed_invoice.payment_secret().expect("was checked by constructor")
}
/// Get the invoice features if they were included in the invoice
/// The invoice contains multiple descriptions and/or description hashes which isn't allowed
MultipleDescriptions,
+ /// The invoice is missing the mandatory payment secret, which all modern lightning nodes
+ /// should provide.
+ NoPaymentSecret,
+
/// The invoice contains multiple payment secrets
MultiplePaymentSecrets,
/// The invoice's signature is invalid
InvalidSignature,
+
+ /// The invoice's amount was not a whole number of millisatoshis
+ ImpreciseAmount,
}
impl Display for SemanticError {
SemanticError::MultiplePaymentHashes => f.write_str("The invoice has multiple payment hashes which isn't allowed"),
SemanticError::NoDescription => f.write_str("No description or description hash are part of the invoice"),
SemanticError::MultipleDescriptions => f.write_str("The invoice contains multiple descriptions and/or description hashes which isn't allowed"),
+ SemanticError::NoPaymentSecret => f.write_str("The invoice is missing the mandatory payment secret"),
SemanticError::MultiplePaymentSecrets => f.write_str("The invoice contains multiple payment secrets"),
SemanticError::InvalidFeatures => f.write_str("The invoice's features are invalid"),
SemanticError::InvalidRecoveryId => f.write_str("The recovery id doesn't fit the signature/pub key"),
SemanticError::InvalidSignature => f.write_str("The invoice's signature is invalid"),
+ SemanticError::ImpreciseAmount => f.write_str("The invoice's amount was not a whole number of millisatoshis"),
}
}
}
let invoice = invoice_template.clone();
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
}.unwrap();
- assert!(Invoice::from_signed(invoice).is_ok());
+ assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
// No payment secret or feature bits
let invoice = {
invoice.data.tagged_fields.push(Features(InvoiceFeatures::empty()).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
}.unwrap();
- assert!(Invoice::from_signed(invoice).is_ok());
+ assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
// Missing payment secret
let invoice = {
invoice.data.tagged_fields.push(Features(InvoiceFeatures::known()).into());
invoice.sign::<_, ()>(|hash| Ok(Secp256k1::new().sign_recoverable(hash, &private_key)))
}.unwrap();
- assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::InvalidFeatures));
+ assert_eq!(Invoice::from_signed(invoice), Err(SemanticError::NoPaymentSecret));
// Multiple payment secrets
let invoice = {
.current_timestamp();
let invoice = builder.clone()
- .amount_pico_btc(15000)
+ .amount_milli_satoshis(1500)
.build_raw()
.unwrap();
let invoice = builder.clone()
- .amount_pico_btc(1500)
+ .amount_milli_satoshis(150)
.build_raw()
.unwrap();
let sign_error_res = builder.clone()
.description("Test".into())
+ .payment_secret(PaymentSecret([0; 32]))
.try_build_signed(|_| {
Err("ImaginaryError")
});
]);
let builder = InvoiceBuilder::new(Currency::BitcoinTestnet)
- .amount_pico_btc(123)
+ .amount_milli_satoshis(123)
.timestamp(UNIX_EPOCH + Duration::from_secs(1234567))
.payee_pub_key(public_key.clone())
.expiry_time(Duration::from_secs(54321))
assert!(invoice.check_signature().is_ok());
assert_eq!(invoice.tagged_fields().count(), 10);
- assert_eq!(invoice.amount_pico_btc(), Some(123));
+ assert_eq!(invoice.amount_pico_btc(), Some(1230));
assert_eq!(invoice.currency(), Currency::BitcoinTestnet);
assert_eq!(
invoice.timestamp().duration_since(UNIX_EPOCH).unwrap().as_secs(),
InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
);
assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&[21;32][..]).unwrap());
- assert_eq!(invoice.payment_secret(), Some(&PaymentSecret([42; 32])));
+ assert_eq!(invoice.payment_secret(), &PaymentSecret([42; 32]));
assert_eq!(invoice.features(), Some(&InvoiceFeatures::known()));
let raw_invoice = builder.build_raw().unwrap();
let signed_invoice = InvoiceBuilder::new(Currency::Bitcoin)
.description("Test".into())
.payment_hash(sha256::Hash::from_slice(&[0;32][..]).unwrap())
+ .payment_secret(PaymentSecret([0; 32]))
.current_timestamp()
.build_raw()
.unwrap()
.basic_mpp()
.min_final_cltv_expiry(MIN_FINAL_CLTV_EXPIRY.into());
if let Some(amt) = amt_msat {
- invoice = invoice.amount_pico_btc(amt * 10);
+ invoice = invoice.amount_milli_satoshis(amt);
}
for hint in route_hints {
invoice = invoice.private_route(hint);
let amt_msat = invoice.amount_pico_btc().unwrap() / 10;
let first_hops = nodes[0].node.list_usable_channels();
let last_hops = invoice.route_hints();
- let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
+ let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let logger = test_utils::TestLogger::new();
let route = router::get_route(
&nodes[0].node.get_our_node_id(),
- &network_graph,
+ network_graph,
&invoice.recover_payee_pub_key(),
Some(invoice.features().unwrap().clone()),
Some(&first_hops.iter().collect::<Vec<_>>()),
let payment_event = {
let mut payment_hash = PaymentHash([0; 32]);
payment_hash.0.copy_from_slice(&invoice.payment_hash().as_ref()[0..32]);
- nodes[0].node.send_payment(&route, payment_hash, &Some(invoice.payment_secret().unwrap().clone())).unwrap();
+ nodes[0].node.send_payment(&route, payment_hash, &Some(invoice.payment_secret().clone())).unwrap();
let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap();
assert_eq!(added_monitors.len(), 1);
added_monitors.clear();
+extern crate bech32;
extern crate bitcoin_hashes;
extern crate lightning;
extern crate lightning_invoice;
extern crate secp256k1;
+extern crate hex;
use bitcoin_hashes::hex::FromHex;
-use bitcoin_hashes::sha256;
+use bitcoin_hashes::{sha256, Hash};
+use bech32::u5;
use lightning::ln::PaymentSecret;
+use lightning::routing::router::{RouteHint, RouteHintHop};
+use lightning::routing::network_graph::RoutingFees;
use lightning_invoice::*;
-use secp256k1::Secp256k1;
-use secp256k1::key::SecretKey;
+use secp256k1::PublicKey;
use secp256k1::recovery::{RecoverableSignature, RecoveryId};
+use std::collections::HashSet;
use std::time::{Duration, UNIX_EPOCH};
+use std::str::FromStr;
-// TODO: add more of the examples from BOLT11 and generate ones causing SemanticErrors
-
-fn get_test_tuples() -> Vec<(String, SignedRawInvoice, Option<SemanticError>)> {
+fn get_test_tuples() -> Vec<(String, SignedRawInvoice, bool, bool)> {
vec![
(
- "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmw\
- wd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq8rkx3yf5tcsyz3d73gafnh3cax9rn449d9p5uxz9\
- ezhhypd0elx87sjle52x86fux2ypatgddc6k63n7erqz25le42c4u4ecky03ylcqca784w".to_owned(),
+ "lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.unwrap()
.sign(|_| {
RecoverableSignature::from_compact(
- & [
- 0x38u8, 0xec, 0x68, 0x91, 0x34, 0x5e, 0x20, 0x41, 0x45, 0xbe, 0x8a,
- 0x3a, 0x99, 0xde, 0x38, 0xe9, 0x8a, 0x39, 0xd6, 0xa5, 0x69, 0x43,
- 0x4e, 0x18, 0x45, 0xc8, 0xaf, 0x72, 0x05, 0xaf, 0xcf, 0xcc, 0x7f,
- 0x42, 0x5f, 0xcd, 0x14, 0x63, 0xe9, 0x3c, 0x32, 0x88, 0x1e, 0xad,
- 0x0d, 0x6e, 0x35, 0x6d, 0x46, 0x7e, 0xc8, 0xc0, 0x25, 0x53, 0xf9,
- 0xaa, 0xb1, 0x5e, 0x57, 0x38, 0xb1, 0x1f, 0x12, 0x7f
- ],
- RecoveryId::from_i32(0).unwrap()
+ &hex::decode("8d3ce9e28357337f62da0162d9454df827f83cfe499aeb1c1db349d4d81127425e434ca29929406c23bba1ae8ac6ca32880b38d4bf6ff874024cac34ba9625f1").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
)
}).unwrap(),
- None
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
),
(
- "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3\
- k7enxv4jsxqzpuaztrnwngzn3kdzw5hydlzf03qdgm2hdq27cqv3agm2awhz5se903vruatfhq77w3ls4evs3ch\
- 9zw97j25emudupq63nyw24cg27h2rspfj9srp".to_owned(),
+ "lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpu9qrsgquk0rl77nj30yxdy8j9vdx85fkpmdla2087ne0xh8nhedh8w27kyke0lp53ut353s06fv3qfegext0eh0ymjpf39tuven09sam30g4vgpfna3rh".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
- .amount_pico_btc(2500000000)
+ .amount_milli_satoshis(250_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
.unwrap()
.sign(|_| {
RecoverableSignature::from_compact(
- & [
- 0xe8, 0x96, 0x39, 0xba, 0x68, 0x14, 0xe3, 0x66, 0x89, 0xd4, 0xb9, 0x1b,
- 0xf1, 0x25, 0xf1, 0x03, 0x51, 0xb5, 0x5d, 0xa0, 0x57, 0xb0, 0x06, 0x47,
- 0xa8, 0xda, 0xba, 0xeb, 0x8a, 0x90, 0xc9, 0x5f, 0x16, 0x0f, 0x9d, 0x5a,
- 0x6e, 0x0f, 0x79, 0xd1, 0xfc, 0x2b, 0x96, 0x42, 0x38, 0xb9, 0x44, 0xe2,
- 0xfa, 0x4a, 0xa6, 0x77, 0xc6, 0xf0, 0x20, 0xd4, 0x66, 0x47, 0x2a, 0xb8,
- 0x42, 0xbd, 0x75, 0x0e
- ],
+ &hex::decode("e59e3ffbd3945e4334879158d31e89b076dff54f3fa7979ae79df2db9dcaf5896cbfe1a478b8d2307e92c88139464cb7e6ef26e414c4abe33337961ddc5e8ab1").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc2500u1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpu9qrsgqhtjpauu9ur7fw2thcl4y9vfvh4m9wlfyz2gem29g5ghe2aak2pm3ps8fdhtceqsaagty2vph7utlgj48u0ged6a337aewvraedendscp573dxr".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(250_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .description("ナンセンス 1杯".to_owned())
+ .expiry_time(Duration::from_secs(60))
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("bae41ef385e0fc972977c7ea42b12cbd76577d2412919da8a8a22f9577b6507710c0e96dd78c821dea16453037f717f44aa7e3d196ebb18fbb97307dcb7336c3").unwrap(),
RecoveryId::from_i32(1).unwrap()
)
}).unwrap(),
- None
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
),
(
- "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qq\
- dhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqscc6gd6ql3jrc5yzme8v4ntcewwz5cnw92tz0pc8qcuufvq7k\
- hhr8wpald05e92xw006sq94mg8v2ndf4sefvf9sygkshp5zfem29trqq2yxxz7".to_owned(),
+ "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs9qrsgq7ea976txfraylvgzuxs8kgcw23ezlrszfnh8r6qtfpr6cxga50aj6txm9rxrydzd06dfeawfk6swupvz4erwnyutnjq7x39ymw6j38gp7ynn44".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
- .amount_pico_btc(20000000000)
+ .amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
+ .payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
- .description_hash(sha256::Hash::from_hex(
- "3925b6f67e2c340036ed12093dd44e0368df1b6ea26c53dbe4811f58fd5db8c1"
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("f67a5f696648fa4fb102e1a07b230e54722f8e024cee71e80b4847ac191da3fb2d2cdb28cc32344d7e9a9cf5c9b6a0ee0582ae46e9938b9c81e344a4dbb5289d").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lntb20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfpp3x9et2e20v6pu37c5d9vax37wxq72un989qrsgqdj545axuxtnfemtpwkc45hx9d2ft7x04mt8q7y6t0k2dge9e7h8kpy9p34ytyslj3yu569aalz2xdk8xkd7ltxqld94u8h2esmsmacgpghe9k8".to_owned(),
+ InvoiceBuilder::new(Currency::BitcoinTestnet)
+ .amount_milli_satoshis(2_000_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .fallback(Fallback::PubKeyHash([49, 114, 181, 101, 79, 102, 131, 200, 251, 20, 105, 89, 211, 71, 206, 48, 60, 174, 76, 167]))
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("6ca95a74dc32e69ced6175b15a5cc56a92bf19f5dace0f134b7d94d464b9f5cf6090a18d48b243f289394d17bdf89466d8e6b37df5981f696bc3dd5986e1bee1").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqhp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqsfpp3qjmp7lwpagxun9pygexvgpjdc4jdj85fr9yq20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqafqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq9qrsgqdfjcdk6w3ak5pca9hwfwfh63zrrz06wwfya0ydlzpgzxkn5xagsqz7x9j4jwe7yj7vaf2k9lqsdk45kts2fd0fkr28am0u4w95tt2nsq76cqw0".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_000_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
+ .fallback(Fallback::PubKeyHash([4, 182, 31, 125, 193, 234, 13, 201, 148, 36, 70, 76, 196, 6, 77, 197, 100, 217, 30, 137]))
+ .private_route(RouteHint(vec![RouteHintHop {
+ src_node_id: PublicKey::from_slice(&hex::decode(
+ "029e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"
+ ).unwrap()).unwrap(),
+ short_channel_id: (66051 << 40) | (263430 << 16) | 1800,
+ fees: RoutingFees { base_msat: 1, proportional_millionths: 20 },
+ cltv_expiry_delta: 3,
+ htlc_maximum_msat: None, htlc_minimum_msat: None,
+ }, RouteHintHop {
+ src_node_id: PublicKey::from_slice(&hex::decode(
+ "039e03a901b85534ff1e92c43c74431f7ce72046060fcf7a95c37e148f78c77255"
+ ).unwrap()).unwrap(),
+ short_channel_id: (197637 << 40) | (395016 << 16) | 2314,
+ fees: RoutingFees { base_msat: 2, proportional_millionths: 30 },
+ cltv_expiry_delta: 4,
+ htlc_maximum_msat: None, htlc_minimum_msat: None,
+ }]))
.build_raw()
.unwrap()
.sign(|_| {
RecoverableSignature::from_compact(
- & [
- 0xc6, 0x34, 0x86, 0xe8, 0x1f, 0x8c, 0x87, 0x8a, 0x10, 0x5b, 0xc9, 0xd9,
- 0x59, 0xaf, 0x19, 0x73, 0x85, 0x4c, 0x4d, 0xc5, 0x52, 0xc4, 0xf0, 0xe0,
- 0xe0, 0xc7, 0x38, 0x96, 0x03, 0xd6, 0xbd, 0xc6, 0x77, 0x07, 0xbf, 0x6b,
- 0xe9, 0x92, 0xa8, 0xce, 0x7b, 0xf5, 0x00, 0x16, 0xbb, 0x41, 0xd8, 0xa9,
- 0xb5, 0x35, 0x86, 0x52, 0xc4, 0x96, 0x04, 0x45, 0xa1, 0x70, 0xd0, 0x49,
- 0xce, 0xd4, 0x55, 0x8c
- ],
+ &hex::decode("6a6586db4e8f6d40e3a5bb92e4df5110c627e9ce493af237e20a046b4e86ea200178c59564ecf892f33a9558bf041b6ad2cb8292d7a6c351fbb7f2ae2d16b54e").unwrap(),
RecoveryId::from_i32(0).unwrap()
)
}).unwrap(),
- None
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
),
(
- "lnbc20m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp59g4z52329g4z52329g4z52329g4z52329g4z52329g4z52329g4q9qrsgqzfhag3vsafx4e5qssalvw4rn0phsvpp3e5h2xxyk9l8fxsutvndx9t840dqvdrlu2gqmk0q8apqrgnjy9amc07hmjl9e9yzqjks5w2gqgjnyms".to_owned(),
+ "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppj3a24vwu6r8ejrss3axul8rxldph2q7z99qrsgqz6qsgww34xlatfj6e3sngrwfy3ytkt29d2qttr8qz2mnedfqysuqypgqex4haa2h8fx3wnypranf3pdwyluftwe680jjcfp438u82xqphf75ym".to_owned(),
InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_000_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
+ .payment_secret(PaymentSecret([0x11; 32]))
.payment_hash(sha256::Hash::from_hex(
"0001020304050607080900010203040506070809000102030405060708090102"
).unwrap())
- .description("coffee beans".to_string())
- .amount_pico_btc(20000000000)
+ .fallback(Fallback::ScriptHash([143, 85, 86, 59, 154, 25, 243, 33, 194, 17, 233, 185, 243, 140, 223, 104, 110, 160, 120, 69]))
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("16810439d1a9bfd5a65acc61340dc92448bb2d456a80b58ce012b73cb5202438020500c9ab7ef5573a4d174c811f669885ae27f895bb3a3be52c243589f87518").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfppqw508d6qejxtdg4y5r3zarvary0c5xw7k9qrsgqt29a0wturnys2hhxpner2e3plp6jyj8qx7548zr2z7ptgjjc7hljm98xhjym0dg52sdrvqamxdezkmqg4gdrvwwnf0kv2jdfnl4xatsqmrnsse".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_000_000_000)
.timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
- .payment_secret(PaymentSecret([42; 32]))
+ .description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(),
+ program: vec![117, 30, 118, 232, 25, 145, 150, 212, 84, 148, 28, 69, 209, 179, 163, 35, 241, 67, 59, 214]
+ })
.build_raw()
.unwrap()
- .sign::<_, ()>(|msg_hash| {
- let privkey = SecretKey::from_slice(&[41; 32]).unwrap();
- let secp_ctx = Secp256k1::new();
- Ok(secp_ctx.sign_recoverable(msg_hash, &privkey))
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("5a8bd7b97c1cc9055ee60cf2356621f8752248e037a953886a1782b44a58f5ff2d94e6bc89b7b514541a3603bb33722b6c08aa1a3639d34becc549a99fea6eae").unwrap(),
+ RecoveryId::from_i32(0).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc20m1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygshp58yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqfp4qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q9qrsgq9vlvyj8cqvq6ggvpwd53jncp9nwc47xlrsnenq2zp70fq83qlgesn4u3uyf4tesfkkwwfg3qs54qe426hp3tz7z6sweqdjg05axsrjqp9yrrwc".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_000_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .description_hash(sha256::Hash::hash(b"One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon"))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .fallback(Fallback::SegWitProgram { version: u5::try_from_u8(0).unwrap(),
+ program: vec![24, 99, 20, 60, 20, 197, 22, 104, 4, 189, 25, 32, 51, 86, 218, 19, 108, 152, 86, 120, 205, 77, 39, 161, 184, 198, 50, 150, 4, 144, 50, 98]
})
- .unwrap(),
- None
- )
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("2b3ec248f80301a421817369194f012cdd8af8df1c279981420f9e901e20fa3309d791e11355e609b59ce4a220852a0cd55ab862b1785a83b206c90fa74d01c8").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc9678785340p1pwmna7lpp5gc3xfm08u9qy06djf8dfflhugl6p7lgza6dsjxq454gxhj9t7a0sd8dgfkx7cmtwd68yetpd5s9xar0wfjn5gpc8qhrsdfq24f5ggrxdaezqsnvda3kkum5wfjkzmfqf3jkgem9wgsyuctwdus9xgrcyqcjcgpzgfskx6eqf9hzqnteypzxz7fzypfhg6trddjhygrcyqezcgpzfysywmm5ypxxjemgw3hxjmn8yptk7untd9hxwg3q2d6xjcmtv4ezq7pqxgsxzmnyyqcjqmt0wfjjq6t5v4khxsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsxqyjw5qcqp2rzjq0gxwkzc8w6323m55m4jyxcjwmy7stt9hwkwe2qxmy8zpsgg7jcuwz87fcqqeuqqqyqqqqlgqqqqn3qq9q9qrsgqrvgkpnmps664wgkp43l22qsgdw4ve24aca4nymnxddlnp8vh9v2sdxlu5ywdxefsfvm0fq3sesf08uf6q9a2ke0hc9j6z6wlxg5z5kqpu2v9wz".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(967878534)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1572468703))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "462264ede7e14047e9b249da94fefc47f41f7d02ee9b091815a5506bc8abf75f"
+ ).unwrap())
+ .expiry_time(Duration::from_secs(604800))
+ .min_final_cltv_expiry(10)
+ .description("Blockstream Store: 88.85 USD for Blockstream Ledger Nano S x 1, \"Back In My Day\" Sticker x 2, \"I Got Lightning Working\" Sticker x 2 and 1 more items".to_owned())
+ .private_route(RouteHint(vec![RouteHintHop {
+ src_node_id: PublicKey::from_slice(&hex::decode(
+ "03d06758583bb5154774a6eb221b1276c9e82d65bbaceca806d90e20c108f4b1c7"
+ ).unwrap()).unwrap(),
+ short_channel_id: (589390 << 40) | (3312 << 16) | 1,
+ fees: RoutingFees { base_msat: 1000, proportional_millionths: 2500 },
+ cltv_expiry_delta: 40,
+ htlc_maximum_msat: None, htlc_minimum_msat: None,
+ }]))
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("1b1160cf6186b55722c1ac7ea502086baaccaabdc76b326e666b7f309d972b15069bfca11cd365304b36f48230cc12f3f13a017aab65f7c165a169df32282a58").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ false, // Same features as set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2a25dxl5hrntdtn6zvydt7d66hyzsyhqs4wdynavys42xgl6sgx9c4g7me86a27t07mdtfry458rtjr0v92cnmswpsjscgt2vcse3sgpz3uapa".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_500_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .description("coffee beans".to_owned())
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("5755469bf4b8e6b6ae7a1308d5f9bad5c82812e0855cd24fac242aa323fa820c5c551ede4faeabcb7fb6d5a464ad0e35c86f615589ee0e0c250c216a662198c1").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ true, // Different features than set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "LNBC25M1PVJLUEZPP5QQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQQQSYQCYQ5RQWZQFQYPQDQ5VDHKVEN9V5SXYETPDEESSP5ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYG3ZYGS9Q5SQQQQQQQQQQQQQQQQSGQ2A25DXL5HRNTDTN6ZVYDT7D66HYZSYHQS4WDYNAVYS42XGL6SGX9C4G7ME86A27T07MDTFRY458RTJR0V92CNMSWPSJSCGT2VCSE3SGPZ3UAPA".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_500_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .description("coffee beans".to_owned())
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("5755469bf4b8e6b6ae7a1308d5f9bad5c82812e0855cd24fac242aa323fa820c5c551ede4faeabcb7fb6d5a464ad0e35c86f615589ee0e0c250c216a662198c1").unwrap(),
+ RecoveryId::from_i32(1).unwrap()
+ )
+ }).unwrap(),
+ true, // Different features than set in InvoiceBuilder
+ false, // No unknown fields
+ ),
+ (
+ "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q5sqqqqqqqqqqqqqqqqsgq2qrqqqfppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqppnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhpnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqhp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqspnqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsp4qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnp5qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqnpkqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqz599y53s3ujmcfjp5xrdap68qxymkqphwsexhmhr8wdz5usdzkzrse33chw6dlp3jhuhge9ley7j2ayx36kawe7kmgg8sv5ugdyusdcqzn8z9x".to_owned(),
+ InvoiceBuilder::new(Currency::Bitcoin)
+ .amount_milli_satoshis(2_500_000_000)
+ .timestamp(UNIX_EPOCH + Duration::from_secs(1496314658))
+ .payment_secret(PaymentSecret([0x11; 32]))
+ .payment_hash(sha256::Hash::from_hex(
+ "0001020304050607080900010203040506070809000102030405060708090102"
+ ).unwrap())
+ .description("coffee beans".to_owned())
+ .build_raw()
+ .unwrap()
+ .sign(|_| {
+ RecoverableSignature::from_compact(
+ &hex::decode("150a5252308f25bc2641a186de87470189bb003774326beee33b9a2a720d1584386631c5dda6fc3195f97464bfc93d2574868eadd767d6da1078329c4349c837").unwrap(),
+ RecoveryId::from_i32(0).unwrap()
+ )
+ }).unwrap(),
+ true, // Different features than set in InvoiceBuilder
+ true, // Some unknown fields
+ ),
]
}
-
#[test]
-fn serialize() {
- for (serialized, deserialized, _) in get_test_tuples() {
- assert_eq!(deserialized.to_string(), serialized);
- }
-}
-
-#[test]
-fn deserialize() {
- for (serialized, deserialized, maybe_error) in get_test_tuples() {
+fn invoice_deserialize() {
+ for (serialized, deserialized, ignore_feature_diff, ignore_unknown_fields) in get_test_tuples() {
+ eprintln!("Testing invoice {}...", serialized);
let parsed = serialized.parse::<SignedRawInvoice>().unwrap();
- assert_eq!(parsed, deserialized);
+ let (parsed_invoice, _, parsed_sig) = parsed.into_parts();
+ let (deserialized_invoice, _, deserialized_sig) = deserialized.into_parts();
- let validated = Invoice::from_signed(parsed);
+ assert_eq!(deserialized_sig, parsed_sig);
+ assert_eq!(deserialized_invoice.hrp, parsed_invoice.hrp);
+ assert_eq!(deserialized_invoice.data.timestamp, parsed_invoice.data.timestamp);
- if let Some(error) = maybe_error {
- assert_eq!(Err(error), validated);
- } else {
- assert!(validated.is_ok());
+ let mut deserialized_hunks: HashSet<_> = deserialized_invoice.data.tagged_fields.iter().collect();
+ let mut parsed_hunks: HashSet<_> = parsed_invoice.data.tagged_fields.iter().collect();
+ if ignore_feature_diff {
+ deserialized_hunks.retain(|h|
+ if let RawTaggedField::KnownSemantics(TaggedField::Features(_)) = h { false } else { true });
+ parsed_hunks.retain(|h|
+ if let RawTaggedField::KnownSemantics(TaggedField::Features(_)) = h { false } else { true });
+ }
+ if ignore_unknown_fields {
+ parsed_hunks.retain(|h|
+ if let RawTaggedField::UnknownSemantics(_) = h { false } else { true });
}
+ assert_eq!(deserialized_hunks, parsed_hunks);
+
+ Invoice::from_signed(serialized.parse::<SignedRawInvoice>().unwrap()).unwrap();
}
}
+
+#[test]
+fn test_bolt_invalid_invoices() {
+ // Tests the BOLT 11 invalid invoice test vectors
+ assert_eq!(Invoice::from_str(
+ "lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5vdhkven9v5sxyetpdeessp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9q4psqqqqqqqqqqqqqqqqsgqtqyx5vggfcsll4wu246hz02kp85x4katwsk9639we5n5yngc3yhqkm35jnjw4len8vrnqnf5ejh0mzj9n3vz2px97evektfm2l6wqccp3y7372"
+ ), Err(ParseOrSemanticError::SemanticError(SemanticError::InvalidFeatures)));
+ assert_eq!(Invoice::from_str(
+ "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrnt"
+ ), Err(ParseOrSemanticError::ParseError(ParseError::Bech32Error(bech32::Error::InvalidChecksum))));
+ assert_eq!(Invoice::from_str(
+ "pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
+ ), Err(ParseOrSemanticError::ParseError(ParseError::Bech32Error(bech32::Error::MissingSeparator))));
+ assert_eq!(Invoice::from_str(
+ "LNBC2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpquwpc4curk03c9wlrswe78q4eyqc7d8d0xqzpuyk0sg5g70me25alkluzd2x62aysf2pyy8edtjeevuv4p2d5p76r4zkmneet7uvyakky2zr4cusd45tftc9c5fh0nnqpnl2jfll544esqchsrny"
+ ), Err(ParseOrSemanticError::ParseError(ParseError::Bech32Error(bech32::Error::MixedCase))));
+ assert_eq!(Invoice::from_str(
+ "lnbc2500u1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqwgt7mcn5yqw3yx0w94pswkpq6j9uh6xfqqqtsk4tnarugeektd4hg5975x9am52rz4qskukxdmjemg92vvqz8nvmsye63r5ykel43pgz7zq0g2"
+ ), Err(ParseOrSemanticError::SemanticError(SemanticError::InvalidSignature)));
+ assert_eq!(Invoice::from_str(
+ "lnbc1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6na6hlh"
+ ), Err(ParseOrSemanticError::ParseError(ParseError::TooShortDataPart)));
+ assert_eq!(Invoice::from_str(
+ "lnbc2500x1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgqrrzc4cvfue4zp3hggxp47ag7xnrlr8vgcmkjxk3j5jqethnumgkpqp23z9jclu3v0a7e0aruz366e9wqdykw6dxhdzcjjhldxq0w6wgqcnu43j"
+ ), Err(ParseOrSemanticError::ParseError(ParseError::UnknownSiPrefix)));
+ assert_eq!(Invoice::from_str(
+ "lnbc2500000001p1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdq5xysxxatsyp3k7enxv4jsxqzpusp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygs9qrsgq0lzc236j96a95uv0m3umg28gclm5lqxtqqwk32uuk4k6673k6n5kfvx3d2h8s295fad45fdhmusm8sjudfhlf6dcsxmfvkeywmjdkxcp99202x"
+ ), Err(ParseOrSemanticError::SemanticError(SemanticError::ImpreciseAmount)));
+}
//! async fn connect_to_node(peer_manager: PeerManager, chain_monitor: Arc<ChainMonitor>, channel_manager: ChannelManager, their_node_id: PublicKey, addr: SocketAddr) {
//! lightning_net_tokio::connect_outbound(peer_manager, their_node_id, addr).await;
//! loop {
-//! channel_manager.await_persistable_update();
-//! channel_manager.process_pending_events(&|event| {
-//! // Handle the event!
-//! });
-//! chain_monitor.process_pending_events(&|event| {
+//! let event_handler = |event: &Event| {
//! // Handle the event!
-//! });
+//! };
+//! channel_manager.await_persistable_update();
+//! channel_manager.process_pending_events(&event_handler);
+//! chain_monitor.process_pending_events(&event_handler);
//! }
//! }
//!
//! async fn accept_socket(peer_manager: PeerManager, chain_monitor: Arc<ChainMonitor>, channel_manager: ChannelManager, socket: TcpStream) {
//! lightning_net_tokio::setup_inbound(peer_manager, socket);
//! loop {
-//! channel_manager.await_persistable_update();
-//! channel_manager.process_pending_events(&|event| {
-//! // Handle the event!
-//! });
-//! chain_monitor.process_pending_events(&|event| {
+//! let event_handler = |event: &Event| {
//! // Handle the event!
-//! });
+//! };
+//! channel_manager.await_persistable_update();
+//! channel_manager.process_pending_events(&event_handler);
+//! chain_monitor.process_pending_events(&event_handler);
//! }
//! }
//! ```
fn handle_node_announcement(&self, _msg: &NodeAnnouncement) -> Result<bool, LightningError> { Ok(false) }
fn handle_channel_announcement(&self, _msg: &ChannelAnnouncement) -> Result<bool, LightningError> { Ok(false) }
fn handle_channel_update(&self, _msg: &ChannelUpdate) -> Result<bool, LightningError> { Ok(false) }
- fn handle_htlc_fail_channel_update(&self, _update: &HTLCFailChannelUpdate) { }
fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) -> Vec<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> { Vec::new() }
fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec<NodeAnnouncement> { Vec::new() }
fn sync_routing_table(&self, _their_node_id: &PublicKey, _init_msg: &Init) { }
use chain::{Filter, WatchedOutput};
use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
use chain::channelmonitor;
-use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, MonitorEvent, Persist, TransactionOutputs};
+use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, Balance, MonitorEvent, Persist, TransactionOutputs};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::Sign;
use util::logger::Logger;
use util::events;
use util::events::EventHandler;
+use ln::channelmanager::ChannelDetails;
use prelude::*;
use sync::RwLock;
}
}
+ /// Gets the balances in the contained [`ChannelMonitor`]s which are claimable on-chain or
+ /// claims which are awaiting confirmation.
+ ///
+ /// Includes the balances from each [`ChannelMonitor`] *except* those included in
+ /// `ignored_channels`, allowing you to filter out balances from channels which are still open
+ /// (and whose balance should likely be pulled from the [`ChannelDetails`]).
+ ///
+ /// See [`ChannelMonitor::get_claimable_balances`] for more details on the exact criteria for
+ /// inclusion in the return value.
+ pub fn get_claimable_balances(&self, ignored_channels: &[ChannelDetails]) -> Vec<Balance> {
+ let mut ret = Vec::new();
+ let monitors = self.monitors.read().unwrap();
+ for (_, monitor) in monitors.iter().filter(|(funding_outpoint, _)| {
+ for chan in ignored_channels {
+ if chan.funding_txo.as_ref() == Some(funding_outpoint) {
+ return false;
+ }
+ }
+ true
+ }) {
+ ret.append(&mut monitor.get_claimable_balances());
+ }
+ ret
+ }
+
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
use util::events::EventsProvider;
let events = core::cell::RefCell::new(Vec::new());
- let event_handler = |event| events.borrow_mut().push(event);
+ let event_handler = |event: &events::Event| events.borrow_mut().push(event.clone());
self.process_pending_events(&event_handler);
events.into_inner()
}
pending_events.append(&mut monitor.get_and_clear_pending_events());
}
for event in pending_events.drain(..) {
- handler.handle_event(event);
+ handler.handle_event(&event);
}
}
}
/// with at worst this delay, so we are not only using this value as a mercy for them but also
/// us as a safeguard to delay with enough time.
pub(crate) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3;
-/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding inbound
-/// HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us losing money.
+/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding
+/// inbound HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us
+/// losing money.
+///
+/// Note that this is a library-wide security assumption. If a reorg deeper than this number of
+/// blocks occurs, counterparties may be able to steal funds or claims made by and balances exposed
+/// by a [`ChannelMonitor`] may be incorrect.
// We also use this delay to be sure we can remove our in-flight claim txn from bump candidates buffer.
// It may cause spurious generation of bumped claim txn but that's alright given the outpoint is already
// solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not
b_htlc_key: PublicKey,
delayed_payment_key: PublicKey,
per_commitment_point: PublicKey,
- feerate_per_kw: u32,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
+ to_self_value_sat: u64,
+ feerate_per_kw: u32,
}
impl_writeable_tlv_based!(HolderSignedTx, {
(0, txid, required),
+ // Note that this is filled in with data from OnchainTxHandler if it's missing.
+ // For HolderSignedTx objects serialized with 0.0.100+, this should be filled in.
+ (1, to_self_value_sat, (default_value, u64::max_value())),
(2, revocation_key, required),
(4, a_htlc_key, required),
(6, b_htlc_key, required),
(14, htlc_outputs, vec_type)
});
-/// We use this to track counterparty commitment transactions and htlcs outputs and
-/// use it to generate any justice or 2nd-stage preimage/timeout transactions.
+/// We use this to track static counterparty commitment transaction data and to generate any
+/// justice or 2nd-stage preimage/timeout transactions.
#[derive(PartialEq)]
-struct CounterpartyCommitmentTransaction {
+struct CounterpartyCommitmentParameters {
counterparty_delayed_payment_base_key: PublicKey,
counterparty_htlc_base_key: PublicKey,
on_counterparty_tx_csv: u16,
- per_htlc: HashMap<Txid, Vec<HTLCOutputInCommitment>>
}
-impl Writeable for CounterpartyCommitmentTransaction {
+impl Writeable for CounterpartyCommitmentParameters {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.write_all(&byte_utils::be64_to_array(self.per_htlc.len() as u64))?;
- for (ref txid, ref htlcs) in self.per_htlc.iter() {
- w.write_all(&txid[..])?;
- w.write_all(&byte_utils::be64_to_array(htlcs.len() as u64))?;
- for &ref htlc in htlcs.iter() {
- htlc.write(w)?;
- }
- }
+ w.write_all(&byte_utils::be64_to_array(0))?;
write_tlv_fields!(w, {
(0, self.counterparty_delayed_payment_base_key, required),
(2, self.counterparty_htlc_base_key, required),
Ok(())
}
}
-impl Readable for CounterpartyCommitmentTransaction {
+impl Readable for CounterpartyCommitmentParameters {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
let counterparty_commitment_transaction = {
+ // Versions prior to 0.0.100 had some per-HTLC state stored here, which is no longer
+ // used. Read it for compatibility.
let per_htlc_len: u64 = Readable::read(r)?;
- let mut per_htlc = HashMap::with_capacity(cmp::min(per_htlc_len as usize, MAX_ALLOC_SIZE / 64));
for _ in 0..per_htlc_len {
- let txid: Txid = Readable::read(r)?;
+ let _txid: Txid = Readable::read(r)?;
let htlcs_count: u64 = Readable::read(r)?;
- let mut htlcs = Vec::with_capacity(cmp::min(htlcs_count as usize, MAX_ALLOC_SIZE / 32));
for _ in 0..htlcs_count {
- let htlc = Readable::read(r)?;
- htlcs.push(htlc);
- }
- if let Some(_) = per_htlc.insert(txid, htlcs) {
- return Err(DecodeError::InvalidValue);
+ let _htlc: HTLCOutputInCommitment = Readable::read(r)?;
}
}
+
let mut counterparty_delayed_payment_base_key = OptionDeserWrapper(None);
let mut counterparty_htlc_base_key = OptionDeserWrapper(None);
let mut on_counterparty_tx_csv: u16 = 0;
(2, counterparty_htlc_base_key, required),
(4, on_counterparty_tx_csv, required),
});
- CounterpartyCommitmentTransaction {
+ CounterpartyCommitmentParameters {
counterparty_delayed_payment_base_key: counterparty_delayed_payment_base_key.0.unwrap(),
counterparty_htlc_base_key: counterparty_htlc_base_key.0.unwrap(),
on_counterparty_tx_csv,
- per_htlc,
}
};
Ok(counterparty_commitment_transaction)
impl OnchainEventEntry {
fn confirmation_threshold(&self) -> u32 {
let mut conf_threshold = self.height + ANTI_REORG_DELAY - 1;
- if let OnchainEvent::MaturingOutput {
- descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor)
- } = self.event {
- // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means
- // it's broadcastable when we see the previous block.
- conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1);
+ match self.event {
+ OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::DelayedPaymentOutput(ref descriptor)
+ } => {
+ // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means
+ // it's broadcastable when we see the previous block.
+ conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1);
+ },
+ OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } |
+ OnchainEvent::HTLCSpendConfirmation { on_to_local_output_csv: Some(csv), .. } => {
+ // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means
+ // it's broadcastable when we see the previous block.
+ conf_threshold = cmp::max(conf_threshold, self.height + csv as u32 - 1);
+ },
+ _ => {},
}
conf_threshold
}
/// once they mature to enough confirmations (ANTI_REORG_DELAY)
#[derive(PartialEq)]
enum OnchainEvent {
- /// HTLC output getting solved by a timeout, at maturation we pass upstream payment source information to solve
- /// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can
- /// only win from it, so it's never an OnchainEvent
+ /// An outbound HTLC failing after a transaction is confirmed. Used
+ /// * when an outbound HTLC output is spent by us after the HTLC timed out
+ /// * an outbound HTLC which was not present in the commitment transaction which appeared
+ /// on-chain (either because it was not fully committed to or it was dust).
+ /// Note that this is *not* used for preimage claims, as those are passed upstream immediately,
+ /// appearing only as an `HTLCSpendConfirmation`, below.
HTLCUpdate {
source: HTLCSource,
payment_hash: PaymentHash,
onchain_value_satoshis: Option<u64>,
+ /// None in the second case, above, ie when there is no relevant output in the commitment
+ /// transaction which appeared on chain.
+ input_idx: Option<u32>,
},
MaturingOutput {
descriptor: SpendableOutputDescriptor,
},
+ /// A spend of the funding output, either a commitment transaction or a cooperative closing
+ /// transaction.
+ FundingSpendConfirmation {
+ /// The CSV delay for the output of the funding spend transaction (implying it is a local
+ /// commitment transaction, and this is the delay on the to_self output).
+ on_local_output_csv: Option<u16>,
+ },
+ /// A spend of a commitment transaction HTLC output, set in the cases where *no* `HTLCUpdate`
+ /// is constructed. This is used when
+ /// * an outbound HTLC is claimed by our counterparty with a preimage, causing us to
+ /// immediately claim the HTLC on the inbound edge and track the resolution here,
+ /// * an inbound HTLC is claimed by our counterparty (with a timeout),
+ /// * an inbound HTLC is claimed by us (with a preimage).
+ /// * a revoked-state HTLC transaction was broadcasted, which was claimed by the revocation
+ /// signature.
+ HTLCSpendConfirmation {
+ input_idx: u32,
+ /// If the claim was made by either party with a preimage, this is filled in
+ preimage: Option<PaymentPreimage>,
+ /// If the claim was made by us on an inbound HTLC against a local commitment transaction,
+ /// we set this to the output CSV value which we will have to wait until to spend the
+ /// output (and generate a SpendableOutput event).
+ on_to_local_output_csv: Option<u16>,
+ },
}
impl Writeable for OnchainEventEntry {
(0, source, required),
(1, onchain_value_satoshis, option),
(2, payment_hash, required),
+ (3, input_idx, option),
},
(1, MaturingOutput) => {
(0, descriptor, required),
},
+ (3, FundingSpendConfirmation) => {
+ (0, on_local_output_csv, option),
+ },
+ (5, HTLCSpendConfirmation) => {
+ (0, input_idx, required),
+ (2, preimage, option),
+ (4, on_to_local_output_csv, option),
+ },
+
);
#[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))]
},
);
+/// Details about the balance(s) available for spending once the channel appears on chain.
+///
+/// See [`ChannelMonitor::get_claimable_balances`] for more details on when these will or will not
+/// be provided.
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[cfg_attr(test, derive(PartialOrd, Ord))]
+pub enum Balance {
+ /// The channel is not yet closed (or the commitment or closing transaction has not yet
+ /// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is
+ /// force-closed now.
+ ClaimableOnChannelClose {
+ /// The amount available to claim, in satoshis, excluding the on-chain fees which will be
+ /// required to do so.
+ claimable_amount_satoshis: u64,
+ },
+ /// The channel has been closed, and the given balance is ours but awaiting confirmations until
+ /// we consider it spendable.
+ ClaimableAwaitingConfirmations {
+ /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which
+ /// were spent in broadcasting the transaction.
+ claimable_amount_satoshis: u64,
+ /// The height at which an [`Event::SpendableOutputs`] event will be generated for this
+ /// amount.
+ confirmation_height: u32,
+ },
+ /// The channel has been closed, and the given balance should be ours but awaiting spending
+ /// transaction confirmation. If the spending transaction does not confirm in time, it is
+ /// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain.
+ ///
+ /// Once the spending transaction confirms, before it has reached enough confirmations to be
+ /// considered safe from chain reorganizations, the balance will instead be provided via
+ /// [`Balance::ClaimableAwaitingConfirmations`].
+ ContentiousClaimable {
+ /// The amount available to claim, in satoshis, excluding the on-chain fees which will be
+ /// required to do so.
+ claimable_amount_satoshis: u64,
+ /// The height at which the counterparty may be able to claim the balance if we have not
+ /// done so.
+ timeout_height: u32,
+ },
+ /// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain
+ /// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat
+ /// likely to be claimed by our counterparty before we do.
+ MaybeClaimableHTLCAwaitingTimeout {
+ /// The amount available to claim, in satoshis, excluding the on-chain fees which will be
+ /// required to do so.
+ claimable_amount_satoshis: u64,
+ /// The height at which we will be able to claim the balance if our counterparty has not
+ /// done so.
+ claimable_height: u32,
+ },
+}
+
+/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY.
+#[derive(PartialEq)]
+struct IrrevocablyResolvedHTLC {
+ input_idx: u32,
+ /// Only set if the HTLC claim was ours using a payment preimage
+ payment_preimage: Option<PaymentPreimage>,
+}
+
+impl_writeable_tlv_based!(IrrevocablyResolvedHTLC, {
+ (0, input_idx, required),
+ (2, payment_preimage, option),
+});
+
/// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates
/// on-chain transactions to ensure no loss of funds occurs.
///
current_counterparty_commitment_txid: Option<Txid>,
prev_counterparty_commitment_txid: Option<Txid>,
- counterparty_tx_cache: CounterpartyCommitmentTransaction,
+ counterparty_commitment_params: CounterpartyCommitmentParameters,
funding_redeemscript: Script,
channel_value_satoshis: u64,
// first is the idx of the first of the two revocation points
// remote monitor out-of-order with regards to the block view.
holder_tx_signed: bool,
+ funding_spend_confirmed: Option<Txid>,
+ /// The set of HTLCs which have been either claimed or failed on chain and have reached
+ /// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the
+ /// spending CSV for revocable outputs).
+ htlcs_resolved_on_chain: Vec<IrrevocablyResolvedHTLC>,
+
// We simply modify best_block in Channel's block_connected so that serialization is
// consistent but hopefully the users' copy handles block_connected in a consistent way.
// (we do *not*, however, update them in update_monitor to ensure any local user copies keep
self.funding_info != other.funding_info ||
self.current_counterparty_commitment_txid != other.current_counterparty_commitment_txid ||
self.prev_counterparty_commitment_txid != other.prev_counterparty_commitment_txid ||
- self.counterparty_tx_cache != other.counterparty_tx_cache ||
+ self.counterparty_commitment_params != other.counterparty_commitment_params ||
self.funding_redeemscript != other.funding_redeemscript ||
self.channel_value_satoshis != other.channel_value_satoshis ||
self.their_cur_revocation_points != other.their_cur_revocation_points ||
self.onchain_events_awaiting_threshold_conf != other.onchain_events_awaiting_threshold_conf ||
self.outputs_to_watch != other.outputs_to_watch ||
self.lockdown_from_offchain != other.lockdown_from_offchain ||
- self.holder_tx_signed != other.holder_tx_signed
+ self.holder_tx_signed != other.holder_tx_signed ||
+ self.funding_spend_confirmed != other.funding_spend_confirmed ||
+ self.htlcs_resolved_on_chain != other.htlcs_resolved_on_chain
{
false
} else {
self.current_counterparty_commitment_txid.write(writer)?;
self.prev_counterparty_commitment_txid.write(writer)?;
- self.counterparty_tx_cache.write(writer)?;
+ self.counterparty_commitment_params.write(writer)?;
self.funding_redeemscript.write(writer)?;
self.channel_value_satoshis.write(writer)?;
self.lockdown_from_offchain.write(writer)?;
self.holder_tx_signed.write(writer)?;
- write_tlv_fields!(writer, {});
+ write_tlv_fields!(writer, {
+ (1, self.funding_spend_confirmed, option),
+ (3, self.htlcs_resolved_on_chain, vec_type),
+ });
Ok(())
}
let counterparty_channel_parameters = channel_parameters.counterparty_parameters.as_ref().unwrap();
let counterparty_delayed_payment_base_key = counterparty_channel_parameters.pubkeys.delayed_payment_basepoint;
let counterparty_htlc_base_key = counterparty_channel_parameters.pubkeys.htlc_basepoint;
- let counterparty_tx_cache = CounterpartyCommitmentTransaction { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv, per_htlc: HashMap::new() };
+ let counterparty_commitment_params = CounterpartyCommitmentParameters { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv };
let channel_keys_id = keys.channel_keys_id();
let holder_revocation_basepoint = keys.pubkeys().revocation_basepoint;
b_htlc_key: tx_keys.countersignatory_htlc_key,
delayed_payment_key: tx_keys.broadcaster_delayed_payment_key,
per_commitment_point: tx_keys.per_commitment_point,
- feerate_per_kw: trusted_tx.feerate_per_kw(),
htlc_outputs: Vec::new(), // There are never any HTLCs in the initial commitment transactions
+ to_self_value_sat: initial_holder_commitment_tx.to_broadcaster_value_sat(),
+ feerate_per_kw: trusted_tx.feerate_per_kw(),
};
(holder_commitment_tx, trusted_tx.commitment_number())
};
current_counterparty_commitment_txid: None,
prev_counterparty_commitment_txid: None,
- counterparty_tx_cache,
+ counterparty_commitment_params,
funding_redeemscript,
channel_value_satoshis,
their_cur_revocation_points: None,
lockdown_from_offchain: false,
holder_tx_signed: false,
+ funding_spend_confirmed: None,
+ htlcs_resolved_on_chain: Vec::new(),
best_block,
pub fn current_best_block(&self) -> BestBlock {
self.inner.lock().unwrap().best_block.clone()
}
+
+ /// Gets the balances in this channel which are either claimable by us if we were to
+ /// force-close the channel now or which are claimable on-chain (possibly awaiting
+ /// confirmation).
+ ///
+ /// Any balances in the channel which are available on-chain (excluding on-chain fees) are
+ /// included here until an [`Event::SpendableOutputs`] event has been generated for the
+ /// balance, or until our counterparty has claimed the balance and accrued several
+ /// confirmations on the claim transaction.
+ ///
+ /// Note that the balances available when you or your counterparty have broadcasted revoked
+ /// state(s) may not be fully captured here.
+ // TODO, fix that ^
+ ///
+ /// See [`Balance`] for additional details on the types of claimable balances which
+ /// may be returned here and their meanings.
+ pub fn get_claimable_balances(&self) -> Vec<Balance> {
+ let mut res = Vec::new();
+ let us = self.inner.lock().unwrap();
+
+ let mut confirmed_txid = us.funding_spend_confirmed;
+ let mut pending_commitment_tx_conf_thresh = None;
+ let funding_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
+ Some((event.txid, event.confirmation_threshold()))
+ } else { None }
+ });
+ if let Some((txid, conf_thresh)) = funding_spend_pending {
+ debug_assert!(us.funding_spend_confirmed.is_none(),
+ "We have a pending funding spend awaiting anti-reorg confirmation, we can't have confirmed it already!");
+ confirmed_txid = Some(txid);
+ pending_commitment_tx_conf_thresh = Some(conf_thresh);
+ }
+
+ macro_rules! walk_htlcs {
+ ($holder_commitment: expr, $htlc_iter: expr) => {
+ for htlc in $htlc_iter {
+ if let Some(htlc_input_idx) = htlc.transaction_output_index {
+ if us.htlcs_resolved_on_chain.iter().any(|v| v.input_idx == htlc_input_idx) {
+ assert!(us.funding_spend_confirmed.is_some());
+ } else if htlc.offered == $holder_commitment {
+ // If the payment was outbound, check if there's an HTLCUpdate
+ // indicating we have spent this HTLC with a timeout, claiming it back
+ // and awaiting confirmations on it.
+ let htlc_update_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::HTLCUpdate { input_idx: Some(input_idx), .. } = event.event {
+ if input_idx == htlc_input_idx { Some(event.confirmation_threshold()) } else { None }
+ } else { None }
+ });
+ if let Some(conf_thresh) = htlc_update_pending {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ confirmation_height: conf_thresh,
+ });
+ } else {
+ res.push(Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ claimable_height: htlc.cltv_expiry,
+ });
+ }
+ } else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
+ // Otherwise (the payment was inbound), only expose it as claimable if
+ // we know the preimage.
+ // Note that if there is a pending claim, but it did not use the
+ // preimage, we lost funds to our counterparty! We will then continue
+ // to show it as ContentiousClaimable until ANTI_REORG_DELAY.
+ let htlc_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } = event.event {
+ if input_idx == htlc_input_idx {
+ Some((event.confirmation_threshold(), preimage.is_some()))
+ } else { None }
+ } else { None }
+ });
+ if let Some((conf_thresh, true)) = htlc_spend_pending {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ confirmation_height: conf_thresh,
+ });
+ } else {
+ res.push(Balance::ContentiousClaimable {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ timeout_height: htlc.cltv_expiry,
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if let Some(txid) = confirmed_txid {
+ let mut found_commitment_tx = false;
+ if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
+ walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().map(|(a, _)| a));
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ if let Some(value) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
+ if let OnchainEvent::MaturingOutput {
+ descriptor: SpendableOutputDescriptor::StaticPaymentOutput(descriptor)
+ } = &event.event {
+ Some(descriptor.output.value)
+ } else { None }
+ }) {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: value,
+ confirmation_height: conf_thresh,
+ });
+ } else {
+ // If a counterparty commitment transaction is awaiting confirmation, we
+ // should either have a StaticPaymentOutput MaturingOutput event awaiting
+ // confirmation with the same height or have never met our dust amount.
+ }
+ }
+ found_commitment_tx = true;
+ } else if txid == us.current_holder_commitment_tx.txid {
+ walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, _)| a));
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
+ confirmation_height: conf_thresh,
+ });
+ }
+ found_commitment_tx = true;
+ } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
+ if txid == prev_commitment.txid {
+ walk_htlcs!(true, prev_commitment.htlc_outputs.iter().map(|(a, _, _)| a));
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: prev_commitment.to_self_value_sat,
+ confirmation_height: conf_thresh,
+ });
+ }
+ found_commitment_tx = true;
+ }
+ }
+ if !found_commitment_tx {
+ if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
+ // We blindly assume this is a cooperative close transaction here, and that
+ // neither us nor our counterparty misbehaved. At worst we've under-estimated
+ // the amount we can claim as we'll punish a misbehaving counterparty.
+ res.push(Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
+ confirmation_height: conf_thresh,
+ });
+ }
+ }
+ // TODO: Add logic to provide claimable balances for counterparty broadcasting revoked
+ // outputs.
+ } else {
+ let mut claimable_inbound_htlc_value_sat = 0;
+ for (htlc, _, _) in us.current_holder_commitment_tx.htlc_outputs.iter() {
+ if htlc.transaction_output_index.is_none() { continue; }
+ if htlc.offered {
+ res.push(Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: htlc.amount_msat / 1000,
+ claimable_height: htlc.cltv_expiry,
+ });
+ } else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
+ claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
+ }
+ }
+ res.push(Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat,
+ });
+ }
+
+ res
+ }
}
/// Compares a broadcasted commitment transaction's HTLCs with those in the latest state,
source: (**source).clone(),
payment_hash: htlc.payment_hash.clone(),
onchain_value_satoshis: Some(htlc.amount_msat / 1000),
+ input_idx: None,
},
};
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})",
htlcs.push(htlc.0);
}
}
- self.counterparty_tx_cache.per_htlc.insert(txid, htlcs);
}
/// Informs this monitor of the latest holder (ie broadcastable) commitment transaction. The
b_htlc_key: tx_keys.countersignatory_htlc_key,
delayed_payment_key: tx_keys.broadcaster_delayed_payment_key,
per_commitment_point: tx_keys.per_commitment_point,
- feerate_per_kw: trusted_tx.feerate_per_kw(),
htlc_outputs,
+ to_self_value_sat: holder_commitment_tx.to_broadcaster_value_sat(),
+ feerate_per_kw: trusted_tx.feerate_per_kw(),
}
};
self.onchain_tx_handler.provide_latest_holder_tx(holder_commitment_tx);
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
let revocation_pubkey = ignore_error!(chan_utils::derive_public_revocation_key(&self.secp_ctx, &per_commitment_point, &self.holder_revocation_basepoint));
- let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_tx_cache.counterparty_delayed_payment_base_key));
+ let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.counterparty_commitment_params.counterparty_delayed_payment_base_key));
- let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_tx_cache.on_counterparty_tx_csv, &delayed_key);
+ let revokeable_redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.counterparty_commitment_params.on_counterparty_tx_csv, &delayed_key);
let revokeable_p2wsh = revokeable_redeemscript.to_v0_p2wsh();
// First, process non-htlc outputs (to_holder & to_counterparty)
for (idx, outp) in tx.output.iter().enumerate() {
if outp.script_pubkey == revokeable_p2wsh {
- let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_tx_cache.on_counterparty_tx_csv);
- let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height);
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, outp.value, self.counterparty_commitment_params.on_counterparty_tx_csv);
+ let justice_package = PackageTemplate::build_package(commitment_txid, idx as u32, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
claimable_outpoints.push(justice_package);
}
}
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
}
- let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone());
+ let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone());
let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, true, height);
claimable_outpoints.push(justice_package);
}
}
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
if preimage.is_some() || !htlc.offered {
- let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, preimage.unwrap(), htlc.clone())) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(*revocation_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, htlc.clone())) };
+ let counterparty_htlc_outp = if htlc.offered { PackageSolvingData::CounterpartyOfferedHTLCOutput(CounterpartyOfferedHTLCOutput::build(*revocation_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, preimage.unwrap(), htlc.clone())) } else { PackageSolvingData::CounterpartyReceivedHTLCOutput(CounterpartyReceivedHTLCOutput::build(*revocation_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, htlc.clone())) };
let aggregation = if !htlc.offered { false } else { true };
let counterparty_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, counterparty_htlc_outp, htlc.cltv_expiry,aggregation, 0);
claimable_outpoints.push(counterparty_package);
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
log_error!(logger, "Got broadcast of revoked counterparty HTLC transaction, spending {}:{}", htlc_txid, 0);
- let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_tx_cache.counterparty_delayed_payment_base_key, self.counterparty_tx_cache.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_tx_cache.on_counterparty_tx_csv);
- let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_tx_cache.on_counterparty_tx_csv as u32, true, height);
+ let revk_outp = RevokedOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, tx.output[0].value, self.counterparty_commitment_params.on_counterparty_tx_csv);
+ let justice_package = PackageTemplate::build_package(htlc_txid, 0, PackageSolvingData::RevokedOutput(revk_outp), height + self.counterparty_commitment_params.on_counterparty_tx_csv as u32, true, height);
let claimable_outpoints = vec!(justice_package);
let outputs = vec![(0, tx.output[0].clone())];
(claimable_outpoints, Some((htlc_txid, outputs)))
/// Attempts to claim any claimable HTLCs in a commitment transaction which was not (yet)
/// revoked using data in holder_claimable_outpoints.
/// Should not be used if check_spend_revoked_transaction succeeds.
- fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> (Vec<PackageTemplate>, TransactionOutputs) where L::Target: Logger {
+ /// Returns None unless the transaction is definitely one of our commitment transactions.
+ fn check_spend_holder_transaction<L: Deref>(&mut self, tx: &Transaction, height: u32, logger: &L) -> Option<(Vec<PackageTemplate>, TransactionOutputs)> where L::Target: Logger {
let commitment_txid = tx.txid();
let mut claim_requests = Vec::new();
let mut watch_outputs = Vec::new();
}
if is_holder_tx {
+ Some((claim_requests, (commitment_txid, watch_outputs)))
+ } else {
+ None
}
-
- (claim_requests, (commitment_txid, watch_outputs))
}
pub fn get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
// filters.
let prevout = &tx.input[0].previous_output;
if prevout.txid == self.funding_info.0.txid && prevout.vout == self.funding_info.0.index as u32 {
+ let mut balance_spendable_csv = None;
+ log_info!(logger, "Channel closed by funding output spend in txid {}.", log_bytes!(tx.txid()));
if (tx.input[0].sequence >> 8*3) as u8 == 0x80 && (tx.lock_time >> 8*3) as u8 == 0x20 {
let (mut new_outpoints, new_outputs) = self.check_spend_counterparty_transaction(&tx, height, &logger);
if !new_outputs.1.is_empty() {
watch_outputs.push(new_outputs);
}
+ claimable_outpoints.append(&mut new_outpoints);
if new_outpoints.is_empty() {
- let (mut new_outpoints, new_outputs) = self.check_spend_holder_transaction(&tx, height, &logger);
- if !new_outputs.1.is_empty() {
- watch_outputs.push(new_outputs);
+ if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &logger) {
+ if !new_outputs.1.is_empty() {
+ watch_outputs.push(new_outputs);
+ }
+ claimable_outpoints.append(&mut new_outpoints);
+ balance_spendable_csv = Some(self.on_holder_tx_csv);
}
- claimable_outpoints.append(&mut new_outpoints);
}
- claimable_outpoints.append(&mut new_outpoints);
}
+ let txid = tx.txid();
+ self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
+ txid,
+ height: height,
+ event: OnchainEvent::FundingSpendConfirmation {
+ on_local_output_csv: balance_spendable_csv,
+ },
+ });
} else {
if let Some(&commitment_number) = self.counterparty_commitment_txn_on_chain.get(&prevout.txid) {
let (mut new_outpoints, new_outputs_option) = self.check_spend_counterparty_htlc(&tx, commitment_number, height, &logger);
.iter()
.filter_map(|entry| match &entry.event {
OnchainEvent::HTLCUpdate { source, .. } => Some(source),
- OnchainEvent::MaturingOutput { .. } => None,
+ _ => None,
})
.collect();
#[cfg(debug_assertions)]
// Produce actionable events from on-chain events having reached their threshold.
for entry in onchain_events_reaching_threshold_conf.drain(..) {
match entry.event {
- OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => {
+ OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis, input_idx } => {
// Check for duplicate HTLC resolutions.
#[cfg(debug_assertions)]
{
source: source.clone(),
onchain_value_satoshis,
}));
+ if let Some(idx) = input_idx {
+ self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx: idx, payment_preimage: None });
+ }
},
OnchainEvent::MaturingOutput { descriptor } => {
log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor));
self.pending_events.push(Event::SpendableOutputs {
outputs: vec![descriptor]
});
- }
+ },
+ OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } => {
+ self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx, payment_preimage: preimage });
+ },
+ OnchainEvent::FundingSpendConfirmation { .. } => {
+ self.funding_spend_confirmed = Some(entry.txid);
+ },
}
}
let revocation_sig_claim = (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && input.witness[1].len() == 33)
|| (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && input.witness[1].len() == 33);
let accepted_preimage_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::AcceptedHTLC);
- let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC);
+ let accepted_timeout_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim;
+ let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim;
+ let offered_timeout_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::OfferedHTLC);
+
+ let mut payment_preimage = PaymentPreimage([0; 32]);
+ if accepted_preimage_claim {
+ payment_preimage.0.copy_from_slice(&input.witness[3]);
+ } else if offered_preimage_claim {
+ payment_preimage.0.copy_from_slice(&input.witness[1]);
+ }
macro_rules! log_claim {
($tx_info: expr, $holder_tx: expr, $htlc: expr, $source_avail: expr) => {
- // We found the output in question, but aren't failing it backwards
- // as we have no corresponding source and no valid counterparty commitment txid
- // to try a weak source binding with same-hash, same-value still-valid offered HTLC.
- // This implies either it is an inbound HTLC or an outbound HTLC on a revoked transaction.
let outbound_htlc = $holder_tx == $htlc.offered;
+ // HTLCs must either be claimed by a matching script type or through the
+ // revocation path:
+ #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures"
+ debug_assert!(!$htlc.offered || offered_preimage_claim || offered_timeout_claim || revocation_sig_claim);
+ #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures"
+ debug_assert!($htlc.offered || accepted_preimage_claim || accepted_timeout_claim || revocation_sig_claim);
+ // Further, only exactly one of the possible spend paths should have been
+ // matched by any HTLC spend:
+ #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures"
+ debug_assert_eq!(accepted_preimage_claim as u8 + accepted_timeout_claim as u8 +
+ offered_preimage_claim as u8 + offered_timeout_claim as u8 +
+ revocation_sig_claim as u8, 1);
if ($holder_tx && revocation_sig_claim) ||
(outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) {
log_error!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!",
// resolve the source HTLC with the original sender.
payment_data = Some(((*source).clone(), htlc_output.payment_hash, htlc_output.amount_msat));
} else if !$holder_tx {
- check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output);
+ check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output);
if payment_data.is_none() {
check_htlc_valid_counterparty!(self.prev_counterparty_commitment_txid, htlc_output);
}
}
if payment_data.is_none() {
log_claim!($tx_info, $holder_tx, htlc_output, false);
+ let outbound_htlc = $holder_tx == htlc_output.offered;
+ if !outbound_htlc || revocation_sig_claim {
+ self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
+ txid: tx.txid(), height,
+ event: OnchainEvent::HTLCSpendConfirmation {
+ input_idx: input.previous_output.vout,
+ preimage: if accepted_preimage_claim || offered_preimage_claim {
+ Some(payment_preimage) } else { None },
+ // If this is a payment to us (!outbound_htlc, above),
+ // wait for the CSV delay before dropping the HTLC from
+ // claimable balance if the claim was an HTLC-Success
+ // transaction.
+ on_to_local_output_csv: if accepted_preimage_claim {
+ Some(self.on_holder_tx_csv) } else { None },
+ },
+ });
+ } else {
+ // Outbound claims should always have payment_data, unless
+ // we've already failed the HTLC as the commitment transaction
+ // which was broadcasted was revoked. In that case, we should
+ // spend the HTLC output here immediately, and expose that fact
+ // as a Balance, something which we do not yet do.
+ // TODO: Track the above as claimable!
+ }
continue 'outer_loop;
}
}
// Check that scan_commitment, above, decided there is some source worth relaying an
// HTLC resolution backwards to and figure out whether we learned a preimage from it.
if let Some((source, payment_hash, amount_msat)) = payment_data {
- let mut payment_preimage = PaymentPreimage([0; 32]);
if accepted_preimage_claim {
if !self.pending_monitor_events.iter().any(
|update| if let &MonitorEvent::HTLCEvent(ref upd) = update { upd.source == source } else { false }) {
- payment_preimage.0.copy_from_slice(&input.witness[3]);
+ self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
+ txid: tx.txid(),
+ height,
+ event: OnchainEvent::HTLCSpendConfirmation {
+ input_idx: input.previous_output.vout,
+ preimage: Some(payment_preimage),
+ on_to_local_output_csv: None,
+ },
+ });
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
source,
payment_preimage: Some(payment_preimage),
|update| if let &MonitorEvent::HTLCEvent(ref upd) = update {
upd.source == source
} else { false }) {
- payment_preimage.0.copy_from_slice(&input.witness[1]);
+ self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
+ txid: tx.txid(),
+ height,
+ event: OnchainEvent::HTLCSpendConfirmation {
+ input_idx: input.previous_output.vout,
+ preimage: Some(payment_preimage),
+ on_to_local_output_csv: None,
+ },
+ });
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
source,
payment_preimage: Some(payment_preimage),
event: OnchainEvent::HTLCUpdate {
source, payment_hash,
onchain_value_satoshis: Some(amount_msat / 1000),
+ input_idx: Some(input.previous_output.vout),
},
};
log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
let current_counterparty_commitment_txid = Readable::read(reader)?;
let prev_counterparty_commitment_txid = Readable::read(reader)?;
- let counterparty_tx_cache = Readable::read(reader)?;
+ let counterparty_commitment_params = Readable::read(reader)?;
let funding_redeemscript = Readable::read(reader)?;
let channel_value_satoshis = Readable::read(reader)?;
}
}
- let prev_holder_signed_commitment_tx = match <u8 as Readable>::read(reader)? {
- 0 => None,
- 1 => {
- Some(Readable::read(reader)?)
- },
- _ => return Err(DecodeError::InvalidValue),
- };
- let current_holder_commitment_tx = Readable::read(reader)?;
+ let mut prev_holder_signed_commitment_tx: Option<HolderSignedTx> =
+ match <u8 as Readable>::read(reader)? {
+ 0 => None,
+ 1 => {
+ Some(Readable::read(reader)?)
+ },
+ _ => return Err(DecodeError::InvalidValue),
+ };
+ let mut current_holder_commitment_tx: HolderSignedTx = Readable::read(reader)?;
let current_counterparty_commitment_number = <U48 as Readable>::read(reader)?.0;
let current_holder_commitment_number = <U48 as Readable>::read(reader)?.0;
return Err(DecodeError::InvalidValue);
}
}
- let onchain_tx_handler = ReadableArgs::read(reader, keys_manager)?;
+ let onchain_tx_handler: OnchainTxHandler<Signer> = ReadableArgs::read(reader, keys_manager)?;
let lockdown_from_offchain = Readable::read(reader)?;
let holder_tx_signed = Readable::read(reader)?;
- read_tlv_fields!(reader, {});
+ if let Some(prev_commitment_tx) = prev_holder_signed_commitment_tx.as_mut() {
+ let prev_holder_value = onchain_tx_handler.get_prev_holder_commitment_to_self_value();
+ if prev_holder_value.is_none() { return Err(DecodeError::InvalidValue); }
+ if prev_commitment_tx.to_self_value_sat == u64::max_value() {
+ prev_commitment_tx.to_self_value_sat = prev_holder_value.unwrap();
+ } else if prev_commitment_tx.to_self_value_sat != prev_holder_value.unwrap() {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+
+ let cur_holder_value = onchain_tx_handler.get_cur_holder_commitment_to_self_value();
+ if current_holder_commitment_tx.to_self_value_sat == u64::max_value() {
+ current_holder_commitment_tx.to_self_value_sat = cur_holder_value;
+ } else if current_holder_commitment_tx.to_self_value_sat != cur_holder_value {
+ return Err(DecodeError::InvalidValue);
+ }
+
+ let mut funding_spend_confirmed = None;
+ let mut htlcs_resolved_on_chain = Some(Vec::new());
+ read_tlv_fields!(reader, {
+ (1, funding_spend_confirmed, option),
+ (3, htlcs_resolved_on_chain, vec_type),
+ });
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&keys_manager.get_secure_random_bytes());
current_counterparty_commitment_txid,
prev_counterparty_commitment_txid,
- counterparty_tx_cache,
+ counterparty_commitment_params,
funding_redeemscript,
channel_value_satoshis,
their_cur_revocation_points,
lockdown_from_offchain,
holder_tx_signed,
+ funding_spend_confirmed,
+ htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(),
best_block,
use chain::transaction::OutPoint;
use ln::chan_utils;
-use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction};
+use ln::chan_utils::{HTLCOutputInCommitment, make_funding_redeemscript, ChannelPublicKeys, HolderCommitmentTransaction, ChannelTransactionParameters, CommitmentTransaction, ClosingTransaction};
use ln::msgs::UnsignedChannelAnnouncement;
use ln::script::ShutdownScript;
/// Note that the commitment number starts at (1 << 48) - 1 and counts backwards.
// TODO: return a Result so we can signal a validation error
fn release_commitment_secret(&self, idx: u64) -> [u8; 32];
+ /// Validate the counterparty's signatures on the holder commitment transaction and HTLCs.
+ ///
+ /// This is required in order for the signer to make sure that releasing a commitment
+ /// secret won't leave us without a broadcastable holder transaction.
+ /// Policy checks should be implemented in this function, including checking the amount
+ /// sent to us and checking the HTLCs.
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()>;
/// Gets the holder's channel public keys and basepoints
fn pubkeys(&self) -> &ChannelPublicKeys;
/// Gets an arbitrary identifier describing the set of keys which are provided back to you in
/// Create a signature for a counterparty's commitment transaction and associated HTLC transactions.
///
/// Note that if signing fails or is rejected, the channel will be force-closed.
+ ///
+ /// Policy checks should be implemented in this function, including checking the amount
+ /// sent to us and checking the HTLCs.
//
// TODO: Document the things someone using this interface should enforce before signing.
fn sign_counterparty_commitment(&self, commitment_tx: &CommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()>;
+ /// Validate the counterparty's revocation.
+ ///
+ /// This is required in order for the signer to make sure that the state has moved
+ /// forward and it is safe to sign the next counterparty commitment.
+ fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;
/// Create a signatures for a holder's commitment transaction and its claiming HTLC transactions.
/// This will only ever be called with a non-revoked commitment_tx. This will be called with the
///
/// Note that, due to rounding, there may be one "missing" satoshi, and either party may have
/// chosen to forgo their output as dust.
- fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()>;
/// Signs a channel announcement message with our funding key, proving it comes from one
/// of the channel participants.
chan_utils::build_commitment_secret(&self.commitment_seed, idx)
}
+ fn validate_holder_commitment(&self, _holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> {
+ Ok(())
+ }
+
fn pubkeys(&self) -> &ChannelPublicKeys { &self.holder_channel_pubkeys }
fn channel_keys_id(&self) -> [u8; 32] { self.channel_keys_id }
Ok((commitment_sig, htlc_sigs))
}
+ fn validate_counterparty_revocation(&self, _idx: u64, _secret: &SecretKey) -> Result<(), ()> {
+ Ok(())
+ }
+
fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
let funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
Err(())
}
- fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
- if closing_tx.input.len() != 1 { return Err(()); }
- if closing_tx.input[0].witness.len() != 0 { return Err(()); }
- if closing_tx.output.len() > 2 { return Err(()); }
-
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
let funding_pubkey = PublicKey::from_secret_key(secp_ctx, &self.funding_key);
let channel_funding_redeemscript = make_funding_redeemscript(&funding_pubkey, &self.counterparty_pubkeys().funding_pubkey);
-
- let sighash = hash_to_message!(&bip143::SigHashCache::new(closing_tx)
- .signature_hash(0, &channel_funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
- Ok(secp_ctx.sign(&sighash, &self.funding_key))
+ Ok(closing_tx.trust().sign(&self.funding_key, &channel_funding_redeemscript, self.channel_value_satoshis, secp_ctx))
}
fn sign_channel_announcement(&self, msg: &UnsignedChannelAnnouncement, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
}
}
+ pub(crate) fn get_prev_holder_commitment_to_self_value(&self) -> Option<u64> {
+ self.prev_holder_commitment.as_ref().map(|commitment| commitment.to_broadcaster_value_sat())
+ }
+
+ pub(crate) fn get_cur_holder_commitment_to_self_value(&self) -> u64 {
+ self.holder_commitment.to_broadcaster_value_sat()
+ }
+
/// Lightning security model (i.e being able to redeem/timeout HTLC or penalize coutnerparty onchain) lays on the assumption of claim transactions getting confirmed before timelock expiration
/// (CSV or CLTV following cases). In case of high-fee spikes, claim tx may stuck in the mempool, so you need to bump its feerate quickly using Replace-By-Fee or Child-Pay-For-Parent.
/// Panics if there are signing errors, because signing operations in reaction to on-chain events
}
}
-impl_writeable!(OutPoint, 0, { txid, index });
+impl_writeable!(OutPoint, { txid, index });
#[cfg(test)]
mod tests {
use ln::{PaymentHash, PaymentPreimage};
use ln::msgs::DecodeError;
use util::ser::{Readable, Writeable, Writer};
-use util::byte_utils;
+use util::{byte_utils, transaction_utils};
use bitcoin::hash_types::WPubkeyHash;
use bitcoin::secp256k1::key::{SecretKey, PublicKey};
use core::cmp;
use ln::chan_utils;
use util::transaction_utils::sort_outputs;
-use ln::channel::INITIAL_COMMITMENT_NUMBER;
+use ln::channel::{INITIAL_COMMITMENT_NUMBER, ANCHOR_OUTPUT_VALUE_SATOSHI};
use core::ops::Deref;
use chain;
res
}
+/// Build a closing transaction
+pub fn build_closing_transaction(to_holder_value_sat: u64, to_counterparty_value_sat: u64, to_holder_script: Script, to_counterparty_script: Script, funding_outpoint: OutPoint) -> Transaction {
+ let txins = {
+ let mut ins: Vec<TxIn> = Vec::new();
+ ins.push(TxIn {
+ previous_output: funding_outpoint,
+ script_sig: Script::new(),
+ sequence: 0xffffffff,
+ witness: Vec::new(),
+ });
+ ins
+ };
+
+ let mut txouts: Vec<(TxOut, ())> = Vec::new();
+
+ if to_counterparty_value_sat > 0 {
+ txouts.push((TxOut {
+ script_pubkey: to_counterparty_script,
+ value: to_counterparty_value_sat
+ }, ()));
+ }
+
+ if to_holder_value_sat > 0 {
+ txouts.push((TxOut {
+ script_pubkey: to_holder_script,
+ value: to_holder_value_sat
+ }, ()));
+ }
+
+ transaction_utils::sort_outputs(&mut txouts, |_, _| { cmp::Ordering::Equal }); // Ordering doesnt matter if they used our pubkey...
+
+ let mut outputs: Vec<TxOut> = Vec::new();
+ for out in txouts.drain(..) {
+ outputs.push(out.0);
+ }
+
+ Transaction {
+ version: 2,
+ lock_time: 0,
+ input: txins,
+ output: outputs,
+ }
+}
+
/// Implements the per-commitment secret storage scheme from
/// [BOLT 3](https://github.com/lightningnetwork/lightning-rfc/blob/dcbf8583976df087c79c3ce0b535311212e6812d/03-transactions.md#efficient-per-commitment-secret-storage).
///
}
}
+/// Gets the witnessScript for an anchor output from the funding public key.
+/// The witness in the spending input must be:
+/// <BIP 143 funding_signature>
+/// After 16 blocks of confirmation, an alternative satisfying witness could be:
+/// <>
+/// (empty vector required to satisfy compliance with MINIMALIF-standard rule)
+#[inline]
+pub(crate) fn get_anchor_redeemscript(funding_pubkey: &PublicKey) -> Script {
+ Builder::new().push_slice(&funding_pubkey.serialize()[..])
+ .push_opcode(opcodes::all::OP_CHECKSIG)
+ .push_opcode(opcodes::all::OP_IFDUP)
+ .push_opcode(opcodes::all::OP_NOTIF)
+ .push_int(16)
+ .push_opcode(opcodes::all::OP_CSV)
+ .push_opcode(opcodes::all::OP_ENDIF)
+ .into_script()
+}
+
/// Per-channel data used to build transactions in conjunction with the per-commitment data (CommitmentTransaction).
/// The fields are organized by holder/counterparty.
///
funding_outpoint: Some(chain::transaction::OutPoint { txid: Default::default(), index: 0 })
};
let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
- let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, keys, 0, &mut htlcs_with_aux, &channel_parameters.as_counterparty_broadcastable());
+ let inner = CommitmentTransaction::new_with_auxiliary_htlc_data(0, 0, 0, false, dummy_key.clone(), dummy_key.clone(), keys, 0, &mut htlcs_with_aux, &channel_parameters.as_counterparty_broadcastable());
HolderCommitmentTransaction {
inner,
counterparty_sig: dummy_sig,
}
}
-/// This class tracks the per-transaction information needed to build a commitment transaction and to
+/// This class tracks the per-transaction information needed to build a closing transaction and will
+/// actually build it and sign.
+///
+/// This class can be used inside a signer implementation to generate a signature given the relevant
+/// secret key.
+pub struct ClosingTransaction {
+ to_holder_value_sat: u64,
+ to_counterparty_value_sat: u64,
+ to_holder_script: Script,
+ to_counterparty_script: Script,
+ built: Transaction,
+}
+
+impl ClosingTransaction {
+ /// Construct an object of the class
+ pub fn new(
+ to_holder_value_sat: u64,
+ to_counterparty_value_sat: u64,
+ to_holder_script: Script,
+ to_counterparty_script: Script,
+ funding_outpoint: OutPoint,
+ ) -> Self {
+ let built = build_closing_transaction(
+ to_holder_value_sat, to_counterparty_value_sat,
+ to_holder_script.clone(), to_counterparty_script.clone(),
+ funding_outpoint
+ );
+ ClosingTransaction {
+ to_holder_value_sat,
+ to_counterparty_value_sat,
+ to_holder_script,
+ to_counterparty_script,
+ built
+ }
+ }
+
+ /// Trust our pre-built transaction.
+ ///
+ /// Applies a wrapper which allows access to the transaction.
+ ///
+ /// This should only be used if you fully trust the builder of this object. It should not
+ /// be used by an external signer - instead use the verify function.
+ pub fn trust(&self) -> TrustedClosingTransaction {
+ TrustedClosingTransaction { inner: self }
+ }
+
+ /// Verify our pre-built transaction.
+ ///
+ /// Applies a wrapper which allows access to the transaction.
+ ///
+ /// An external validating signer must call this method before signing
+ /// or using the built transaction.
+ pub fn verify(&self, funding_outpoint: OutPoint) -> Result<TrustedClosingTransaction, ()> {
+ let built = build_closing_transaction(
+ self.to_holder_value_sat, self.to_counterparty_value_sat,
+ self.to_holder_script.clone(), self.to_counterparty_script.clone(),
+ funding_outpoint
+ );
+ if self.built != built {
+ return Err(())
+ }
+ Ok(TrustedClosingTransaction { inner: self })
+ }
+
+ /// The value to be sent to the holder, or zero if the output will be omitted
+ pub fn to_holder_value_sat(&self) -> u64 {
+ self.to_holder_value_sat
+ }
+
+ /// The value to be sent to the counterparty, or zero if the output will be omitted
+ pub fn to_counterparty_value_sat(&self) -> u64 {
+ self.to_counterparty_value_sat
+ }
+
+ /// The destination of the holder's output
+ pub fn to_holder_script(&self) -> &Script {
+ &self.to_holder_script
+ }
+
+ /// The destination of the counterparty's output
+ pub fn to_counterparty_script(&self) -> &Script {
+ &self.to_counterparty_script
+ }
+}
+
+/// A wrapper on ClosingTransaction indicating that the built bitcoin
+/// transaction is trusted.
+///
+/// See trust() and verify() functions on CommitmentTransaction.
+///
+/// This structure implements Deref.
+pub struct TrustedClosingTransaction<'a> {
+ inner: &'a ClosingTransaction,
+}
+
+impl<'a> Deref for TrustedClosingTransaction<'a> {
+ type Target = ClosingTransaction;
+
+ fn deref(&self) -> &Self::Target { self.inner }
+}
+
+impl<'a> TrustedClosingTransaction<'a> {
+ /// The pre-built Bitcoin commitment transaction
+ pub fn built_transaction(&self) -> &Transaction {
+ &self.inner.built
+ }
+
+ /// Get the SIGHASH_ALL sighash value of the transaction.
+ ///
+ /// This can be used to verify a signature.
+ pub fn get_sighash_all(&self, funding_redeemscript: &Script, channel_value_satoshis: u64) -> Message {
+ let sighash = &bip143::SigHashCache::new(&self.inner.built).signature_hash(0, funding_redeemscript, channel_value_satoshis, SigHashType::All)[..];
+ hash_to_message!(sighash)
+ }
+
+ /// Sign a transaction, either because we are counter-signing the counterparty's transaction or
+ /// because we are about to broadcast a holder transaction.
+ pub fn sign<T: secp256k1::Signing>(&self, funding_key: &SecretKey, funding_redeemscript: &Script, channel_value_satoshis: u64, secp_ctx: &Secp256k1<T>) -> Signature {
+ let sighash = self.get_sighash_all(funding_redeemscript, channel_value_satoshis);
+ secp_ctx.sign(&sighash, funding_key)
+ }
+}
+
+/// This class tracks the per-transaction information needed to build a commitment transaction and will
/// actually build it and sign. It is used for holder transactions that we sign only when needed
/// and for transactions we sign for the counterparty.
///
to_countersignatory_value_sat: u64,
feerate_per_kw: u32,
htlcs: Vec<HTLCOutputInCommitment>,
+ // A boolean that is serialization backwards-compatible
+ opt_anchors: Option<()>,
// A cache of the parties' pubkeys required to construct the transaction, see doc for trust()
keys: TxCreationKeys,
// For access to the pre-built transaction, see doc for trust()
self.to_countersignatory_value_sat == o.to_countersignatory_value_sat &&
self.feerate_per_kw == o.feerate_per_kw &&
self.htlcs == o.htlcs &&
+ self.opt_anchors == o.opt_anchors &&
self.keys == o.keys;
if eq {
debug_assert_eq!(self.built.transaction, o.built.transaction);
(8, keys, required),
(10, built, required),
(12, htlcs, vec_type),
+ (14, opt_anchors, option),
});
impl CommitmentTransaction {
/// Only include HTLCs that are above the dust limit for the channel.
///
/// (C-not exported) due to the generic though we likely should expose a version without
- pub fn new_with_auxiliary_htlc_data<T>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
+ pub fn new_with_auxiliary_htlc_data<T>(commitment_number: u64, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, opt_anchors: bool, broadcaster_funding_key: PublicKey, countersignatory_funding_key: PublicKey, keys: TxCreationKeys, feerate_per_kw: u32, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> CommitmentTransaction {
// Sort outputs and populate output indices while keeping track of the auxiliary data
- let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters).unwrap();
+ let (outputs, htlcs) = Self::internal_build_outputs(&keys, to_broadcaster_value_sat, to_countersignatory_value_sat, htlcs_with_aux, channel_parameters, opt_anchors, &broadcaster_funding_key, &countersignatory_funding_key).unwrap();
let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(commitment_number, channel_parameters);
let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
to_countersignatory_value_sat,
feerate_per_kw,
htlcs,
+ opt_anchors: if opt_anchors { Some(()) } else { None },
keys,
built: BuiltCommitmentTransaction {
transaction,
}
}
- fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters) -> Result<BuiltCommitmentTransaction, ()> {
+ fn internal_rebuild_transaction(&self, keys: &TxCreationKeys, channel_parameters: &DirectedChannelTransactionParameters, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<BuiltCommitmentTransaction, ()> {
let (obscured_commitment_transaction_number, txins) = Self::internal_build_inputs(self.commitment_number, channel_parameters);
let mut htlcs_with_aux = self.htlcs.iter().map(|h| (h.clone(), ())).collect();
- let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters)?;
+ let (outputs, _) = Self::internal_build_outputs(keys, self.to_broadcaster_value_sat, self.to_countersignatory_value_sat, &mut htlcs_with_aux, channel_parameters, self.opt_anchors.is_some(), broadcaster_funding_key, countersignatory_funding_key)?;
let transaction = Self::make_transaction(obscured_commitment_transaction_number, txins, outputs);
let txid = transaction.txid();
// - initial sorting of outputs / HTLCs in the constructor, in which case T is auxiliary data the
// caller needs to have sorted together with the HTLCs so it can keep track of the output index
// - building of a bitcoin transaction during a verify() call, in which case T is just ()
- fn internal_build_outputs<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
+ fn internal_build_outputs<T>(keys: &TxCreationKeys, to_broadcaster_value_sat: u64, to_countersignatory_value_sat: u64, htlcs_with_aux: &mut Vec<(HTLCOutputInCommitment, T)>, channel_parameters: &DirectedChannelTransactionParameters, opt_anchors: bool, broadcaster_funding_key: &PublicKey, countersignatory_funding_key: &PublicKey) -> Result<(Vec<TxOut>, Vec<HTLCOutputInCommitment>), ()> {
let countersignatory_pubkeys = channel_parameters.countersignatory_pubkeys();
let contest_delay = channel_parameters.contest_delay();
));
}
+ if opt_anchors {
+ if to_broadcaster_value_sat > 0 || !htlcs_with_aux.is_empty() {
+ let anchor_script = get_anchor_redeemscript(broadcaster_funding_key);
+ txouts.push((
+ TxOut {
+ script_pubkey: anchor_script.to_v0_p2wsh(),
+ value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+ },
+ None,
+ ));
+ }
+
+ if to_countersignatory_value_sat > 0 || !htlcs_with_aux.is_empty() {
+ let anchor_script = get_anchor_redeemscript(countersignatory_funding_key);
+ txouts.push((
+ TxOut {
+ script_pubkey: anchor_script.to_v0_p2wsh(),
+ value: ANCHOR_OUTPUT_VALUE_SATOSHI,
+ },
+ None,
+ ));
+ }
+ }
+
let mut htlcs = Vec::with_capacity(htlcs_with_aux.len());
for (htlc, _) in htlcs_with_aux {
let script = chan_utils::get_htlc_redeemscript(&htlc, &keys);
/// Applies a wrapper which allows access to these fields.
///
/// This should only be used if you fully trust the builder of this object. It should not
- /// be used by an external signer - instead use the verify function.
+ /// be used by an external signer - instead use the verify function.
pub fn trust(&self) -> TrustedCommitmentTransaction {
TrustedCommitmentTransaction { inner: self }
}
if keys != self.keys {
return Err(());
}
- let tx = self.internal_rebuild_transaction(&keys, channel_parameters)?;
+ let tx = self.internal_rebuild_transaction(&keys, channel_parameters, &broadcaster_keys.funding_pubkey, &countersignatory_keys.funding_pubkey)?;
if self.built.transaction != tx.transaction || self.built.txid != tx.txid {
return Err(());
}
#[cfg(test)]
mod tests {
use super::CounterpartyCommitmentSecrets;
- use hex;
+ use ::{hex, chain};
use prelude::*;
+ use ln::chan_utils::{CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
+ use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
+ use util::test_utils;
+ use chain::keysinterface::{KeysInterface, BaseSign};
+ use bitcoin::Network;
+ use ln::PaymentHash;
+
+ #[test]
+ fn test_anchors() {
+ let secp_ctx = Secp256k1::new();
+
+ let seed = [42; 32];
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
+ let signer = keys_provider.get_channel_signer(false, 3000);
+ let counterparty_signer = keys_provider.get_channel_signer(false, 3000);
+ let delayed_payment_base = &signer.pubkeys().delayed_payment_basepoint;
+ let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
+ let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
+ let htlc_basepoint = &signer.pubkeys().htlc_basepoint;
+ let holder_pubkeys = signer.pubkeys();
+ let counterparty_pubkeys = counterparty_signer.pubkeys();
+ let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint).unwrap();
+ let channel_parameters = ChannelTransactionParameters {
+ holder_pubkeys: holder_pubkeys.clone(),
+ holder_selected_contest_delay: 0,
+ is_outbound_from_holder: false,
+ counterparty_parameters: Some(CounterpartyChannelTransactionParameters { pubkeys: counterparty_pubkeys.clone(), selected_contest_delay: 0 }),
+ funding_outpoint: Some(chain::transaction::OutPoint { txid: Default::default(), index: 0 })
+ };
+
+ let mut htlcs_with_aux: Vec<(_, ())> = Vec::new();
+
+ // Generate broadcaster and counterparty outputs
+ let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
+ 0, 1000, 2000,
+ false,
+ holder_pubkeys.funding_pubkey,
+ counterparty_pubkeys.funding_pubkey,
+ keys.clone(), 1,
+ &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
+ );
+ assert_eq!(tx.built.transaction.output.len(), 2);
+
+ // Generate broadcaster and counterparty outputs as well as two anchors
+ let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
+ 0, 1000, 2000,
+ true,
+ holder_pubkeys.funding_pubkey,
+ counterparty_pubkeys.funding_pubkey,
+ keys.clone(), 1,
+ &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
+ );
+ assert_eq!(tx.built.transaction.output.len(), 4);
+
+ // Generate broadcaster output and anchor
+ let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
+ 0, 3000, 0,
+ true,
+ holder_pubkeys.funding_pubkey,
+ counterparty_pubkeys.funding_pubkey,
+ keys.clone(), 1,
+ &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
+ );
+ assert_eq!(tx.built.transaction.output.len(), 2);
+
+ // Generate counterparty output and anchor
+ let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
+ 0, 0, 3000,
+ true,
+ holder_pubkeys.funding_pubkey,
+ counterparty_pubkeys.funding_pubkey,
+ keys.clone(), 1,
+ &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
+ );
+ assert_eq!(tx.built.transaction.output.len(), 2);
+
+ // Generate broadcaster output, an HTLC output and two anchors
+ let payment_hash = PaymentHash([42; 32]);
+ let htlc_info = HTLCOutputInCommitment {
+ offered: false,
+ amount_msat: 1000000,
+ cltv_expiry: 100,
+ payment_hash,
+ transaction_output_index: None,
+ };
+
+ let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
+ 0, 3000, 0,
+ true,
+ holder_pubkeys.funding_pubkey,
+ counterparty_pubkeys.funding_pubkey,
+ keys.clone(), 1,
+ &mut vec![(htlc_info, ())], &channel_parameters.as_holder_broadcastable()
+ );
+ assert_eq!(tx.built.transaction.output.len(), 4);
+ }
#[test]
fn test_per_commitment_storage() {
false => *nodes[0].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::PermanentFailure))
}
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)), true, APIError::ChannelUnavailable {..}, {});
check_added_monitors!(nodes[0], 2);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)), false, APIError::MonitorUpdateFailed, {});
check_added_monitors!(nodes[0], 1);
}
false => *nodes[0].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure))
}
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)), false, APIError::MonitorUpdateFailed, {});
check_added_monitors!(nodes[0], 1);
}
{
*nodes[0].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)), false, APIError::MonitorUpdateFailed, {});
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage_1, our_payment_hash, payment_secret_1) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(payment_secret_1)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage_1, our_payment_hash_1, our_payment_secret_1) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash_1, &Some(our_payment_secret_1)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage_2, our_payment_hash_2, our_payment_secret_2) = get_payment_preimage_hash!(nodes[0]);
{
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, our_payment_hash_2, &Some(our_payment_secret_2)).unwrap();
check_added_monitors!(nodes[1], 1);
}
let (payment_preimage_2, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[2]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (_, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[2]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_3, &Some(payment_secret_3)).unwrap();
check_added_monitors!(nodes[0], 1);
}
// Try to route another payment backwards from 2 to make sure 1 holds off on responding
let (payment_preimage_4, payment_hash_4, payment_secret_4) = get_payment_preimage_hash!(nodes[0]);
let net_graph_msg_handler = &nodes[2].net_graph_msg_handler;
- let route = get_route(&nodes[2].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[2].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[2].node.send_payment(&route, payment_hash_4, &Some(payment_secret_4)).unwrap();
check_added_monitors!(nodes[2], 1);
// generation during RAA while in monitor-update-failed state.
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)).unwrap();
check_added_monitors!(nodes[0], 1);
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
// commitment transaction states) whereas here we can explicitly check for it.
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_3, &Some(payment_secret_3)).unwrap();
check_added_monitors!(nodes[0], 0);
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
let (payment_preimage_2, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage_1, payment_hash_1, payment_secret_1) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage_1, payment_hash_1, payment_secret_1) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (payment_preimage_2, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let route;
{
let net_graph_msg_handler = &nodes[2].net_graph_msg_handler;
- route = get_route(&nodes[2].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1_000_000, TEST_FINAL_CLTV, &logger).unwrap();
+ route = get_route(&nodes[2].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1_000_000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[2].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[2], 1);
}
let (payment_preimage_2, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[0]);
{
let net_graph_msg_handler = &nodes[2].net_graph_msg_handler;
- let route = get_route(&nodes[2].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[2].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[2].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[2], 1);
}
let (payment_preimage_2, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let logger = test_utils::TestLogger::new();
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[3]);
- let mut route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let mut route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
// Set us up to take multiple routes, one 0 -> 1 -> 3 and one 0 -> 2 -> 3:
let path = route.paths[0].clone();
send_payment(&nodes[0], &[&nodes[1]], 100_000_00);
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[0]);
- let route = get_route(&nodes[1].node.get_our_node_id(), &nodes[1].net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[1].node.get_our_node_id(), &nodes[1].net_graph_msg_handler.network_graph,
&nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1_000_000, TEST_FINAL_CLTV, nodes[1].logger).unwrap();
nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
check_added_monitors!(nodes[1], 1);
let route = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), None, None, &Vec::new(), 100000, TEST_FINAL_CLTV, nodes[0].logger).unwrap()
+ get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), None, None, &Vec::new(), 100000, TEST_FINAL_CLTV, nodes[0].logger).unwrap()
};
nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)).unwrap();
// In order to get the HTLC claim into the holding cell at nodes[1], we need nodes[1] to be
// awaiting a remote revoke_and_ack from nodes[0].
let (_, second_payment_hash, second_payment_secret) = get_payment_preimage_hash!(nodes[1]);
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph,
&nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, nodes[1].logger).unwrap();
nodes[0].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 2);
}
+
+#[test]
+fn double_temp_error() {
+ // Test that it's OK to have multiple `ChainMonitor::update_channel` calls fail in a row.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, channel_id, _) = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
+
+ let (payment_preimage_1, _, _) = route_payment(&nodes[0], &[&nodes[1]], 1000000);
+ let (payment_preimage_2, _, _) = route_payment(&nodes[0], &[&nodes[1]], 1000000);
+
+ *nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
+ // `claim_funds` results in a ChannelMonitorUpdate.
+ assert!(nodes[1].node.claim_funds(payment_preimage_1));
+ check_added_monitors!(nodes[1], 1);
+ let (funding_tx, latest_update_1) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+
+ *nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Err(ChannelMonitorUpdateErr::TemporaryFailure));
+ // Previously, this would've panicked due to a double-call to `Channel::monitor_update_failed`,
+ // which had some asserts that prevented it from being called twice.
+ assert!(nodes[1].node.claim_funds(payment_preimage_2));
+ check_added_monitors!(nodes[1], 1);
+ *nodes[1].chain_monitor.update_ret.lock().unwrap() = Some(Ok(()));
+
+ let (_, latest_update_2) = nodes[1].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
+ nodes[1].node.channel_monitor_updated(&funding_tx, latest_update_1);
+ assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
+ check_added_monitors!(nodes[1], 0);
+ nodes[1].node.channel_monitor_updated(&funding_tx, latest_update_2);
+
+ // Complete the first HTLC.
+ let events = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 1);
+ let (update_fulfill_1, commitment_signed_b1, node_id) = {
+ match &events[0] {
+ &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, ref update_fee, ref commitment_signed } } => {
+ assert!(update_add_htlcs.is_empty());
+ assert_eq!(update_fulfill_htlcs.len(), 1);
+ assert!(update_fail_htlcs.is_empty());
+ assert!(update_fail_malformed_htlcs.is_empty());
+ assert!(update_fee.is_none());
+ (update_fulfill_htlcs[0].clone(), commitment_signed.clone(), node_id.clone())
+ },
+ _ => panic!("Unexpected event"),
+ }
+ };
+ assert_eq!(node_id, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &update_fulfill_1);
+ check_added_monitors!(nodes[0], 0);
+ expect_payment_sent!(nodes[0], payment_preimage_1);
+ nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &commitment_signed_b1);
+ check_added_monitors!(nodes[0], 1);
+ nodes[0].node.process_pending_htlc_forwards();
+ let (raa_a1, commitment_signed_a1) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ check_added_monitors!(nodes[1], 0);
+ assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
+ nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &raa_a1);
+ check_added_monitors!(nodes[1], 1);
+ nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &commitment_signed_a1);
+ check_added_monitors!(nodes[1], 1);
+
+ // Complete the second HTLC.
+ let ((update_fulfill_2, commitment_signed_b2), raa_b2) = {
+ let events = nodes[1].node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), 2);
+ (match &events[0] {
+ MessageSendEvent::UpdateHTLCs { node_id, updates } => {
+ assert_eq!(*node_id, nodes[0].node.get_our_node_id());
+ assert!(updates.update_add_htlcs.is_empty());
+ assert!(updates.update_fail_htlcs.is_empty());
+ assert!(updates.update_fail_malformed_htlcs.is_empty());
+ assert!(updates.update_fee.is_none());
+ assert_eq!(updates.update_fulfill_htlcs.len(), 1);
+ (updates.update_fulfill_htlcs[0].clone(), updates.commitment_signed.clone())
+ },
+ _ => panic!("Unexpected event"),
+ },
+ match events[1] {
+ MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
+ assert_eq!(*node_id, nodes[0].node.get_our_node_id());
+ (*msg).clone()
+ },
+ _ => panic!("Unexpected event"),
+ })
+ };
+ nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &raa_b2);
+ check_added_monitors!(nodes[0], 1);
+
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &update_fulfill_2);
+ check_added_monitors!(nodes[0], 0);
+ assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
+ commitment_signed_dance!(nodes[0], nodes[1], commitment_signed_b2, false);
+ expect_payment_sent!(nodes[0], payment_preimage_2);
+}
// licenses.
use bitcoin::blockdata::script::{Script,Builder};
-use bitcoin::blockdata::transaction::{TxIn, TxOut, Transaction, SigHashType};
+use bitcoin::blockdata::transaction::{Transaction, SigHashType};
use bitcoin::util::bip143;
use bitcoin::consensus::encode;
use ln::msgs;
use ln::msgs::{DecodeError, OptionalField, DataLossProtect};
use ln::script::ShutdownScript;
-use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
-use ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor};
+use ln::channelmanager::{CounterpartyForwardingInfo, PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
+use ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor, ClosingTransaction};
use ln::chan_utils;
use chain::BestBlock;
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER};
use chain::transaction::{OutPoint, TransactionData};
use chain::keysinterface::{Sign, KeysInterface};
-use util::transaction_utils;
use util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
use util::logger::Logger;
use util::errors::APIError;
}
}
-/// Information needed for constructing an invoice route hint for this channel.
-#[derive(Clone, Debug, PartialEq)]
-pub struct CounterpartyForwardingInfo {
- /// Base routing fee in millisatoshis.
- pub fee_base_msat: u32,
- /// Amount in millionths of a satoshi the channel will charge per transferred satoshi.
- pub fee_proportional_millionths: u32,
- /// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart,
- /// such that the outgoing HTLC is forwardable to this counterparty. See `msgs::ChannelUpdate`'s
- /// `cltv_expiry_delta` for more details.
- pub cltv_expiry_delta: u16,
-}
-
/// A return value enum for get_update_fulfill_htlc. See UpdateFulfillCommitFetch variants for
/// description
enum UpdateFulfillFetch {
monitor_pending_forwards: Vec<(PendingHTLCInfo, u64)>,
monitor_pending_failures: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>,
- // pending_update_fee is filled when sending and receiving update_fee
- // For outbound channel, feerate_per_kw is updated with the value from
- // pending_update_fee when revoke_and_ack is received
+ // pending_update_fee is filled when sending and receiving update_fee.
//
- // For inbound channel, feerate_per_kw is updated when it receives
- // commitment_signed and revoke_and_ack is generated
- // The pending value is kept when another pair of update_fee and commitment_signed
- // is received during AwaitingRemoteRevoke and relieved when the expected
- // revoke_and_ack is received and new commitment_signed is generated to be
- // sent to the funder. Otherwise, the pending value is removed when receiving
- // commitment_signed.
+ // Because it follows the same commitment flow as HTLCs, `FeeUpdateState` is either `Outbound`
+ // or matches a subset of the `InboundHTLCOutput` variants. It is then updated/used when
+ // generating new commitment transactions with exactly the same criteria as inbound/outbound
+ // HTLCs with similar state.
pending_update_fee: Option<(u32, FeeUpdateState)>,
- // update_fee() during ChannelState::AwaitingRemoteRevoke is hold in
- // holdina_cell_update_fee then moved to pending_udpate_fee when revoke_and_ack
- // is received. holding_cell_update_fee is updated when there are additional
- // update_fee() during ChannelState::AwaitingRemoteRevoke.
+ // If a `send_update_fee()` call is made with ChannelState::AwaitingRemoteRevoke set, we place
+ // it here instead of `pending_update_fee` in the same way as we place outbound HTLC updates in
+ // `holding_cell_htlc_updates` instead of `pending_outbound_htlcs`. It is released into
+ // `pending_update_fee` with the same criteria as outbound HTLC updates but can be updated by
+ // further `send_update_fee` calls, dropping the previous holding cell update entirely.
holding_cell_update_fee: Option<u32>,
next_holder_htlc_id: u64,
next_counterparty_htlc_id: u64,
/// closing_signed message and handling it in `maybe_propose_closing_signed`.
pending_counterparty_closing_signed: Option<msgs::ClosingSigned>,
- /// The minimum and maximum absolute fee we are willing to place on the closing transaction.
- /// These are set once we reach `closing_negotiation_ready`.
+ /// The minimum and maximum absolute fee, in satoshis, we are willing to place on the closing
+ /// transaction. These are set once we reach `closing_negotiation_ready`.
#[cfg(test)]
pub(crate) closing_fee_limits: Option<(u64, u64)>,
#[cfg(not(test))]
#[cfg(test)]
pub const COMMITMENT_TX_WEIGHT_PER_HTLC: u64 = 172;
-/// Maximmum `funding_satoshis` value, according to the BOLT #2 specification
+pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
+
+/// Maximum `funding_satoshis` value, according to the BOLT #2 specification
/// it's 2^24.
pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
let mut value_to_a = if local { value_to_self } else { value_to_remote };
let mut value_to_b = if local { value_to_remote } else { value_to_self };
+ let (funding_pubkey_a, funding_pubkey_b) = if local {
+ (self.get_holder_pubkeys().funding_pubkey, self.get_counterparty_pubkeys().funding_pubkey)
+ } else {
+ (self.get_counterparty_pubkeys().funding_pubkey, self.get_holder_pubkeys().funding_pubkey)
+ };
if value_to_a >= (broadcaster_dust_limit_satoshis as i64) {
log_trace!(logger, " ...including {} output with value {}", if local { "to_local" } else { "to_remote" }, value_to_a);
let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(commitment_number,
value_to_a as u64,
value_to_b as u64,
+ false,
+ funding_pubkey_a,
+ funding_pubkey_b,
keys.clone(),
feerate_per_kw,
&mut included_non_dust_htlcs,
}
#[inline]
- fn build_closing_transaction(&self, proposed_total_fee_satoshis: u64, skip_remote_output: bool) -> (Transaction, u64) {
- let txins = {
- let mut ins: Vec<TxIn> = Vec::new();
- ins.push(TxIn {
- previous_output: self.funding_outpoint().into_bitcoin_outpoint(),
- script_sig: Script::new(),
- sequence: 0xffffffff,
- witness: Vec::new(),
- });
- ins
- };
-
+ fn build_closing_transaction(&self, proposed_total_fee_satoshis: u64, skip_remote_output: bool) -> (ClosingTransaction, u64) {
assert!(self.pending_inbound_htlcs.is_empty());
assert!(self.pending_outbound_htlcs.is_empty());
assert!(self.pending_update_fee.is_none());
- let mut txouts: Vec<(TxOut, ())> = Vec::new();
let mut total_fee_satoshis = proposed_total_fee_satoshis;
- let value_to_self: i64 = (self.value_to_self_msat as i64) / 1000 - if self.is_outbound() { total_fee_satoshis as i64 } else { 0 };
- let value_to_remote: i64 = ((self.channel_value_satoshis * 1000 - self.value_to_self_msat) as i64 / 1000) - if self.is_outbound() { 0 } else { total_fee_satoshis as i64 };
+ let mut value_to_holder: i64 = (self.value_to_self_msat as i64) / 1000 - if self.is_outbound() { total_fee_satoshis as i64 } else { 0 };
+ let mut value_to_counterparty: i64 = ((self.channel_value_satoshis * 1000 - self.value_to_self_msat) as i64 / 1000) - if self.is_outbound() { 0 } else { total_fee_satoshis as i64 };
- if value_to_self < 0 {
+ if value_to_holder < 0 {
assert!(self.is_outbound());
- total_fee_satoshis += (-value_to_self) as u64;
- } else if value_to_remote < 0 {
+ total_fee_satoshis += (-value_to_holder) as u64;
+ } else if value_to_counterparty < 0 {
assert!(!self.is_outbound());
- total_fee_satoshis += (-value_to_remote) as u64;
+ total_fee_satoshis += (-value_to_counterparty) as u64;
}
- if !skip_remote_output && value_to_remote as u64 > self.holder_dust_limit_satoshis {
- txouts.push((TxOut {
- script_pubkey: self.counterparty_shutdown_scriptpubkey.clone().unwrap(),
- value: value_to_remote as u64
- }, ()));
+ if skip_remote_output || value_to_counterparty as u64 <= self.holder_dust_limit_satoshis {
+ value_to_counterparty = 0;
}
- assert!(self.shutdown_scriptpubkey.is_some());
- if value_to_self as u64 > self.holder_dust_limit_satoshis {
- txouts.push((TxOut {
- script_pubkey: self.get_closing_scriptpubkey(),
- value: value_to_self as u64
- }, ()));
+ if value_to_holder as u64 <= self.holder_dust_limit_satoshis {
+ value_to_holder = 0;
}
- transaction_utils::sort_outputs(&mut txouts, |_, _| { cmp::Ordering::Equal }); // Ordering doesnt matter if they used our pubkey...
-
- let mut outputs: Vec<TxOut> = Vec::new();
- for out in txouts.drain(..) {
- outputs.push(out.0);
- }
+ assert!(self.shutdown_scriptpubkey.is_some());
+ let holder_shutdown_script = self.get_closing_scriptpubkey();
+ let counterparty_shutdown_script = self.counterparty_shutdown_scriptpubkey.clone().unwrap();
+ let funding_outpoint = self.funding_outpoint().into_bitcoin_outpoint();
- (Transaction {
- version: 2,
- lock_time: 0,
- input: txins,
- output: outputs,
- }, total_fee_satoshis)
+ let closing_transaction = ClosingTransaction::new(value_to_holder as u64, value_to_counterparty as u64, holder_shutdown_script, counterparty_shutdown_script, funding_outpoint);
+ (closing_transaction, total_fee_satoshis)
}
fn funding_outpoint(&self) -> OutPoint {
self.counterparty_funding_pubkey()
);
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ .map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
+
// Now that we're past error-generating stuff, update our local state:
let funding_redeemscript = self.get_funding_redeemscript();
self.counterparty_funding_pubkey()
);
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ .map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
+
let funding_redeemscript = self.get_funding_redeemscript();
let funding_txo = self.get_funding_txo().unwrap();
// We can't accept HTLCs sent after we've sent a shutdown.
let local_sent_shutdown = (self.channel_state & (ChannelState::ChannelFunded as u32 | ChannelState::LocalShutdownSent as u32)) != (ChannelState::ChannelFunded as u32);
if local_sent_shutdown {
- pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x1000|20);
+ pending_forward_status = create_pending_htlc_status(self, pending_forward_status, 0x4000|8);
}
// If the remote has sent a shutdown prior to adding this HTLC, then they are in violation of the spec.
let remote_sent_shutdown = (self.channel_state & (ChannelState::ChannelFunded as u32 | ChannelState::RemoteShutdownSent as u32)) != (ChannelState::ChannelFunded as u32);
);
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx);
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ .map_err(|_| (None, ChannelError::Close("Failed to validate our commitment".to_owned())))?;
let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1);
// Update state now that we've passed all the can-fail calls...
return Err(ChannelError::Close("Peer sent revoke_and_ack after we'd started exchanging closing_signeds".to_owned()));
}
+ let secret = secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret".to_owned());
+
if let Some(counterparty_prev_commitment_point) = self.counterparty_prev_commitment_point {
- if PublicKey::from_secret_key(&self.secp_ctx, &secp_check!(SecretKey::from_slice(&msg.per_commitment_secret), "Peer provided an invalid per_commitment_secret".to_owned())) != counterparty_prev_commitment_point {
+ if PublicKey::from_secret_key(&self.secp_ctx, &secret) != counterparty_prev_commitment_point {
return Err(ChannelError::Close("Got a revoke commitment secret which didn't correspond to their current pubkey".to_owned()));
}
}
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
}
+ self.holder_signer.validate_counterparty_revocation(
+ self.cur_counterparty_commitment_transaction_number + 1,
+ &secret
+ ).map_err(|_| ChannelError::Close("Failed to validate revocation from peer".to_owned()))?;
+
self.commitment_secrets.provide_secret(self.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret)
.map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?;
self.latest_monitor_update_id += 1;
/// monitor update failure must *not* have been sent to the remote end, and must instead
/// have been dropped. They will be regenerated when monitor_updating_restored is called.
pub fn monitor_update_failed(&mut self, resend_raa: bool, resend_commitment: bool, mut pending_forwards: Vec<(PendingHTLCInfo, u64)>, mut pending_fails: Vec<(HTLCSource, PaymentHash, HTLCFailReason)>) {
- assert_eq!(self.channel_state & ChannelState::MonitorUpdateFailed as u32, 0);
- self.monitor_pending_revoke_and_ack = resend_raa;
- self.monitor_pending_commitment_signed = resend_commitment;
- assert!(self.monitor_pending_forwards.is_empty());
- mem::swap(&mut pending_forwards, &mut self.monitor_pending_forwards);
- assert!(self.monitor_pending_failures.is_empty());
- mem::swap(&mut pending_fails, &mut self.monitor_pending_failures);
+ self.monitor_pending_revoke_and_ack |= resend_raa;
+ self.monitor_pending_commitment_signed |= resend_commitment;
+ self.monitor_pending_forwards.append(&mut pending_forwards);
+ self.monitor_pending_failures.append(&mut pending_fails);
self.channel_state |= ChannelState::MonitorUpdateFailed as u32;
}
cmp::max(normal_feerate as u64 * tx_weight / 1000 + self.config.force_close_avoidance_max_fee_satoshis,
proposed_max_feerate as u64 * tx_weight / 1000)
} else {
- u64::max_value()
+ self.channel_value_satoshis - (self.value_to_self_msat + 999) / 1000
};
self.closing_fee_limits = Some((proposed_total_fee_satoshis, proposed_max_total_fee_satoshis));
Ok((shutdown, monitor_update, dropped_outbound_htlcs))
}
- fn build_signed_closing_transaction(&self, tx: &mut Transaction, counterparty_sig: &Signature, sig: &Signature) {
- if tx.input.len() != 1 { panic!("Tried to sign closing transaction that had input count != 1!"); }
- if tx.input[0].witness.len() != 0 { panic!("Tried to re-sign closing transaction"); }
- if tx.output.len() > 2 { panic!("Tried to sign bogus closing transaction"); }
+ fn build_signed_closing_transaction(&self, closing_tx: &ClosingTransaction, counterparty_sig: &Signature, sig: &Signature) -> Transaction {
+ let mut tx = closing_tx.trust().built_transaction().clone();
tx.input[0].witness.push(Vec::new()); // First is the multisig dummy
tx.input[0].witness[2].push(SigHashType::All as u8);
tx.input[0].witness.push(self.get_funding_redeemscript().into_bytes());
+ tx
}
pub fn closing_signed<F: Deref>(&mut self, fee_estimator: &F, msg: &msgs::ClosingSigned) -> Result<(Option<msgs::ClosingSigned>, Option<Transaction>), ChannelError>
if used_total_fee != msg.fee_satoshis {
return Err(ChannelError::Close(format!("Remote sent us a closing_signed with a fee other than the value they can claim. Fee in message: {}. Actual closing tx fee: {}", msg.fee_satoshis, used_total_fee)));
}
- let mut sighash = hash_to_message!(&bip143::SigHashCache::new(&closing_tx).signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
+ let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.channel_value_satoshis);
match self.secp_ctx.verify(&sighash, &msg.signature, &self.get_counterparty_pubkeys().funding_pubkey) {
Ok(_) => {},
// The remote end may have decided to revoke their output due to inconsistent dust
// limits, so check for that case by re-checking the signature here.
closing_tx = self.build_closing_transaction(msg.fee_satoshis, true).0;
- sighash = hash_to_message!(&bip143::SigHashCache::new(&closing_tx).signature_hash(0, &funding_redeemscript, self.channel_value_satoshis, SigHashType::All)[..]);
+ let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.channel_value_satoshis);
secp_check!(self.secp_ctx.verify(&sighash, &msg.signature, self.counterparty_funding_pubkey()), "Invalid closing tx signature from peer".to_owned());
},
};
assert!(self.shutdown_scriptpubkey.is_some());
if let Some((last_fee, sig)) = self.last_sent_closing_fee {
if last_fee == msg.fee_satoshis {
- self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
+ let tx = self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig);
self.channel_state = ChannelState::ShutdownComplete as u32;
self.update_time_counter += 1;
- return Ok((None, Some(closing_tx)));
+ return Ok((None, Some(tx)));
}
}
macro_rules! propose_fee {
($new_fee: expr) => {
- let (mut tx, used_fee) = if $new_fee == msg.fee_satoshis {
+ let (closing_tx, used_fee) = if $new_fee == msg.fee_satoshis {
(closing_tx, $new_fee)
} else {
self.build_closing_transaction($new_fee, false)
};
let sig = self.holder_signer
- .sign_closing_transaction(&tx, &self.secp_ctx)
+ .sign_closing_transaction(&closing_tx, &self.secp_ctx)
.map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?;
let signed_tx = if $new_fee == msg.fee_satoshis {
self.channel_state = ChannelState::ShutdownComplete as u32;
self.update_time_counter += 1;
- self.build_signed_closing_transaction(&mut tx, &msg.signature, &sig);
+ let tx = self.build_signed_closing_transaction(&closing_tx, &msg.signature, &sig);
Some(tx)
} else { None };
if !self.is_outbound() {
// They have to pay, so pick the highest fee in the overlapping range.
- debug_assert_eq!(our_max_fee, u64::max_value()); // We should never set an upper bound
+ // We should never set an upper bound aside from their full balance
+ debug_assert_eq!(our_max_fee, self.channel_value_satoshis - (self.value_to_self_msat + 999) / 1000);
propose_fee!(cmp::min(max_fee_satoshis, our_max_fee));
} else {
if msg.fee_satoshis < our_min_fee || msg.fee_satoshis > our_max_fee {
// more dust balance if the feerate increases when we have several HTLCs pending
// which are near the dust limit.
let mut feerate_per_kw = self.feerate_per_kw;
+ // If there's a pending update fee, use it to ensure we aren't under-estimating
+ // potential feerate updates coming soon.
if let Some((feerate, _)) = self.pending_update_fee {
feerate_per_kw = cmp::max(feerate_per_kw, feerate);
}
if self.is_outbound() {
self.pending_update_fee.map(|(a, _)| a).write(writer)?;
} else if let Some((feerate, FeeUpdateState::AwaitingRemoteRevokeToAnnounce)) = self.pending_update_fee {
- // As for inbound HTLCs, if the update was only announced and never committed, drop it.
Some(feerate).write(writer)?;
} else {
+ // As for inbound HTLCs, if the update was only announced and never committed in a
+ // commitment_signed, drop it.
None::<u32>.write(writer)?;
}
self.holding_cell_update_fee.write(writer)?;
use bitcoin::hashes::hex::FromHex;
use hex;
use ln::{PaymentPreimage, PaymentHash};
- use ln::channelmanager::HTLCSource;
+ use ln::channelmanager::{HTLCSource, MppId};
use ln::channel::{Channel,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,HTLCCandidate,HTLCInitiator,TxCreationKeys};
use ln::channel::MAX_FUNDING_SATOSHIS;
use ln::features::InitFeatures;
path: Vec::new(),
session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
first_hop_htlc_msat: 548,
+ mpp_id: MppId([42; 32]),
}
});
// Since this struct is returned in `list_channels` methods, expose it here in case users want to
// construct one themselves.
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
-pub use ln::channel::CounterpartyForwardingInfo;
use ln::channel::{Channel, ChannelError, ChannelUpdateStatus, UpdateFulfillCommitFetch};
use ln::features::{InitFeatures, NodeFeatures};
use routing::router::{Route, RouteHop};
use util::config::UserConfig;
use util::events::{EventHandler, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
use util::{byte_utils, events};
-use util::ser::{Readable, ReadableArgs, MaybeReadable, Writeable, Writer};
+use util::ser::{BigSize, FixedLengthReader, Readable, ReadableArgs, MaybeReadable, Writeable, Writer};
use util::chacha20::{ChaCha20, ChaChaReader};
use util::logger::{Logger, Level};
use util::errors::APIError;
onion_payload: OnionPayload,
}
+/// A payment identifier used to correlate an MPP payment's per-path HTLC sources internally.
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+pub(crate) struct MppId(pub [u8; 32]);
+
+impl Writeable for MppId {
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ self.0.write(w)
+ }
+}
+
+impl Readable for MppId {
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let buf: [u8; 32] = Readable::read(r)?;
+ Ok(MppId(buf))
+ }
+}
/// Tracks the inbound corresponding to an outbound HTLC
#[derive(Clone, PartialEq)]
pub(crate) enum HTLCSource {
/// Technically we can recalculate this from the route, but we cache it here to avoid
/// doing a double-pass on route when we get a failure back
first_hop_htlc_msat: u64,
+ mpp_id: MppId,
},
}
#[cfg(test)]
path: Vec::new(),
session_priv: SecretKey::from_slice(&[1; 32]).unwrap(),
first_hop_htlc_msat: 0,
+ mpp_id: MppId([2; 32]),
}
}
}
/// which may generate a claim event, we may receive similar duplicate claim/fail MonitorEvents
/// after reloading from disk while replaying blocks against ChannelMonitors.
///
+ /// Each payment has each of its MPP part's session_priv bytes in the HashSet of the map (even
+ /// payments over a single path).
+ ///
/// Locked *after* channel_state.
- pending_outbound_payments: Mutex<HashSet<[u8; 32]>>,
+ pending_outbound_payments: Mutex<HashMap<MppId, HashSet<[u8; 32]>>>,
our_network_key: SecretKey,
our_network_pubkey: PublicKey,
#[allow(dead_code)]
const CHECK_CLTV_EXPIRY_SANITY_2: u32 = MIN_CLTV_EXPIRY_DELTA as u32 - LATENCY_GRACE_PERIOD_BLOCKS - 2*CLTV_CLAIM_BUFFER;
+/// Information needed for constructing an invoice route hint for this channel.
+#[derive(Clone, Debug, PartialEq)]
+pub struct CounterpartyForwardingInfo {
+ /// Base routing fee in millisatoshis.
+ pub fee_base_msat: u32,
+ /// Amount in millionths of a satoshi the channel will charge per transferred satoshi.
+ pub fee_proportional_millionths: u32,
+ /// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart,
+ /// such that the outgoing HTLC is forwardable to this counterparty. See `msgs::ChannelUpdate`'s
+ /// `cltv_expiry_delta` for more details.
+ pub cltv_expiry_delta: u16,
+}
+
/// Channel parameters which apply to our counterparty. These are split out from [`ChannelDetails`]
/// to better separate parameters.
#[derive(Clone, Debug, PartialEq)]
pending_msg_events: Vec::new(),
}),
pending_inbound_payments: Mutex::new(HashMap::new()),
- pending_outbound_payments: Mutex::new(HashSet::new()),
+ pending_outbound_payments: Mutex::new(HashMap::new()),
our_network_key: keys_manager.get_node_secret(),
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()),
}
// Only public for testing, this should otherwise never be called direcly
- pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
+ pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, mpp_id: MppId, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
let prng_seed = self.keys_manager.get_secure_random_bytes();
let session_priv_bytes = self.keys_manager.get_secure_random_bytes();
let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
- assert!(self.pending_outbound_payments.lock().unwrap().insert(session_priv_bytes));
+ let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
+ let sessions = pending_outbounds.entry(mpp_id).or_insert(HashSet::new());
+ assert!(sessions.insert(session_priv_bytes));
let err: Result<(), _> = loop {
let mut channel_lock = self.channel_state.lock().unwrap();
path: path.clone(),
session_priv: session_priv.clone(),
first_hop_htlc_msat: htlc_msat,
+ mpp_id,
}, onion_packet, &self.logger), channel_state, chan)
} {
Some((update_add, commitment_signed, monitor_update)) => {
let mut total_value = 0;
let our_node_id = self.get_our_node_id();
let mut path_errs = Vec::with_capacity(route.paths.len());
+ let mpp_id = MppId(self.keys_manager.get_secure_random_bytes());
'path_check: for path in route.paths.iter() {
if path.len() < 1 || path.len() > 20 {
path_errs.push(Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"}));
let cur_height = self.best_block.read().unwrap().height() + 1;
let mut results = Vec::new();
for path in route.paths.iter() {
- results.push(self.send_payment_along_path(&path, &payment_hash, payment_secret, total_value, cur_height, &keysend_preimage));
+ results.push(self.send_payment_along_path(&path, &payment_hash, payment_secret, total_value, cur_height, mpp_id, &keysend_preimage));
}
let mut has_ok = false;
let mut has_err = false;
self.fail_htlc_backwards_internal(channel_state,
htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data});
},
- HTLCSource::OutboundRoute { session_priv, .. } => {
- if {
- let mut session_priv_bytes = [0; 32];
- session_priv_bytes.copy_from_slice(&session_priv[..]);
- self.pending_outbound_payments.lock().unwrap().remove(&session_priv_bytes)
- } {
- self.pending_events.lock().unwrap().push(
- events::Event::PaymentFailed {
- payment_hash,
- rejected_by_dest: false,
-#[cfg(test)]
- error_code: None,
-#[cfg(test)]
- error_data: None,
+ HTLCSource::OutboundRoute { session_priv, mpp_id, .. } => {
+ let mut session_priv_bytes = [0; 32];
+ session_priv_bytes.copy_from_slice(&session_priv[..]);
+ let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+ if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(mpp_id) {
+ if sessions.get_mut().remove(&session_priv_bytes) {
+ self.pending_events.lock().unwrap().push(
+ events::Event::PaymentFailed {
+ payment_hash,
+ rejected_by_dest: false,
+ network_update: None,
+ all_paths_failed: sessions.get().len() == 0,
+ #[cfg(test)]
+ error_code: None,
+ #[cfg(test)]
+ error_data: None,
+ }
+ );
+ if sessions.get().len() == 0 {
+ sessions.remove();
}
- )
+ }
} else {
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
}
// from block_connected which may run during initialization prior to the chain_monitor
// being fully configured. See the docs for `ChannelManagerReadArgs` for more.
match source {
- HTLCSource::OutboundRoute { ref path, session_priv, .. } => {
- if {
- let mut session_priv_bytes = [0; 32];
- session_priv_bytes.copy_from_slice(&session_priv[..]);
- !self.pending_outbound_payments.lock().unwrap().remove(&session_priv_bytes)
- } {
+ HTLCSource::OutboundRoute { ref path, session_priv, mpp_id, .. } => {
+ let mut session_priv_bytes = [0; 32];
+ session_priv_bytes.copy_from_slice(&session_priv[..]);
+ let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+ let mut all_paths_failed = false;
+ if let hash_map::Entry::Occupied(mut sessions) = outbounds.entry(mpp_id) {
+ if !sessions.get_mut().remove(&session_priv_bytes) {
+ log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
+ return;
+ }
+ if sessions.get().len() == 0 {
+ all_paths_failed = true;
+ sessions.remove();
+ }
+ } else {
log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
return;
}
match &onion_error {
&HTLCFailReason::LightningError { ref err } => {
#[cfg(test)]
- let (channel_update, payment_retryable, onion_error_code, onion_error_data) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+ let (network_update, 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 (channel_update, payment_retryable, _, _) = onion_utils::process_onion_failure(&self.secp_ctx, &self.logger, &source, err.data.clone());
+ let (network_update, 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!
- if let Some(update) = channel_update {
- self.channel_state.lock().unwrap().pending_msg_events.push(
- events::MessageSendEvent::PaymentFailureNetworkUpdate {
- update,
- }
- );
- }
self.pending_events.lock().unwrap().push(
events::Event::PaymentFailed {
payment_hash: payment_hash.clone(),
rejected_by_dest: !payment_retryable,
+ network_update,
+ all_paths_failed,
#[cfg(test)]
error_code: onion_error_code,
#[cfg(test)]
ref data,
.. } => {
// we get a fail_malformed_htlc from the first hop
- // TODO: We'd like to generate a PaymentFailureNetworkUpdate for temporary
+ // TODO: We'd like to generate a NetworkUpdate for temporary
// failures here, but that would be insufficient as get_route
// generally ignores its view of our own channels as we provide them via
// ChannelDetails.
events::Event::PaymentFailed {
payment_hash: payment_hash.clone(),
rejected_by_dest: path.len() == 1,
+ network_update: None,
+ all_paths_failed,
#[cfg(test)]
error_code: Some(*failure_code),
#[cfg(test)]
fn claim_funds_internal(&self, mut channel_state_lock: MutexGuard<ChannelHolder<Signer>>, source: HTLCSource, payment_preimage: PaymentPreimage, forwarded_htlc_value_msat: Option<u64>, from_onchain: bool) {
match source {
- HTLCSource::OutboundRoute { session_priv, .. } => {
+ HTLCSource::OutboundRoute { session_priv, mpp_id, .. } => {
mem::drop(channel_state_lock);
- if {
- let mut session_priv_bytes = [0; 32];
- session_priv_bytes.copy_from_slice(&session_priv[..]);
- self.pending_outbound_payments.lock().unwrap().remove(&session_priv_bytes)
- } {
- let mut pending_events = self.pending_events.lock().unwrap();
- pending_events.push(events::Event::PaymentSent {
- payment_preimage
- });
+ let mut session_priv_bytes = [0; 32];
+ session_priv_bytes.copy_from_slice(&session_priv[..]);
+ let mut outbounds = self.pending_outbound_payments.lock().unwrap();
+ let found_payment = if let Some(mut sessions) = outbounds.remove(&mpp_id) {
+ sessions.remove(&session_priv_bytes)
+ } else { false };
+ if found_payment {
+ self.pending_events.lock().unwrap().push(
+ events::Event::PaymentSent { payment_preimage }
+ );
} else {
log_trace!(self.logger, "Received duplicative fulfill for HTLC with payment_preimage {}", log_bytes!(payment_preimage.0));
}
}
let create_pending_htlc_status = |chan: &Channel<Signer>, pending_forward_info: PendingHTLCStatus, error_code: u16| {
- // Ensure error_code has the UPDATE flag set, since by default we send a
- // channel update along as part of failing the HTLC.
- assert!((error_code & 0x1000) != 0);
// If the update_add is completely bogus, the call will Err and we will close,
// but if we've sent a shutdown and they haven't acknowledged it yet, we just
// want to reject the new HTLC and fail it backwards instead of forwarding.
match pending_forward_info {
PendingHTLCStatus::Forward(PendingHTLCInfo { ref incoming_shared_secret, .. }) => {
- let reason = if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
- let mut res = Vec::with_capacity(8 + 128);
- // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
- res.extend_from_slice(&byte_utils::be16_to_array(0));
- res.extend_from_slice(&upd.encode_with_len()[..]);
- res
- }[..])
+ let reason = if (error_code & 0x1000) != 0 {
+ if let Ok(upd) = self.get_channel_update_for_unicast(chan) {
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
+ let mut res = Vec::with_capacity(8 + 128);
+ // TODO: underspecified, follow https://github.com/lightningnetwork/lightning-rfc/issues/791
+ res.extend_from_slice(&byte_utils::be16_to_array(0));
+ res.extend_from_slice(&upd.encode_with_len()[..]);
+ res
+ }[..])
+ } else {
+ // The only case where we'd be unable to
+ // successfully get a channel update is if the
+ // channel isn't in the fully-funded state yet,
+ // implying our counterparty is trying to route
+ // payments over the channel back to themselves
+ // (because no one else should know the short_id
+ // is a lightning channel yet). We should have
+ // no problem just calling this
+ // unknown_next_peer (0x4000|10).
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ }
} else {
- // The only case where we'd be unable to
- // successfully get a channel update is if the
- // channel isn't in the fully-funded state yet,
- // implying our counterparty is trying to route
- // payments over the channel back to themselves
- // (cause no one else should know the short_id
- // is a lightning channel yet). We should have
- // no problem just calling this
- // unknown_next_peer (0x4000|10).
- onion_utils::build_first_hop_failure_packet(incoming_shared_secret, 0x4000|10, &[])
+ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &[])
};
let msg = msgs::UpdateFailHTLC {
channel_id: msg.channel_id,
#[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
pub fn get_and_clear_pending_events(&self) -> Vec<events::Event> {
let events = core::cell::RefCell::new(Vec::new());
- let event_handler = |event| events.borrow_mut().push(event);
+ let event_handler = |event: &events::Event| events.borrow_mut().push(event.clone());
self.process_pending_events(&event_handler);
events.into_inner()
}
}
for event in pending_events.drain(..) {
- handler.handle_event(event);
+ handler.handle_event(&event);
}
result
&events::MessageSendEvent::BroadcastChannelUpdate { .. } => true,
&events::MessageSendEvent::SendChannelUpdate { ref node_id, .. } => node_id != counterparty_node_id,
&events::MessageSendEvent::HandleError { ref node_id, .. } => node_id != counterparty_node_id,
- &events::MessageSendEvent::PaymentFailureNetworkUpdate { .. } => true,
&events::MessageSendEvent::SendChannelRangeQuery { .. } => false,
&events::MessageSendEvent::SendShortIdsQuery { .. } => false,
&events::MessageSendEvent::SendReplyChannelRange { .. } => false,
(8, outgoing_cltv_value, required)
});
-impl_writeable_tlv_based_enum!(HTLCFailureMsg, ;
- (0, Relay),
- (1, Malformed),
-);
+
+impl Writeable for HTLCFailureMsg {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ match self {
+ HTLCFailureMsg::Relay(msgs::UpdateFailHTLC { channel_id, htlc_id, reason }) => {
+ 0u8.write(writer)?;
+ channel_id.write(writer)?;
+ htlc_id.write(writer)?;
+ reason.write(writer)?;
+ },
+ HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
+ channel_id, htlc_id, sha256_of_onion, failure_code
+ }) => {
+ 1u8.write(writer)?;
+ channel_id.write(writer)?;
+ htlc_id.write(writer)?;
+ sha256_of_onion.write(writer)?;
+ failure_code.write(writer)?;
+ },
+ }
+ Ok(())
+ }
+}
+
+impl Readable for HTLCFailureMsg {
+ fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ let id: u8 = Readable::read(reader)?;
+ match id {
+ 0 => {
+ Ok(HTLCFailureMsg::Relay(msgs::UpdateFailHTLC {
+ channel_id: Readable::read(reader)?,
+ htlc_id: Readable::read(reader)?,
+ reason: Readable::read(reader)?,
+ }))
+ },
+ 1 => {
+ Ok(HTLCFailureMsg::Malformed(msgs::UpdateFailMalformedHTLC {
+ channel_id: Readable::read(reader)?,
+ htlc_id: Readable::read(reader)?,
+ sha256_of_onion: Readable::read(reader)?,
+ failure_code: Readable::read(reader)?,
+ }))
+ },
+ // In versions prior to 0.0.101, HTLCFailureMsg objects were written with type 0 or 1 but
+ // weren't length-prefixed and thus didn't support reading the TLV stream suffix of the network
+ // messages contained in the variants.
+ // In version 0.0.101, support for reading the variants with these types was added, and
+ // we should migrate to writing these variants when UpdateFailHTLC or
+ // UpdateFailMalformedHTLC get TLV fields.
+ 2 => {
+ let length: BigSize = Readable::read(reader)?;
+ let mut s = FixedLengthReader::new(reader, length.0);
+ let res = Readable::read(&mut s)?;
+ s.eat_remaining()?; // Return ShortRead if there's actually not enough bytes
+ Ok(HTLCFailureMsg::Relay(res))
+ },
+ 3 => {
+ let length: BigSize = Readable::read(reader)?;
+ let mut s = FixedLengthReader::new(reader, length.0);
+ let res = Readable::read(&mut s)?;
+ s.eat_remaining()?; // Return ShortRead if there's actually not enough bytes
+ Ok(HTLCFailureMsg::Malformed(res))
+ },
+ _ => Err(DecodeError::UnknownRequiredFeature),
+ }
+ }
+}
+
impl_writeable_tlv_based_enum!(PendingHTLCStatus, ;
(0, Forward),
(1, Fail),
}
}
-impl_writeable_tlv_based_enum!(HTLCSource,
- (0, OutboundRoute) => {
- (0, session_priv, required),
- (2, first_hop_htlc_msat, required),
- (4, path, vec_type),
- }, ;
- (1, PreviousHopData)
-);
+impl Readable for HTLCSource {
+ fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ let id: u8 = Readable::read(reader)?;
+ match id {
+ 0 => {
+ let mut session_priv: ::util::ser::OptionDeserWrapper<SecretKey> = ::util::ser::OptionDeserWrapper(None);
+ let mut first_hop_htlc_msat: u64 = 0;
+ let mut path = Some(Vec::new());
+ let mut mpp_id = None;
+ read_tlv_fields!(reader, {
+ (0, session_priv, required),
+ (1, mpp_id, option),
+ (2, first_hop_htlc_msat, required),
+ (4, path, vec_type),
+ });
+ if mpp_id.is_none() {
+ // For backwards compat, if there was no mpp_id written, use the session_priv bytes
+ // instead.
+ mpp_id = Some(MppId(*session_priv.0.unwrap().as_ref()));
+ }
+ Ok(HTLCSource::OutboundRoute {
+ session_priv: session_priv.0.unwrap(),
+ first_hop_htlc_msat: first_hop_htlc_msat,
+ path: path.unwrap(),
+ mpp_id: mpp_id.unwrap(),
+ })
+ }
+ 1 => Ok(HTLCSource::PreviousHopData(Readable::read(reader)?)),
+ _ => Err(DecodeError::UnknownRequiredFeature),
+ }
+ }
+}
+
+impl Writeable for HTLCSource {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::io::Error> {
+ match self {
+ HTLCSource::OutboundRoute { ref session_priv, ref first_hop_htlc_msat, ref path, mpp_id } => {
+ 0u8.write(writer)?;
+ let mpp_id_opt = Some(mpp_id);
+ write_tlv_fields!(writer, {
+ (0, session_priv, required),
+ (1, mpp_id_opt, option),
+ (2, first_hop_htlc_msat, required),
+ (4, path, vec_type),
+ });
+ }
+ HTLCSource::PreviousHopData(ref field) => {
+ 1u8.write(writer)?;
+ field.write(writer)?;
+ }
+ }
+ Ok(())
+ }
+}
impl_writeable_tlv_based_enum!(HTLCFailReason,
(0, LightningError) => {
}
let pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
- (pending_outbound_payments.len() as u64).write(writer)?;
- for session_priv in pending_outbound_payments.iter() {
- session_priv.write(writer)?;
+ // For backwards compat, write the session privs and their total length.
+ let mut num_pending_outbounds_compat: u64 = 0;
+ for (_, outbounds) in pending_outbound_payments.iter() {
+ num_pending_outbounds_compat += outbounds.len() as u64;
+ }
+ num_pending_outbounds_compat.write(writer)?;
+ for (_, outbounds) in pending_outbound_payments.iter() {
+ for outbound in outbounds.iter() {
+ outbound.write(writer)?;
+ }
}
- write_tlv_fields!(writer, {});
+ write_tlv_fields!(writer, {
+ (1, pending_outbound_payments, required),
+ });
Ok(())
}
}
}
- let pending_outbound_payments_count: u64 = Readable::read(reader)?;
- let mut pending_outbound_payments: HashSet<[u8; 32]> = HashSet::with_capacity(cmp::min(pending_outbound_payments_count as usize, MAX_ALLOC_SIZE/32));
- for _ in 0..pending_outbound_payments_count {
- if !pending_outbound_payments.insert(Readable::read(reader)?) {
- return Err(DecodeError::InvalidValue);
- }
+ let pending_outbound_payments_count_compat: u64 = Readable::read(reader)?;
+ let mut pending_outbound_payments_compat: HashMap<MppId, HashSet<[u8; 32]>> =
+ HashMap::with_capacity(cmp::min(pending_outbound_payments_count_compat as usize, MAX_ALLOC_SIZE/32));
+ for _ in 0..pending_outbound_payments_count_compat {
+ let session_priv = Readable::read(reader)?;
+ if pending_outbound_payments_compat.insert(MppId(session_priv), [session_priv].iter().cloned().collect()).is_some() {
+ return Err(DecodeError::InvalidValue)
+ };
}
- read_tlv_fields!(reader, {});
+ let mut pending_outbound_payments = None;
+ read_tlv_fields!(reader, {
+ (1, pending_outbound_payments, option),
+ });
+ if pending_outbound_payments.is_none() {
+ pending_outbound_payments = Some(pending_outbound_payments_compat);
+ }
let mut secp_ctx = Secp256k1::new();
secp_ctx.seeded_randomize(&args.keys_manager.get_secure_random_bytes());
pending_msg_events: Vec::new(),
}),
pending_inbound_payments: Mutex::new(pending_inbound_payments),
- pending_outbound_payments: Mutex::new(pending_outbound_payments),
+ pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
our_network_key: args.keys_manager.get_node_secret(),
our_network_pubkey: PublicKey::from_secret_key(&secp_ctx, &args.keys_manager.get_node_secret()),
use bitcoin::hashes::sha256::Hash as Sha256;
use core::time::Duration;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
- use ln::channelmanager::PaymentSendFailure;
+ use ln::channelmanager::{MppId, PaymentSendFailure};
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::functional_test_utils::*;
use ln::msgs;
// First, send a partial MPP payment.
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger).unwrap();
let (payment_preimage, our_payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[1]);
+ let mpp_id = MppId([42; 32]);
// Use the utility function send_payment_along_path to send the payment with MPP data which
// indicates there are more HTLCs coming.
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
- nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, &None).unwrap();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, mpp_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
expect_payment_failed!(nodes[0], our_payment_hash, true);
// Send the second half of the original MPP payment.
- nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, &None).unwrap();
+ nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200_000, cur_height, mpp_id, &None).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bs_third_raa);
check_added_monitors!(nodes[0], 1);
- // There's an existing bug that generates a PaymentSent event for each MPP path, so handle that here.
+ // Note that successful MPP payments will generate 1 event upon the first path's success. No
+ // further events will be generated for subsequence path successes.
let events = nodes[0].node.get_and_clear_pending_events();
match events[0] {
Event::PaymentSent { payment_preimage: ref preimage } => {
},
_ => panic!("Unexpected event"),
}
- match events[1] {
- Event::PaymentSent { payment_preimage: ref preimage } => {
- assert_eq!(payment_preimage, *preimage);
- },
- _ => panic!("Unexpected event"),
- }
}
#[test]
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &expected_route, 100_000);
// Next, attempt a keysend payment and make sure it fails.
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(), &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
// To start (2), send a keysend payment but don't claim it.
let payment_preimage = PaymentPreimage([42; 32]);
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(), &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100_000, TEST_FINAL_CLTV, &logger).unwrap();
let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(payment_preimage)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
- let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
+ let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let first_hops = nodes[0].node.list_usable_channels();
- let route = get_keysend_route(&payer_pubkey, &network_graph, &payee_pubkey,
+ let route = get_keysend_route(&payer_pubkey, network_graph, &payee_pubkey,
Some(&first_hops.iter().collect::<Vec<_>>()), &vec![], 10000, 40,
nodes[0].logger).unwrap();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
- let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
+ let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let first_hops = nodes[0].node.list_usable_channels();
- let route = get_keysend_route(&payer_pubkey, &network_graph, &payee_pubkey,
+ let route = get_keysend_route(&payer_pubkey, network_graph, &payee_pubkey,
Some(&first_hops.iter().collect::<Vec<_>>()), &vec![], 10000, 40,
nodes[0].logger).unwrap();
// Marshall an MPP route.
let (_, payment_hash, _) = get_payment_preimage_hash!(&nodes[3]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
let path = route.paths[0].clone();
route.paths.push(path);
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
use io;
use prelude::*;
use core::{cmp, fmt};
+use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use bitcoin::bech32;
}
}
}
+impl<T: sealed::Context> Hash for Features<T> {
+ fn hash<H: Hasher>(&self, hasher: &mut H) {
+ self.flags.hash(hasher);
+ }
+}
impl<T: sealed::Context> PartialEq for Features<T> {
fn eq(&self, o: &Self) -> bool {
self.flags.eq(&o.flags)
/// Writes all features present up to, and including, 13.
pub(crate) fn write_up_to_13<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
let len = cmp::min(2, self.flags.len());
- w.size_hint(len + 2);
(len as u16).write(w)?;
for i in (0..len).rev() {
if i == 0 {
&self.flags
}
- pub(crate) fn requires_unknown_bits(&self) -> bool {
+ /// Returns true if this `Features` object contains unknown feature flags which are set as
+ /// "required".
+ pub fn requires_unknown_bits(&self) -> bool {
// Bitwise AND-ing with all even bits set except for known features will select required
// unknown features.
let byte_count = T::KNOWN_FEATURE_MASK.len();
(byte & unknown_features) != 0
})
}
-
- /// The number of bytes required to represent the feature flags present. This does not include
- /// the length bytes which are included in the serialized form.
- pub(crate) fn byte_count(&self) -> usize {
- self.flags.len()
- }
}
impl<T: sealed::DataLossProtect> Features<T> {
impl<T: sealed::Context> Writeable for Features<T> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(self.flags.len() + 2);
(self.flags.len() as u16).write(w)?;
for f in self.flags.iter().rev() { // Swap back to big-endian
f.write(w)?;
#[test]
fn convert_to_context_with_unknown_flags() {
// Ensure the `from` context has fewer known feature bytes than the `to` context.
- assert!(InvoiceFeatures::known().byte_count() < NodeFeatures::known().byte_count());
+ assert!(InvoiceFeatures::known().flags.len() < NodeFeatures::known().flags.len());
let invoice_features = InvoiceFeatures::known().set_unknown_feature_optional();
assert!(invoice_features.supports_unknown_bits());
let node_features: NodeFeatures = invoice_features.to_context();
// Check that if we serialize the Router, we can deserialize it again.
{
let mut w = test_utils::TestVecWriter(Vec::new());
- let network_graph_ser = self.net_graph_msg_handler.network_graph.read().unwrap();
+ let network_graph_ser = &self.net_graph_msg_handler.network_graph;
network_graph_ser.write(&mut w).unwrap();
let network_graph_deser = <NetworkGraph>::read(&mut io::Cursor::new(&w.0)).unwrap();
- assert!(network_graph_deser == *self.net_graph_msg_handler.network_graph.read().unwrap());
- let net_graph_msg_handler = NetGraphMsgHandler::from_net_graph(
- Some(self.chain_source), self.logger, network_graph_deser
+ assert!(network_graph_deser == self.net_graph_msg_handler.network_graph);
+ let net_graph_msg_handler = NetGraphMsgHandler::new(
+ network_graph_deser, Some(self.chain_source), self.logger
);
let mut chan_progress = 0;
loop {
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!($recv_node);
let net_graph_msg_handler = &$send_node.net_graph_msg_handler;
let route = get_route(&$send_node.node.get_our_node_id(),
- &net_graph_msg_handler.network_graph.read().unwrap(),
+ &net_graph_msg_handler.network_graph,
&$recv_node.node.get_our_node_id(), None, None, &Vec::new(), $recv_value, TEST_FINAL_CLTV, $send_node.logger).unwrap();
(route, payment_hash, payment_preimage, payment_secret)
}}
}
#[cfg(test)]
-macro_rules! expect_payment_failure_chan_update {
- ($node: expr, $scid: expr, $chan_closed: expr) => {
- let events = $node.node.get_and_clear_pending_msg_events();
+macro_rules! expect_payment_failed_with_update {
+ ($node: expr, $expected_payment_hash: expr, $rejected_by_dest: expr, $scid: expr, $chan_closed: expr) => {
+ let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- MessageSendEvent::PaymentFailureNetworkUpdate { ref update } => {
- match update {
- &HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } if !$chan_closed => {
+ Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref network_update, ref error_code, ref error_data, .. } => {
+ assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
+ assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
+ assert!(error_code.is_some(), "expected error_code.is_some() = true");
+ assert!(error_data.is_some(), "expected error_data.is_some() = true");
+ match network_update {
+ &Some(NetworkUpdate::ChannelUpdateMessage { ref msg }) if !$chan_closed => {
assert_eq!(msg.contents.short_channel_id, $scid);
assert_eq!(msg.contents.flags & 2, 0);
},
- &HTLCFailChannelUpdate::ChannelClosed { short_channel_id, is_permanent } if $chan_closed => {
+ &Some(NetworkUpdate::ChannelClosed { short_channel_id, is_permanent }) if $chan_closed => {
assert_eq!(short_channel_id, $scid);
assert!(is_permanent);
},
- _ => panic!("Unexpected update type"),
+ Some(_) => panic!("Unexpected update type"),
+ None => panic!("Expected update"),
}
},
_ => panic!("Unexpected event"),
let events = $node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- Event::PaymentFailed { ref payment_hash, rejected_by_dest, ref error_code, ref error_data } => {
+ Event::PaymentFailed { ref payment_hash, rejected_by_dest, network_update: _, ref error_code, ref error_data, .. } => {
assert_eq!(*payment_hash, $expected_payment_hash, "unexpected payment_hash");
assert_eq!(rejected_by_dest, $rejected_by_dest, "unexpected rejected_by_dest value");
assert!(error_code.is_some(), "expected error_code.is_some() = true");
if !skip_last {
last_update_fulfill_dance!(origin_node, expected_route.first().unwrap());
- expect_payment_sent!(origin_node, our_payment_preimage);
}
}
+ if !skip_last {
+ expect_payment_sent!(origin_node, our_payment_preimage);
+ }
}
pub fn claim_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_preimage: PaymentPreimage) {
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) {
let net_graph_msg_handler = &origin_node.net_graph_msg_handler;
- let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph,
&expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()),
Some(&origin_node.node.list_usable_channels().iter().collect::<Vec<_>>()), &[],
recv_value, TEST_FINAL_CLTV, origin_node.logger).unwrap();
pub fn route_over_limit<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], recv_value: u64) {
let net_graph_msg_handler = &origin_node.net_graph_msg_handler;
- let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), recv_value, TEST_FINAL_CLTV, origin_node.logger).unwrap();
+ let route = get_route(&origin_node.node.get_our_node_id(), &net_graph_msg_handler.network_graph, &expected_route.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), recv_value, TEST_FINAL_CLTV, origin_node.logger).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), expected_route.len());
for (node, hop) in expected_route.iter().zip(route.paths[0].iter()) {
claim_payment(&origin, expected_route, our_payment_preimage);
}
-pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], skip_last: bool, our_payment_hash: PaymentHash) {
- assert!(expected_route.last().unwrap().node.fail_htlc_backwards(&our_payment_hash));
- expect_pending_htlcs_forwardable!(expected_route.last().unwrap());
- check_added_monitors!(expected_route.last().unwrap(), 1);
+pub fn fail_payment_along_route<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_paths_slice: &[&[&Node<'a, 'b, 'c>]], skip_last: bool, our_payment_hash: PaymentHash) {
+ let mut expected_paths: Vec<_> = expected_paths_slice.iter().collect();
+ for path in expected_paths.iter() {
+ assert_eq!(path.last().unwrap().node.get_our_node_id(), expected_paths[0].last().unwrap().node.get_our_node_id());
+ }
+ assert!(expected_paths[0].last().unwrap().node.fail_htlc_backwards(&our_payment_hash));
+ expect_pending_htlcs_forwardable!(expected_paths[0].last().unwrap());
+ check_added_monitors!(expected_paths[0].last().unwrap(), expected_paths.len());
- let mut next_msgs: Option<(msgs::UpdateFailHTLC, msgs::CommitmentSigned)> = None;
- macro_rules! update_fail_dance {
- ($node: expr, $prev_node: expr, $last_node: expr) => {
- {
- $node.node.handle_update_fail_htlc(&$prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
- commitment_signed_dance!($node, $prev_node, next_msgs.as_ref().unwrap().1, !$last_node);
- if skip_last && $last_node {
- expect_pending_htlcs_forwardable!($node);
+ let mut per_path_msgs: Vec<((msgs::UpdateFailHTLC, msgs::CommitmentSigned), PublicKey)> = Vec::with_capacity(expected_paths.len());
+ let events = expected_paths[0].last().unwrap().node.get_and_clear_pending_msg_events();
+ assert_eq!(events.len(), expected_paths.len());
+ for ev in events.iter() {
+ let (update_fail, commitment_signed, node_id) = match ev {
+ &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, ref update_fee, ref commitment_signed } } => {
+ assert!(update_add_htlcs.is_empty());
+ assert!(update_fulfill_htlcs.is_empty());
+ assert_eq!(update_fail_htlcs.len(), 1);
+ assert!(update_fail_malformed_htlcs.is_empty());
+ assert!(update_fee.is_none());
+ (update_fail_htlcs[0].clone(), commitment_signed.clone(), node_id.clone())
+ },
+ _ => panic!("Unexpected event"),
+ };
+ per_path_msgs.push(((update_fail, commitment_signed), node_id));
+ }
+ per_path_msgs.sort_unstable_by(|(_, node_id_a), (_, node_id_b)| node_id_a.cmp(node_id_b));
+ expected_paths.sort_unstable_by(|path_a, path_b| path_a[path_a.len() - 2].node.get_our_node_id().cmp(&path_b[path_b.len() - 2].node.get_our_node_id()));
+
+ for (i, (expected_route, (path_msgs, next_hop))) in expected_paths.iter().zip(per_path_msgs.drain(..)).enumerate() {
+ let mut next_msgs = Some(path_msgs);
+ let mut expected_next_node = next_hop;
+ let mut prev_node = expected_route.last().unwrap();
+
+ for (idx, node) in expected_route.iter().rev().enumerate().skip(1) {
+ assert_eq!(expected_next_node, node.node.get_our_node_id());
+ let update_next_node = !skip_last || idx != expected_route.len() - 1;
+ if next_msgs.is_some() {
+ node.node.handle_update_fail_htlc(&prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
+ commitment_signed_dance!(node, prev_node, next_msgs.as_ref().unwrap().1, update_next_node);
+ if !update_next_node {
+ expect_pending_htlcs_forwardable!(node);
}
}
- }
- }
+ let events = node.node.get_and_clear_pending_msg_events();
+ if update_next_node {
+ assert_eq!(events.len(), 1);
+ match events[0] {
+ 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, ref update_fee, ref commitment_signed } } => {
+ assert!(update_add_htlcs.is_empty());
+ assert!(update_fulfill_htlcs.is_empty());
+ assert_eq!(update_fail_htlcs.len(), 1);
+ assert!(update_fail_malformed_htlcs.is_empty());
+ assert!(update_fee.is_none());
+ expected_next_node = node_id.clone();
+ next_msgs = Some((update_fail_htlcs[0].clone(), commitment_signed.clone()));
+ },
+ _ => panic!("Unexpected event"),
+ }
+ } else {
+ assert!(events.is_empty());
+ }
+ if !skip_last && idx == expected_route.len() - 1 {
+ assert_eq!(expected_next_node, origin_node.node.get_our_node_id());
+ }
- let mut expected_next_node = expected_route.last().unwrap().node.get_our_node_id();
- let mut prev_node = expected_route.last().unwrap();
- for (idx, node) in expected_route.iter().rev().enumerate() {
- assert_eq!(expected_next_node, node.node.get_our_node_id());
- if next_msgs.is_some() {
- // We may be the "last node" for the purpose of the commitment dance if we're
- // skipping the last node (implying it is disconnected) and we're the
- // second-to-last node!
- update_fail_dance!(node, prev_node, skip_last && idx == expected_route.len() - 1);
+ prev_node = node;
}
- let events = node.node.get_and_clear_pending_msg_events();
- if !skip_last || idx != expected_route.len() - 1 {
+ if !skip_last {
+ let prev_node = expected_route.first().unwrap();
+ origin_node.node.handle_update_fail_htlc(&prev_node.node.get_our_node_id(), &next_msgs.as_ref().unwrap().0);
+ check_added_monitors!(origin_node, 0);
+ assert!(origin_node.node.get_and_clear_pending_msg_events().is_empty());
+ commitment_signed_dance!(origin_node, prev_node, next_msgs.as_ref().unwrap().1, false);
+ let events = origin_node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
- 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, ref update_fee, ref commitment_signed } } => {
- assert!(update_add_htlcs.is_empty());
- assert!(update_fulfill_htlcs.is_empty());
- assert_eq!(update_fail_htlcs.len(), 1);
- assert!(update_fail_malformed_htlcs.is_empty());
- assert!(update_fee.is_none());
- expected_next_node = node_id.clone();
- next_msgs = Some((update_fail_htlcs[0].clone(), commitment_signed.clone()));
+ Event::PaymentFailed { payment_hash, rejected_by_dest, all_paths_failed, .. } => {
+ assert_eq!(payment_hash, our_payment_hash);
+ assert!(rejected_by_dest);
+ assert_eq!(all_paths_failed, i == expected_paths.len() - 1);
},
_ => panic!("Unexpected event"),
}
- } else {
- assert!(events.is_empty());
- }
- if !skip_last && idx == expected_route.len() - 1 {
- assert_eq!(expected_next_node, origin_node.node.get_our_node_id());
- }
-
- prev_node = node;
- }
-
- if !skip_last {
- update_fail_dance!(origin_node, expected_route.first().unwrap(), true);
-
- let events = origin_node.node.get_and_clear_pending_events();
- assert_eq!(events.len(), 1);
- match events[0] {
- Event::PaymentFailed { payment_hash, rejected_by_dest, .. } => {
- assert_eq!(payment_hash, our_payment_hash);
- assert!(rejected_by_dest);
- },
- _ => panic!("Unexpected event"),
}
}
}
-pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_route: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash) {
- fail_payment_along_route(origin_node, expected_route, false, our_payment_hash);
+pub fn fail_payment<'a, 'b, 'c>(origin_node: &Node<'a, 'b, 'c>, expected_path: &[&Node<'a, 'b, 'c>], our_payment_hash: PaymentHash) {
+ fail_payment_along_route(origin_node, &[&expected_path[..]], false, our_payment_hash);
}
pub fn create_chanmon_cfgs(node_count: usize) -> Vec<TestChanMonCfg> {
let connect_style = Rc::new(RefCell::new(ConnectStyle::FullBlockViaListen));
for i in 0..node_count {
- let net_graph_msg_handler = NetGraphMsgHandler::new(cfgs[i].chain_source.genesis_hash, None, cfgs[i].logger);
+ let network_graph = NetworkGraph::new(cfgs[i].chain_source.genesis_hash);
+ let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, cfgs[i].logger);
nodes.push(Node{ chain_source: cfgs[i].chain_source,
tx_broadcaster: cfgs[i].tx_broadcaster, chain_monitor: &cfgs[i].chain_monitor,
keys_manager: &cfgs[i].keys_manager, node: &chan_mgrs[i], net_graph_msg_handler,
use chain::channelmonitor;
use chain::channelmonitor::{ChannelMonitor, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
use chain::transaction::OutPoint;
-use chain::keysinterface::{KeysInterface, BaseSign};
+use chain::keysinterface::BaseSign;
use ln::{PaymentPreimage, PaymentSecret, PaymentHash};
use ln::channel::{COMMITMENT_TX_BASE_WEIGHT, COMMITMENT_TX_WEIGHT_PER_HTLC};
-use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
+use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs, MppId, RAACommitmentOrder, PaymentSendFailure, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA};
use ln::channel::{Channel, ChannelError};
use ln::{chan_utils, onion_utils};
use ln::chan_utils::HTLC_SUCCESS_TX_WEIGHT;
use routing::router::{Route, RouteHop, RouteHint, RouteHintHop, get_route, get_keysend_route};
-use routing::network_graph::RoutingFees;
+use routing::network_graph::{NetworkUpdate, RoutingFees};
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
use ln::msgs;
-use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction};
+use ln::msgs::{ChannelMessageHandler, RoutingMessageHandler, ErrorAction};
use util::enforcing_trait_impls::EnforcingSigner;
use util::{byte_utils, test_utils};
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
use util::ser::{Writeable, ReadableArgs};
use util::config::UserConfig;
-use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hash_types::{Txid, BlockHash};
use bitcoin::blockdata::block::{Block, BlockHeader};
use bitcoin::blockdata::script::Builder;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
-use bitcoin::secp256k1::{Secp256k1, Message};
+use bitcoin::secp256k1::Secp256k1;
use bitcoin::secp256k1::key::{PublicKey,SecretKey};
use regex;
// ...but before it's delivered, nodes[1] starts to send a payment back to nodes[0]...
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[0]);
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- nodes[1].node.send_payment(&get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40000, TEST_FINAL_CLTV, &logger).unwrap(), our_payment_hash, &Some(our_payment_secret)).unwrap();
+ nodes[1].node.send_payment(&get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40000, TEST_FINAL_CLTV, &logger).unwrap(), our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[1], 1);
let payment_event = {
// ...but before it's delivered, nodes[1] starts to send a payment back to nodes[0]...
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[0]);
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- nodes[1].node.send_payment(&get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40000, TEST_FINAL_CLTV, &logger).unwrap(), our_payment_hash, &Some(our_payment_secret)).unwrap();
+ nodes[1].node.send_payment(&get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40000, TEST_FINAL_CLTV, &logger).unwrap(), our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[1], 1);
let payment_event = {
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[0]);
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 800000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 800000, TEST_FINAL_CLTV, &logger).unwrap();
// nothing happens since node[1] is in AwaitingRemoteRevoke
nodes[1].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
for _ in 0..::ln::channel::OUR_MAX_HTLCS {
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
payments.push((payment_preimage, payment_hash));
}
let (_, payment_hash_1, payment_secret_1) = get_payment_preimage_hash!(nodes[2]);
{
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[1].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)), true, APIError::ChannelUnavailable { ref err },
assert!(regex::Regex::new(r"Cannot push more than their max accepted HTLCs \(\d+\)").unwrap().is_match(err)));
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
let (_, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[2]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
}
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &bs_fail_updates.update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], bs_fail_updates.commitment_signed, false, true);
- expect_payment_failure_chan_update!(nodes[0], chan_2.0.contents.short_channel_id, false);
- expect_payment_failed!(nodes[0], payment_hash_2, false);
+ expect_payment_failed_with_update!(nodes[0], payment_hash_2, false, chan_2.0.contents.short_channel_id, false);
// Now forward all the pending HTLCs and claim them back
nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &initial_payment_event.msgs[0]);
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &vec!(&nodes[1])[..], 900_000);
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 800_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 800_000, TEST_FINAL_CLTV, &logger).unwrap();
let node_a_payment_secret = nodes[0].node.create_inbound_payment_for_hash(payment_hash, None, 7200, 0).unwrap();
send_along_route_with_secret(&nodes[1], route, &[&[&nodes[0]]], 800_000, payment_hash, node_a_payment_secret);
let commit_tx_fee = 2 * commit_tx_fee_msat(get_feerate!(nodes[0], chan.2), 1 + 1);
let max_can_send = 5000000 - channel_reserve - commit_tx_fee;
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), max_can_send + 1, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes.last().unwrap().node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), max_can_send + 1, TEST_FINAL_CLTV, &logger).unwrap();
let err = nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).err().unwrap();
match err {
PaymentSendFailure::AllFailedRetrySafe(ref fails) => {
// Get the EnforcingSigner for each channel, which will be used to (1) get the keys
// needed to sign the new commitment tx and (2) sign the new commitment tx.
- let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point) = {
+ let (local_revocation_basepoint, local_htlc_basepoint, local_secret, next_local_point, local_funding) = {
let chan_lock = nodes[0].node.channel_state.lock().unwrap();
let local_chan = chan_lock.by_id.get(&chan.2).unwrap();
let chan_signer = local_chan.get_signer();
+ // Make the signer believe we validated another commitment, so we can release the secret
+ chan_signer.get_enforcement_state().last_holder_commitment -= 1;
+
let pubkeys = chan_signer.pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
chan_signer.release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
- chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx))
+ chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx),
+ chan_signer.pubkeys().funding_pubkey)
};
- let (remote_delayed_payment_basepoint, remote_htlc_basepoint,remote_point) = {
+ let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = {
let chan_lock = nodes[1].node.channel_state.lock().unwrap();
let remote_chan = chan_lock.by_id.get(&chan.2).unwrap();
let chan_signer = remote_chan.get_signer();
let pubkeys = chan_signer.pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
- chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx))
+ chan_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
+ chan_signer.pubkeys().funding_pubkey)
};
// Assemble the set of keys we can use for signatures for our commitment_signed message.
commitment_number,
95000,
local_chan_balance,
+ false, local_funding, remote_funding,
commit_tx_keys.clone(),
feerate_per_kw,
&mut vec![(accepted_htlc_info, ())],
let (payment_preimage_3, payment_hash_3, payment_secret_3) = get_payment_preimage_hash!(nodes[1]);
let send_1 = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_3, &Some(payment_secret_3)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
let (payment_preimage_4, payment_hash_4, payment_secret_4) = get_payment_preimage_hash!(nodes[0]);
let send_2 = {
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 10000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 10000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, payment_hash_4, &Some(payment_secret_4)).unwrap();
check_added_monitors!(nodes[1], 1);
let mut events = nodes[1].node.get_and_clear_pending_msg_events();
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false, true);
- expect_payment_failure_chan_update!(nodes[0], chan_2.0.contents.short_channel_id, true);
- expect_payment_failed!(nodes[0], payment_hash, false);
+ expect_payment_failed_with_update!(nodes[0], payment_hash, false, chan_2.0.contents.short_channel_id, true);
},
_ => panic!("Unexpected event"),
}
let (_, fourth_payment_hash, fourth_payment_secret) = get_payment_preimage_hash!(nodes[2]);
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
let logger = test_utils::TestLogger::new();
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, fourth_payment_hash, &Some(fourth_payment_secret)).unwrap();
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
commitment_signed_dance!(nodes[0], nodes[1], commitment_signed, false, true);
- let events = nodes[0].node.get_and_clear_pending_msg_events();
- // If we delivered B's RAA we got an unknown preimage error, not something
- // that we should update our routing table for.
- assert_eq!(events.len(), if deliver_bs_raa { 2 } else { 3 });
- for event in events {
- match event {
- MessageSendEvent::PaymentFailureNetworkUpdate { .. } => {},
- _ => panic!("Unexpected event"),
- }
- }
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 3);
match events[0] {
- Event::PaymentFailed { ref payment_hash, .. } => {
+ Event::PaymentFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
assert!(failed_htlcs.insert(payment_hash.0));
+ // If we delivered B's RAA we got an unknown preimage error, not something
+ // that we should update our routing table for.
+ if !deliver_bs_raa {
+ assert!(network_update.is_some());
+ }
},
_ => panic!("Unexpected event"),
}
match events[1] {
- Event::PaymentFailed { ref payment_hash, .. } => {
+ Event::PaymentFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
assert!(failed_htlcs.insert(payment_hash.0));
+ assert!(network_update.is_some());
},
_ => panic!("Unexpected event"),
}
match events[2] {
- Event::PaymentFailed { ref payment_hash, .. } => {
+ Event::PaymentFailed { ref payment_hash, rejected_by_dest: _, ref network_update, .. } => {
assert!(failed_htlcs.insert(payment_hash.0));
+ assert!(network_update.is_some());
},
_ => panic!("Unexpected event"),
}
{
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let (_, failed_payment_hash, failed_payment_secret) = get_payment_preimage_hash!(nodes[1]);
{
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, failed_payment_hash, &Some(failed_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 0);
let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
let current_height = nodes[1].node.best_block.read().unwrap().height() + 1;
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(&route.paths[0], 50_000, &Some(payment_secret), current_height, &None).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
let mut payment_event = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, 42, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, 42, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id(), false);
claim_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], true, payment_preimage_3);
- fail_payment_along_route(&nodes[0], &[&nodes[1], &nodes[2]], true, payment_hash_5);
+ fail_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[2]]], true, payment_hash_5);
reconnect_nodes(&nodes[0], &nodes[1], (false, false), (0, 0), (0, 0), (0, 0), (1, 0), (1, 0), (false, false));
{
let logger = test_utils::TestLogger::new();
let payment_event = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph,
&nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
&Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_1, &Some(payment_secret_1)).unwrap();
// Channel should still work fine...
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph,
&nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), Some(&nodes[0].node.list_usable_channels().iter().collect::<Vec<_>>()),
&Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
let payment_preimage_2 = send_along_route(&nodes[0], route, &[&nodes[1]], 1000000).0;
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
let logger = test_utils::TestLogger::new();
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
let (payment_preimage, _, _) = send_along_route(&nodes[0], route, &[&nodes[1]], 1000000);
claim_payment(&nodes[0], &[&nodes[1]], payment_preimage);
// Now try to send a second payment which will fail to send
let (payment_preimage_2, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash_2, &Some(payment_secret_2)).unwrap();
check_added_monitors!(nodes[0], 1);
let our_payment_hash = if send_partial_mpp {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
let (_, our_payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[1]);
// Use the utility function send_payment_along_path to send the payment with MPP data which
// indicates there are more HTLCs coming.
let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
- nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, cur_height, &None).unwrap();
+ let mpp_id = MppId([42; 32]);
+ nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, cur_height, mpp_id, &None).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 (_, first_payment_hash, first_payment_secret) = get_payment_preimage_hash!(nodes[2]);
{
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, first_payment_hash, &Some(first_payment_secret)).unwrap();
}
assert_eq!(nodes[1].node.get_and_clear_pending_msg_events().len(), 1);
let (_, second_payment_hash, second_payment_secret) = get_payment_preimage_hash!(nodes[2]);
if forwarded_htlc {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, second_payment_hash, &Some(first_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let payment_event = SendEvent::from_event(nodes[0].node.get_and_clear_pending_msg_events().remove(0));
check_added_monitors!(nodes[1], 0);
} else {
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, second_payment_hash, &Some(second_payment_secret)).unwrap();
check_added_monitors!(nodes[1], 0);
}
},
_ => unreachable!(),
}
- expect_payment_failed!(nodes[0], second_payment_hash, false);
- expect_payment_failure_chan_update!(nodes[0], chan_2.0.contents.short_channel_id, false);
+ expect_payment_failed_with_update!(nodes[0], second_payment_hash, false, chan_2.0.contents.short_channel_id, false);
} else {
expect_payment_failed!(nodes[1], second_payment_hash, true);
}
do_test_holding_cell_htlc_add_timeouts(true);
}
-#[test]
-fn test_invalid_channel_announcement() {
- //Test BOLT 7 channel_announcement msg requirement for final node, gather data to build customed channel_announcement msgs
- let secp_ctx = Secp256k1::new();
- let chanmon_cfgs = create_chanmon_cfgs(2);
- let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
- let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
- let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
-
- let chan_announcement = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
-
- let a_channel_lock = nodes[0].node.channel_state.lock().unwrap();
- let b_channel_lock = nodes[1].node.channel_state.lock().unwrap();
- let as_chan = a_channel_lock.by_id.get(&chan_announcement.3).unwrap();
- let bs_chan = b_channel_lock.by_id.get(&chan_announcement.3).unwrap();
-
- nodes[0].net_graph_msg_handler.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap(), is_permanent: false } );
-
- let as_bitcoin_key = as_chan.get_signer().inner.holder_channel_pubkeys.funding_pubkey;
- let bs_bitcoin_key = bs_chan.get_signer().inner.holder_channel_pubkeys.funding_pubkey;
-
- let as_network_key = nodes[0].node.get_our_node_id();
- let bs_network_key = nodes[1].node.get_our_node_id();
-
- let were_node_one = as_bitcoin_key.serialize()[..] < bs_bitcoin_key.serialize()[..];
-
- let mut chan_announcement;
-
- macro_rules! dummy_unsigned_msg {
- () => {
- msgs::UnsignedChannelAnnouncement {
- features: ChannelFeatures::known(),
- chain_hash: genesis_block(Network::Testnet).header.block_hash(),
- short_channel_id: as_chan.get_short_channel_id().unwrap(),
- node_id_1: if were_node_one { as_network_key } else { bs_network_key },
- node_id_2: if were_node_one { bs_network_key } else { as_network_key },
- bitcoin_key_1: if were_node_one { as_bitcoin_key } else { bs_bitcoin_key },
- bitcoin_key_2: if were_node_one { bs_bitcoin_key } else { as_bitcoin_key },
- excess_data: Vec::new(),
- }
- }
- }
-
- macro_rules! sign_msg {
- ($unsigned_msg: expr) => {
- let msghash = Message::from_slice(&Sha256dHash::hash(&$unsigned_msg.encode()[..])[..]).unwrap();
- let as_bitcoin_sig = secp_ctx.sign(&msghash, &as_chan.get_signer().inner.funding_key);
- let bs_bitcoin_sig = secp_ctx.sign(&msghash, &bs_chan.get_signer().inner.funding_key);
- let as_node_sig = secp_ctx.sign(&msghash, &nodes[0].keys_manager.get_node_secret());
- let bs_node_sig = secp_ctx.sign(&msghash, &nodes[1].keys_manager.get_node_secret());
- chan_announcement = msgs::ChannelAnnouncement {
- node_signature_1 : if were_node_one { as_node_sig } else { bs_node_sig},
- node_signature_2 : if were_node_one { bs_node_sig } else { as_node_sig},
- bitcoin_signature_1: if were_node_one { as_bitcoin_sig } else { bs_bitcoin_sig },
- bitcoin_signature_2 : if were_node_one { bs_bitcoin_sig } else { as_bitcoin_sig },
- contents: $unsigned_msg
- }
- }
- }
-
- let unsigned_msg = dummy_unsigned_msg!();
- sign_msg!(unsigned_msg);
- assert_eq!(nodes[0].net_graph_msg_handler.handle_channel_announcement(&chan_announcement).unwrap(), true);
- let _ = nodes[0].net_graph_msg_handler.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id : as_chan.get_short_channel_id().unwrap(), is_permanent: false } );
-
- // Configured with Network::Testnet
- let mut unsigned_msg = dummy_unsigned_msg!();
- unsigned_msg.chain_hash = genesis_block(Network::Bitcoin).header.block_hash();
- sign_msg!(unsigned_msg);
- assert!(nodes[0].net_graph_msg_handler.handle_channel_announcement(&chan_announcement).is_err());
-
- let mut unsigned_msg = dummy_unsigned_msg!();
- unsigned_msg.chain_hash = BlockHash::hash(&[1,2,3,4,5,6,7,8,9]);
- sign_msg!(unsigned_msg);
- assert!(nodes[0].net_graph_msg_handler.handle_channel_announcement(&chan_announcement).is_err());
-}
-
#[test]
fn test_no_txn_manager_serialize_deserialize() {
let chanmon_cfgs = create_chanmon_cfgs(2);
send_payment(&nodes[0], &[&nodes[1]], 1000000);
}
+#[test]
+fn mpp_failure() {
+ let chanmon_cfgs = create_chanmon_cfgs(4);
+ let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+ let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+ let chan_1_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let chan_2_id = create_announced_chan_between_nodes(&nodes, 0, 2, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let chan_3_id = create_announced_chan_between_nodes(&nodes, 1, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let chan_4_id = create_announced_chan_between_nodes(&nodes, 2, 3, InitFeatures::known(), InitFeatures::known()).0.contents.short_channel_id;
+ let logger = test_utils::TestLogger::new();
+
+ let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[3]);
+ let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
+ let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let path = route.paths[0].clone();
+ route.paths.push(path);
+ route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
+ route.paths[0][0].short_channel_id = chan_1_id;
+ route.paths[0][1].short_channel_id = chan_3_id;
+ route.paths[1][0].pubkey = nodes[2].node.get_our_node_id();
+ route.paths[1][0].short_channel_id = chan_2_id;
+ route.paths[1][1].short_channel_id = chan_4_id;
+ send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], 200_000, payment_hash, payment_secret);
+ fail_payment_along_route(&nodes[0], &[&[&nodes[1], &nodes[3]], &[&nodes[2], &nodes[3]]], false, payment_hash);
+}
+
#[test]
fn test_dup_htlc_onchain_fails_on_reload() {
// When a Channel is closed, any outbound HTLCs which were relayed through it are simply
// We reduce the final CLTV here by a somewhat arbitrary constant to keep it under the one-byte
// script push size limit so that the below script length checks match
// ACCEPTED_HTLC_SCRIPT_WEIGHT.
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph,
&nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 900000, TEST_FINAL_CLTV - 40, nodes[0].logger).unwrap();
send_along_route_with_secret(&nodes[0], route, &[&[&nodes[1], &nodes[2], &nodes[3]]], 900000, duplicate_payment_hash, payment_secret);
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
{
commitment_signed_dance!(nodes[0], nodes[1], &htlc_updates.commitment_signed, false, true);
- expect_payment_failure_chan_update!(nodes[0], chan_2.0.contents.short_channel_id, true);
}
- expect_payment_failed!(nodes[0], duplicate_payment_hash, false);
+ expect_payment_failed_with_update!(nodes[0], duplicate_payment_hash, false, chan_2.0.contents.short_channel_id, true);
// Solve 2nd HTLC by broadcasting on B's chain HTLC-Success Tx from C
// Note that the fee paid is effectively double as the HTLC value (including the nodes[1] fee
let (_, payment_hash_2, _) = route_payment(&nodes[0], &[&nodes[2], &nodes[3], &nodes[4]], ds_dust_limit*1000); // not added < dust limit + HTLC tx fee
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
let our_node_id = &nodes[1].node.get_our_node_id();
- let route = get_route(our_node_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), ds_dust_limit*1000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(our_node_id, &net_graph_msg_handler.network_graph, &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), ds_dust_limit*1000, TEST_FINAL_CLTV, &logger).unwrap();
// 2nd HTLC:
send_along_route_with_secret(&nodes[1], route.clone(), &[&[&nodes[2], &nodes[3], &nodes[5]]], ds_dust_limit*1000, payment_hash_1, nodes[5].node.create_inbound_payment_for_hash(payment_hash_1, None, 7200, 0).unwrap()); // not added < dust limit + HTLC tx fee
// 3rd HTLC:
let (_, payment_hash_3, _) = route_payment(&nodes[0], &[&nodes[2], &nodes[3], &nodes[4]], 1000000);
// 5th HTLC:
let (_, payment_hash_4, _) = route_payment(&nodes[0], &[&nodes[2], &nodes[3], &nodes[4]], 1000000);
- let route = get_route(our_node_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(our_node_id, &net_graph_msg_handler.network_graph, &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
// 6th HTLC:
send_along_route_with_secret(&nodes[1], route.clone(), &[&[&nodes[2], &nodes[3], &nodes[5]]], 1000000, payment_hash_3, nodes[5].node.create_inbound_payment_for_hash(payment_hash_3, None, 7200, 0).unwrap());
// 7th HTLC:
// 8th HTLC:
let (_, payment_hash_5, _) = route_payment(&nodes[0], &[&nodes[2], &nodes[3], &nodes[4]], 1000000);
// 9th HTLC:
- let route = get_route(our_node_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), ds_dust_limit*1000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(our_node_id, &net_graph_msg_handler.network_graph, &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), ds_dust_limit*1000, TEST_FINAL_CLTV, &logger).unwrap();
send_along_route_with_secret(&nodes[1], route, &[&[&nodes[2], &nodes[3], &nodes[5]]], ds_dust_limit*1000, payment_hash_5, nodes[5].node.create_inbound_payment_for_hash(payment_hash_5, None, 7200, 0).unwrap()); // not added < dust limit + HTLC tx fee
// 10th HTLC:
let (_, payment_hash_6, _) = route_payment(&nodes[0], &[&nodes[2], &nodes[3], &nodes[4]], ds_dust_limit*1000); // not added < dust limit + HTLC tx fee
// 11th HTLC:
- let route = get_route(our_node_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(our_node_id, &net_graph_msg_handler.network_graph, &nodes[5].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 1000000, TEST_FINAL_CLTV, &logger).unwrap();
send_along_route_with_secret(&nodes[1], route, &[&[&nodes[2], &nodes[3], &nodes[5]]], 1000000, payment_hash_6, nodes[5].node.create_inbound_payment_for_hash(payment_hash_6, None, 7200, 0).unwrap());
// Double-check that six of the new HTLC were added
let as_events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(as_events.len(), if announce_latest { 5 } else { 3 });
let mut as_failds = HashSet::new();
+ let mut as_updates = 0;
for event in as_events.iter() {
- if let &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, .. } = event {
+ if let &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, .. } = event {
assert!(as_failds.insert(*payment_hash));
if *payment_hash != payment_hash_2 {
assert_eq!(*rejected_by_dest, deliver_last_raa);
} else {
assert!(!rejected_by_dest);
}
+ if network_update.is_some() {
+ as_updates += 1;
+ }
} else { panic!("Unexpected event"); }
}
assert!(as_failds.contains(&payment_hash_1));
let bs_events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(bs_events.len(), if announce_latest { 4 } else { 3 });
let mut bs_failds = HashSet::new();
+ let mut bs_updates = 0;
for event in bs_events.iter() {
- if let &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, .. } = event {
+ if let &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, .. } = event {
assert!(bs_failds.insert(*payment_hash));
if *payment_hash != payment_hash_1 && *payment_hash != payment_hash_5 {
assert_eq!(*rejected_by_dest, deliver_last_raa);
} else {
assert!(!rejected_by_dest);
}
+ if network_update.is_some() {
+ bs_updates += 1;
+ }
} else { panic!("Unexpected event"); }
}
assert!(bs_failds.contains(&payment_hash_1));
assert!(bs_failds.contains(&payment_hash_5));
// For each HTLC which was not failed-back by normal process (ie deliver_last_raa), we should
- // get a PaymentFailureNetworkUpdate. A should have gotten 4 HTLCs which were failed-back due
- // to unknown-preimage-etc, B should have gotten 2. Thus, in the
- // announce_latest && deliver_last_raa case, we should have 5-4=1 and 4-2=2
- // PaymentFailureNetworkUpdates.
- let as_msg_events = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(as_msg_events.len(), if deliver_last_raa { 1 } else if !announce_latest { 3 } else { 5 });
- let bs_msg_events = nodes[1].node.get_and_clear_pending_msg_events();
- assert_eq!(bs_msg_events.len(), if deliver_last_raa { 2 } else if !announce_latest { 3 } else { 4 });
- for event in as_msg_events.iter().chain(bs_msg_events.iter()) {
- match event {
- &MessageSendEvent::PaymentFailureNetworkUpdate { .. } => {},
- _ => panic!("Unexpected event"),
- }
- }
+ // get a NetworkUpdate. A should have gotten 4 HTLCs which were failed-back due to
+ // unknown-preimage-etc, B should have gotten 2. Thus, in the
+ // announce_latest && deliver_last_raa case, we should have 5-4=1 and 4-2=2 NetworkUpdates.
+ assert_eq!(as_updates, if deliver_last_raa { 1 } else if !announce_latest { 3 } else { 5 });
+ assert_eq!(bs_updates, if deliver_last_raa { 2 } else if !announce_latest { 3 } else { 4 });
}
#[test]
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), if use_dust { 50000 } else { 3000000 }, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), if use_dust { 50000 } else { 3000000 }, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let max_can_send = 5000000 - channel_reserve - 2*commit_tx_fee_msat(feerate, 1 + 1);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], max_can_send, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], max_can_send, TEST_FINAL_CLTV, &logger).unwrap();
// Send a payment which passes reserve checks but gets stuck in the holding cell.
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match &events[0] {
- &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref error_code, ref error_data } => {
+ &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed } => {
assert_eq!(our_payment_hash.clone(), *payment_hash);
assert_eq!(*rejected_by_dest, false);
+ assert_eq!(*all_paths_failed, true);
+ assert_eq!(*network_update, None);
assert_eq!(*error_code, None);
assert_eq!(*error_data, None);
},
let (_, payment_hash_2, payment_secret_2) = get_payment_preimage_hash!(nodes[1]);
let amt_2 = 5000000 - channel_reserve - 2*commit_tx_fee_msat(feerate, 2 + 1) - amt_1;
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route_1 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], amt_1, TEST_FINAL_CLTV, &logger).unwrap();
- let route_2 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], amt_2, TEST_FINAL_CLTV, &logger).unwrap();
+ let route_1 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], amt_1, TEST_FINAL_CLTV, &logger).unwrap();
+ let route_2 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], amt_2, TEST_FINAL_CLTV, &logger).unwrap();
// Send 2 payments which pass reserve checks but get stuck in the holding cell.
nodes[0].node.send_payment(&route_1, payment_hash_1, &Some(payment_secret_1)).unwrap();
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match &events[0] {
- &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref error_code, ref error_data } => {
+ &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref error_code, ref error_data, ref all_paths_failed } => {
assert_eq!(payment_hash_2.clone(), *payment_hash);
assert_eq!(*rejected_by_dest, false);
+ assert_eq!(*all_paths_failed, true);
+ assert_eq!(*network_update, None);
assert_eq!(*error_code, None);
assert_eq!(*error_data, None);
},
let max_can_send = 5000000 - channel_reserve - 2*commit_tx_fee_msat(feerate, 1 + 1) - total_routing_fee_msat;
let payment_event = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], max_can_send, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], max_can_send, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
_ => panic!("Unexpected event"),
};
nodes[0].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &raa);
- expect_payment_failure_chan_update!(nodes[0], chan_1_2.0.contents.short_channel_id, false);
- expect_payment_failed!(nodes[0], our_payment_hash, false);
+ expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_1_2.0.contents.short_channel_id, false);
check_added_monitors!(nodes[0], 1);
}
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
let logger = test_utils::TestLogger::new();
- let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
route.paths[0][0].fee_msat = 100;
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::ChannelUnavailable { ref err },
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
let logger = test_utils::TestLogger::new();
- let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
route.paths[0][0].fee_msat = 0;
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::ChannelUnavailable { ref err },
assert_eq!(err, "Cannot send 0-msat HTLC"));
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
let logger = test_utils::TestLogger::new();
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000000, 500000001, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000000, 500000001, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::RouteError { ref err },
assert_eq!(err, &"Channel CLTV overflowed?"));
}
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let payment_event = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
}
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)), true, APIError::ChannelUnavailable { ref err },
assert!(regex::Regex::new(r"Cannot push more than their max accepted HTLCs \(\d+\)").unwrap().is_match(err)));
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
let logger = test_utils::TestLogger::new();
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], htlc_minimum_msat, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], htlc_minimum_msat, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let max_can_send = 5000000 - channel_reserve - commit_tx_fee_outbound;
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], max_can_send, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], max_can_send, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 3999999, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 3999999, TEST_FINAL_CLTV, &logger).unwrap();
let cur_height = nodes[0].node.best_block.read().unwrap().height() + 1;
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::known(), InitFeatures::known());
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let chan = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[1]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 1000000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
//First hop
let mut payment_event = {
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
// Create some initial channels
create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, TEST_FINAL_CLTV, nodes[0].logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, TEST_FINAL_CLTV, nodes[0].logger).unwrap();
let (_, our_payment_hash, _) = get_payment_preimage_hash!(nodes[0]);
let our_payment_secret = nodes[1].node.create_inbound_payment_for_hash(our_payment_hash, Some(100_000), 7200, 0).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
// to nodes[2], which should be rejected:
let (our_payment_preimage, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[2]);
let route = get_route(&nodes[0].node.get_our_node_id(),
- &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(),
+ &nodes[0].net_graph_msg_handler.network_graph,
&nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None,
&[&RouteHint(vec![RouteHintHop {
src_node_id: nodes[1].node.get_our_node_id(),
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_fail_updates.update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], htlc_fail_updates.commitment_signed, true, true);
- expect_payment_failed!(nodes[0], our_payment_hash, false);
- expect_payment_failure_chan_update!(nodes[0], nodes[2].node.list_channels()[0].short_channel_id.unwrap(), true);
+ expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, nodes[2].node.list_channels()[0].short_channel_id.unwrap(), true);
// Now disconnect nodes[1] from its peers and restart with accept_forwards_to_priv_channels set
// to true. Sadly there is currently no way to change it at runtime.
let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3000000, 30, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3000000, 30, &logger).unwrap();
send_along_route(&nodes[1], route, &vec!(&nodes[0])[..], 3000000);
let revoked_txn = get_local_commitment_txn!(nodes[0], chan.2);
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1000000, 59000000, InitFeatures::known(), InitFeatures::known());
// Lock HTLC in both directions (using a slightly lower CLTV delay to provide timely RBF bumps)
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph,
&nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3_000_000, 50, nodes[0].logger).unwrap();
let payment_preimage = send_along_route(&nodes[0], route, &[&nodes[1]], 3_000_000).0;
- let route = get_route(&nodes[1].node.get_our_node_id(), &nodes[1].net_graph_msg_handler.network_graph.read().unwrap(),
+ let route = get_route(&nodes[1].node.get_our_node_id(), &nodes[1].net_graph_msg_handler.network_graph,
&nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3_000_000, 50, nodes[0].logger).unwrap();
send_along_route(&nodes[1], route, &[&nodes[0]], 3_000_000);
// commitment transaction, we would have happily carried on and provided them the next
// commitment transaction based on one RAA forward. This would probably eventually have led to
// channel closure, but it would not have resulted in funds loss. Still, our
- // EnforcingSigner would have paniced as it doesn't like jumps into the future. Here, we
+ // EnforcingSigner would have panicked as it doesn't like jumps into the future. Here, we
// check simply that the channel is closed in response to such an RAA, but don't check whether
// we decide to punish our counterparty for revoking their funds (as we don't currently
// implement that).
let channel_id = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()).2;
let mut guard = nodes[0].node.channel_state.lock().unwrap();
- let keys = &guard.by_id.get_mut(&channel_id).unwrap().get_signer();
+ let keys = guard.by_id.get_mut(&channel_id).unwrap().get_signer();
+
const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1;
+
+ // Make signer believe we got a counterparty signature, so that it allows the revocation
+ keys.get_enforcement_state().last_holder_commitment -= 1;
let per_commitment_secret = keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER);
+
// Must revoke without gaps
+ keys.get_enforcement_state().last_holder_commitment -= 1;
keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);
+
+ keys.get_enforcement_state().last_holder_commitment -= 1;
let next_per_commitment_point = PublicKey::from_secret_key(&Secp256k1::new(),
&SecretKey::from_slice(&keys.release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(&nodes[3]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let mut route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[3].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
let path = route.paths[0].clone();
route.paths.push(path);
route.paths[0][0].pubkey = nodes[1].node.get_our_node_id();
let logger = test_utils::TestLogger::new();
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100_000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
{
let logger = test_utils::TestLogger::new();
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100_000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
let logger = test_utils::TestLogger::new();
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100_000, TEST_FINAL_CLTV, &logger).unwrap();
// All the below cases should end up being handled exactly identically, so we macro the
// resulting events.
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[0]);
{
let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
- let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3000000 , TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 3000000 , TEST_FINAL_CLTV, &logger).unwrap();
nodes[1].node.send_payment(&route, payment_hash, &Some(payment_secret)).unwrap();
}
check_added_monitors!(nodes[1], 1);
assert!(updates.update_fee.is_none());
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates.update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], updates.commitment_signed, true, true);
- expect_payment_failed!(nodes[0], payment_hash, false);
- expect_payment_failure_chan_update!(nodes[0], chan_announce.contents.short_channel_id, true);
+ expect_payment_failed_with_update!(nodes[0], payment_hash, false, chan_announce.contents.short_channel_id, true);
}
}
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
- let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
+ let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let payer_pubkey = nodes[0].node.get_our_node_id();
let payee_pubkey = nodes[1].node.get_our_node_id();
- let route = get_route(&payer_pubkey, &network_graph, &payee_pubkey, None,
+ let route = get_route(&payer_pubkey, network_graph, &payee_pubkey, None,
None, &vec![], 10000, 40,
nodes[0].logger).unwrap();
nodes[1].node.peer_connected(&payer_pubkey, &msgs::Init { features: InitFeatures::known() });
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1], InitFeatures::known(), InitFeatures::known());
- let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
+ let network_graph = &nodes[0].net_graph_msg_handler.network_graph;
let first_hops = nodes[0].node.list_usable_channels();
let route = get_keysend_route(&payer_pubkey, &network_graph, &payee_pubkey,
Some(&first_hops.iter().collect::<Vec<_>>()), &vec![], 10000, 40,
//! Further functional tests which test blockchain reorganizations.
-use chain::channelmonitor::ANTI_REORG_DELAY;
-use ln::{PaymentPreimage, PaymentHash};
+use chain::channelmonitor::{ANTI_REORG_DELAY, Balance};
+use chain::transaction::OutPoint;
+use ln::{channel, PaymentPreimage, PaymentHash};
+use ln::channelmanager::BREAKDOWN_TIMEOUT;
use ln::features::InitFeatures;
-use ln::msgs::{ChannelMessageHandler, HTLCFailChannelUpdate, ErrorAction};
+use ln::msgs::{ChannelMessageHandler, ErrorAction};
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
+use routing::network_graph::NetworkUpdate;
use routing::router::get_route;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
+use bitcoin::blockdata::script::Builder;
+use bitcoin::blockdata::opcodes;
+use bitcoin::secp256k1::Secp256k1;
+
use prelude::*;
use ln::functional_test_utils::*;
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_updates.update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], fail_updates.commitment_signed, true, true);
- expect_payment_failed!(nodes[0], payment_hash, false);
- expect_payment_failure_chan_update!(nodes[0], update_a.contents.short_channel_id, true);
+ expect_payment_failed_with_update!(nodes[0], payment_hash, false, update_a.contents.short_channel_id, true);
+}
+
+#[test]
+fn chanmon_claim_value_coop_close() {
+ // Tests `get_claimable_balances` returns the correct values across a simple cooperative claim.
+ // Specifically, this tests that the channel non-HTLC balances show up in
+ // `get_claimable_balances` until the cooperative claims have confirmed and generated a
+ // `SpendableOutputs` event, and no longer.
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) =
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 1_000_000, InitFeatures::known(), InitFeatures::known());
+ let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
+ assert_eq!(funding_outpoint.to_channel_id(), chan_id);
+
+ let chan_feerate = get_feerate!(nodes[0], chan_id) as u64;
+
+ assert_eq!(vec![Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: 1_000_000 - 1_000 - chan_feerate * channel::COMMITMENT_TX_BASE_WEIGHT / 1000
+ }],
+ nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+ assert_eq!(vec![Balance::ClaimableOnChannelClose { claimable_amount_satoshis: 1_000, }],
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ nodes[0].node.close_channel(&chan_id).unwrap();
+ let node_0_shutdown = get_event_msg!(nodes[0], MessageSendEvent::SendShutdown, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_shutdown(&nodes[0].node.get_our_node_id(), &InitFeatures::known(), &node_0_shutdown);
+ let node_1_shutdown = get_event_msg!(nodes[1], MessageSendEvent::SendShutdown, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_shutdown(&nodes[1].node.get_our_node_id(), &InitFeatures::known(), &node_1_shutdown);
+
+ let node_0_closing_signed = get_event_msg!(nodes[0], MessageSendEvent::SendClosingSigned, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_closing_signed);
+ let node_1_closing_signed = get_event_msg!(nodes[1], MessageSendEvent::SendClosingSigned, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_closing_signed(&nodes[1].node.get_our_node_id(), &node_1_closing_signed);
+ let (_, node_0_2nd_closing_signed) = get_closing_signed_broadcast!(nodes[0].node, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_closing_signed(&nodes[0].node.get_our_node_id(), &node_0_2nd_closing_signed.unwrap());
+ let (_, node_1_none) = get_closing_signed_broadcast!(nodes[1].node, nodes[0].node.get_our_node_id());
+ assert!(node_1_none.is_none());
+
+ let shutdown_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(shutdown_tx, nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0));
+ assert_eq!(shutdown_tx.len(), 1);
+
+ mine_transaction(&nodes[0], &shutdown_tx[0]);
+ mine_transaction(&nodes[1], &shutdown_tx[0]);
+
+ assert!(nodes[0].node.list_channels().is_empty());
+ assert!(nodes[1].node.list_channels().is_empty());
+
+ assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
+ assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
+
+ assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 1_000_000 - 1_000 - chan_feerate * channel::COMMITMENT_TX_BASE_WEIGHT / 1000,
+ confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
+ }],
+ nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+ assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 1000,
+ confirmation_height: nodes[1].best_block_info().1 + ANTI_REORG_DELAY - 1,
+ }],
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
+ connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
+
+ assert_eq!(Vec::<Balance>::new(),
+ nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+ assert_eq!(Vec::<Balance>::new(),
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(node_a_spendable.len(), 1);
+ if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ check_spends!(spend_tx, shutdown_tx[0]);
+ }
+
+ let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(node_b_spendable.len(), 1);
+ if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() {
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ check_spends!(spend_tx, shutdown_tx[0]);
+ }
+}
+
+fn sorted_vec<T: Ord>(mut v: Vec<T>) -> Vec<T> {
+ v.sort_unstable();
+ v
+}
+
+fn do_test_claim_value_force_close(prev_commitment_tx: bool) {
+ // Tests `get_claimable_balances` with an HTLC across a force-close.
+ // We build a channel with an HTLC pending, then force close the channel and check that the
+ // `get_claimable_balances` return value is correct as transactions confirm on-chain.
+ let mut chanmon_cfgs = create_chanmon_cfgs(2);
+ if prev_commitment_tx {
+ // We broadcast a second-to-latest commitment transaction, without providing the revocation
+ // secret to the counterparty. However, because we always immediately take the revocation
+ // secret from the keys_manager, we would panic at broadcast as we're trying to sign a
+ // transaction which, from the point of view of our keys_manager, is revoked.
+ chanmon_cfgs[1].keys_manager.disable_revocation_policy_check = true;
+ }
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, chan_id, funding_tx) =
+ create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 1_000_000, InitFeatures::known(), InitFeatures::known());
+ let funding_outpoint = OutPoint { txid: funding_tx.txid(), index: 0 };
+ assert_eq!(funding_outpoint.to_channel_id(), chan_id);
+
+ // This HTLC is immediately claimed, giving node B the preimage
+ let payment_preimage = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
+ // This HTLC is allowed to time out, letting A claim it. However, in order to test claimable
+ // balances more fully we also give B the preimage for this HTLC.
+ let (timeout_payment_preimage, timeout_payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 4_000_000);
+ // This HTLC will be dust, and not be claimable at all:
+ let (dust_payment_preimage, dust_payment_hash, _) = route_payment(&nodes[0], &[&nodes[1]], 3_000);
+
+ let htlc_cltv_timeout = nodes[0].best_block_info().1 + TEST_FINAL_CLTV + 1; // Note ChannelManager adds one to CLTV timeouts for safety
+
+ let chan_feerate = get_feerate!(nodes[0], chan_id) as u64;
+
+ let remote_txn = get_local_commitment_txn!(nodes[1], chan_id);
+ // Before B receives the payment preimage, it only suggests the push_msat value of 1_000 sats
+ // as claimable. A lists both its to-self balance and the (possibly-claimable) HTLCs.
+ assert_eq!(sorted_vec(vec![Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - chan_feerate *
+ (channel::COMMITMENT_TX_BASE_WEIGHT + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 3_000,
+ claimable_height: htlc_cltv_timeout,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+ assert_eq!(vec![Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: 1_000,
+ }],
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ nodes[1].node.claim_funds(payment_preimage);
+ check_added_monitors!(nodes[1], 1);
+ let b_htlc_msgs = get_htlc_update_msgs!(&nodes[1], nodes[0].node.get_our_node_id());
+ // We claim the dust payment here as well, but it won't impact our claimable balances as its
+ // dust and thus doesn't appear on chain at all.
+ nodes[1].node.claim_funds(dust_payment_preimage);
+ check_added_monitors!(nodes[1], 1);
+ nodes[1].node.claim_funds(timeout_payment_preimage);
+ check_added_monitors!(nodes[1], 1);
+
+ if prev_commitment_tx {
+ // To build a previous commitment transaction, deliver one round of commitment messages.
+ nodes[0].node.handle_update_fulfill_htlc(&nodes[1].node.get_our_node_id(), &b_htlc_msgs.update_fulfill_htlcs[0]);
+ expect_payment_sent!(nodes[0], payment_preimage);
+ nodes[0].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &b_htlc_msgs.commitment_signed);
+ check_added_monitors!(nodes[0], 1);
+ let (as_raa, as_cs) = get_revoke_commit_msgs!(nodes[0], nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(), &as_raa);
+ let _htlc_updates = get_htlc_update_msgs!(&nodes[1], nodes[0].node.get_our_node_id());
+ check_added_monitors!(nodes[1], 1);
+ nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &as_cs);
+ let _bs_raa = get_event_msg!(nodes[1], MessageSendEvent::SendRevokeAndACK, nodes[0].node.get_our_node_id());
+ check_added_monitors!(nodes[1], 1);
+ }
+
+ // Once B has received the payment preimage, it includes the value of the HTLC in its
+ // "claimable if you were to close the channel" balance.
+ let mut a_expected_balances = vec![Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: 1_000_000 - // Channel funding value in satoshis
+ 4_000 - // The to-be-failed HTLC value in satoshis
+ 3_000 - // The claimed HTLC value in satoshis
+ 1_000 - // The push_msat value in satoshis
+ 3 - // The dust HTLC value in satoshis
+ // The commitment transaction fee with two HTLC outputs:
+ chan_feerate * (channel::COMMITMENT_TX_BASE_WEIGHT +
+ if prev_commitment_tx { 1 } else { 2 } *
+ channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ }];
+ if !prev_commitment_tx {
+ a_expected_balances.push(Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 3_000,
+ claimable_height: htlc_cltv_timeout,
+ });
+ }
+ assert_eq!(sorted_vec(a_expected_balances),
+ sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+ assert_eq!(vec![Balance::ClaimableOnChannelClose {
+ claimable_amount_satoshis: 1_000 + 3_000 + 4_000,
+ }],
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ // Broadcast the closing transaction (which has both pending HTLCs in it) and get B's
+ // broadcasted HTLC claim transaction with preimage.
+ let node_b_commitment_claimable = nodes[1].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
+ mine_transaction(&nodes[0], &remote_txn[0]);
+ mine_transaction(&nodes[1], &remote_txn[0]);
+
+ let b_broadcast_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(b_broadcast_txn.len(), if prev_commitment_tx { 4 } else { 5 });
+ if prev_commitment_tx {
+ check_spends!(b_broadcast_txn[3], b_broadcast_txn[2]);
+ } else {
+ assert_eq!(b_broadcast_txn[0], b_broadcast_txn[3]);
+ assert_eq!(b_broadcast_txn[1], b_broadcast_txn[4]);
+ }
+ // b_broadcast_txn[0] should spend the HTLC output of the commitment tx for 3_000 sats
+ check_spends!(b_broadcast_txn[0], remote_txn[0]);
+ check_spends!(b_broadcast_txn[1], remote_txn[0]);
+ assert_eq!(b_broadcast_txn[0].input.len(), 1);
+ assert_eq!(b_broadcast_txn[1].input.len(), 1);
+ assert_eq!(remote_txn[0].output[b_broadcast_txn[0].input[0].previous_output.vout as usize].value, 3_000);
+ assert_eq!(remote_txn[0].output[b_broadcast_txn[1].input[0].previous_output.vout as usize].value, 4_000);
+ check_spends!(b_broadcast_txn[2], funding_tx);
+
+ assert!(nodes[0].node.list_channels().is_empty());
+ check_closed_broadcast!(nodes[0], true);
+ check_added_monitors!(nodes[0], 1);
+ assert!(nodes[1].node.list_channels().is_empty());
+ check_closed_broadcast!(nodes[1], true);
+ check_added_monitors!(nodes[1], 1);
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+ assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
+
+ // Once the commitment transaction confirms, we will wait until ANTI_REORG_DELAY until we
+ // generate any `SpendableOutputs` events. Thus, the same balances will still be listed
+ // available in `get_claimable_balances`. However, both will swap from `ClaimableOnClose` to
+ // other Balance variants, as close has already happened.
+ assert!(nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
+ assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
+
+ assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 1_000_000 - 3_000 - 4_000 - 1_000 - 3 - chan_feerate *
+ (channel::COMMITMENT_TX_BASE_WEIGHT + 2 * channel::COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000,
+ confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 3_000,
+ claimable_height: htlc_cltv_timeout,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+ // The main non-HTLC balance is just awaiting confirmations, but the claimable height is the
+ // CSV delay, not ANTI_REORG_DELAY.
+ assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 1_000,
+ confirmation_height: node_b_commitment_claimable,
+ },
+ // Both HTLC balances are "contentious" as our counterparty could claim them if we wait too
+ // long.
+ Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 3_000,
+ timeout_height: htlc_cltv_timeout,
+ }, Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
+ expect_payment_failed!(nodes[0], dust_payment_hash, true);
+ connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
+
+ // After ANTI_REORG_DELAY, A will consider its balance fully spendable and generate a
+ // `SpendableOutputs` event. However, B still has to wait for the CSV delay.
+ assert_eq!(sorted_vec(vec![Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 3_000,
+ claimable_height: htlc_cltv_timeout,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+ assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 1_000,
+ confirmation_height: node_b_commitment_claimable,
+ }, Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 3_000,
+ timeout_height: htlc_cltv_timeout,
+ }, Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+
+ let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(node_a_spendable.len(), 1);
+ if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ check_spends!(spend_tx, remote_txn[0]);
+ }
+
+ assert!(nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events().is_empty());
+
+ // After broadcasting the HTLC claim transaction, node A will still consider the HTLC
+ // possibly-claimable up to ANTI_REORG_DELAY, at which point it will drop it.
+ mine_transaction(&nodes[0], &b_broadcast_txn[0]);
+ if !prev_commitment_tx {
+ expect_payment_sent!(nodes[0], payment_preimage);
+ }
+ assert_eq!(sorted_vec(vec![Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 3_000,
+ claimable_height: htlc_cltv_timeout,
+ }, Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
+ assert_eq!(vec![Balance::MaybeClaimableHTLCAwaitingTimeout {
+ claimable_amount_satoshis: 4_000,
+ claimable_height: htlc_cltv_timeout,
+ }],
+ nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ // When the HTLC timeout output is spendable in the next block, A should broadcast it
+ connect_blocks(&nodes[0], htlc_cltv_timeout - nodes[0].best_block_info().1 - 1);
+ let a_broadcast_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
+ assert_eq!(a_broadcast_txn.len(), 3);
+ check_spends!(a_broadcast_txn[0], funding_tx);
+ assert_eq!(a_broadcast_txn[1].input.len(), 1);
+ check_spends!(a_broadcast_txn[1], remote_txn[0]);
+ assert_eq!(a_broadcast_txn[2].input.len(), 1);
+ check_spends!(a_broadcast_txn[2], remote_txn[0]);
+ assert_ne!(a_broadcast_txn[1].input[0].previous_output.vout,
+ a_broadcast_txn[2].input[0].previous_output.vout);
+ // a_broadcast_txn [1] and [2] should spend the HTLC outputs of the commitment tx
+ assert_eq!(remote_txn[0].output[a_broadcast_txn[1].input[0].previous_output.vout as usize].value, 3_000);
+ assert_eq!(remote_txn[0].output[a_broadcast_txn[2].input[0].previous_output.vout as usize].value, 4_000);
+
+ // Once the HTLC-Timeout transaction confirms, A will no longer consider the HTLC
+ // "MaybeClaimable", but instead move it to "AwaitingConfirmations".
+ mine_transaction(&nodes[0], &a_broadcast_txn[2]);
+ assert!(nodes[0].node.get_and_clear_pending_events().is_empty());
+ assert_eq!(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 4_000,
+ confirmation_height: nodes[0].best_block_info().1 + ANTI_REORG_DELAY - 1,
+ }],
+ nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+ // After ANTI_REORG_DELAY, A will generate a SpendableOutputs event and drop the claimable
+ // balance entry.
+ connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
+ assert_eq!(Vec::<Balance>::new(),
+ nodes[0].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+ expect_payment_failed!(nodes[0], timeout_payment_hash, true);
+
+ let mut node_a_spendable = nodes[0].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(node_a_spendable.len(), 1);
+ if let Event::SpendableOutputs { outputs } = node_a_spendable.pop().unwrap() {
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[0].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ check_spends!(spend_tx, a_broadcast_txn[2]);
+ } else { panic!(); }
+
+ // Node B will no longer consider the HTLC "contentious" after the HTLC claim transaction
+ // confirms, and consider it simply "awaiting confirmations". Note that it has to wait for the
+ // standard revocable transaction CSV delay before receiving a `SpendableOutputs`.
+ let node_b_htlc_claimable = nodes[1].best_block_info().1 + BREAKDOWN_TIMEOUT as u32;
+ mine_transaction(&nodes[1], &b_broadcast_txn[0]);
+
+ assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 1_000,
+ confirmation_height: node_b_commitment_claimable,
+ }, Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 3_000,
+ confirmation_height: node_b_htlc_claimable,
+ }, Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+
+ // After reaching the commitment output CSV, we'll get a SpendableOutputs event for it and have
+ // only the HTLCs claimable on node B.
+ connect_blocks(&nodes[1], node_b_commitment_claimable - nodes[1].best_block_info().1);
+
+ let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(node_b_spendable.len(), 1);
+ if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() {
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ check_spends!(spend_tx, remote_txn[0]);
+ }
+
+ assert_eq!(sorted_vec(vec![Balance::ClaimableAwaitingConfirmations {
+ claimable_amount_satoshis: 3_000,
+ confirmation_height: node_b_htlc_claimable,
+ }, Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ }]),
+ sorted_vec(nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances()));
+
+ // After reaching the claimed HTLC output CSV, we'll get a SpendableOutptus event for it and
+ // have only one HTLC output left spendable.
+ connect_blocks(&nodes[1], node_b_htlc_claimable - nodes[1].best_block_info().1);
+
+ let mut node_b_spendable = nodes[1].chain_monitor.chain_monitor.get_and_clear_pending_events();
+ assert_eq!(node_b_spendable.len(), 1);
+ if let Event::SpendableOutputs { outputs } = node_b_spendable.pop().unwrap() {
+ assert_eq!(outputs.len(), 1);
+ let spend_tx = nodes[1].keys_manager.backing.spend_spendable_outputs(&[&outputs[0]], Vec::new(),
+ Builder::new().push_opcode(opcodes::all::OP_RETURN).into_script(), 253, &Secp256k1::new()).unwrap();
+ check_spends!(spend_tx, b_broadcast_txn[0]);
+ } else { panic!(); }
+
+ assert_eq!(vec![Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ }],
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+
+ // Finally, mine the HTLC timeout transaction that A broadcasted (even though B should be able
+ // to claim this HTLC with the preimage it knows!). It will remain listed as a claimable HTLC
+ // until ANTI_REORG_DELAY confirmations on the spend.
+ mine_transaction(&nodes[1], &a_broadcast_txn[2]);
+ assert_eq!(vec![Balance::ContentiousClaimable {
+ claimable_amount_satoshis: 4_000,
+ timeout_height: htlc_cltv_timeout,
+ }],
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+ connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
+ assert_eq!(Vec::<Balance>::new(),
+ nodes[1].chain_monitor.chain_monitor.monitors.read().unwrap().get(&funding_outpoint).unwrap().get_claimable_balances());
+}
+
+#[test]
+fn test_claim_value_force_close() {
+ do_test_claim_value_force_close(true);
+ do_test_claim_value_force_close(false);
}
pub funding_txid: Txid,
/// The specific output index funding this channel
pub funding_output_index: u16,
- /// The signature of the channel initiator (funder) on the funding transaction
+ /// The signature of the channel initiator (funder) on the initial commitment transaction
pub signature: Signature,
}
pub struct FundingSigned {
/// The channel ID
pub channel_id: [u8; 32],
- /// The signature of the channel acceptor (fundee) on the funding transaction
+ /// The signature of the channel acceptor (fundee) on the initial commitment transaction
pub signature: Signature,
}
pub commitment_signed: CommitmentSigned,
}
-/// The information we received from a peer along the route of a payment we originated. This is
-/// returned by ChannelMessageHandler::handle_update_fail_htlc to be passed into
-/// RoutingMessageHandler::handle_htlc_fail_channel_update to update our network map.
-#[derive(Clone, Debug, PartialEq)]
-pub enum HTLCFailChannelUpdate {
- /// We received an error which included a full ChannelUpdate message.
- ChannelUpdateMessage {
- /// The unwrapped message we received
- msg: ChannelUpdate,
- },
- /// We received an error which indicated only that a channel has been closed
- ChannelClosed {
- /// The short_channel_id which has now closed.
- short_channel_id: u64,
- /// when this true, this channel should be permanently removed from the
- /// consideration. Otherwise, this channel can be restored as new channel_update is received
- is_permanent: bool,
- },
- /// We received an error which indicated only that a node has failed
- NodeFailure {
- /// The node_id that has failed.
- node_id: PublicKey,
- /// when this true, node should be permanently removed from the
- /// consideration. Otherwise, the channels connected to this node can be
- /// restored as new channel_update is received
- is_permanent: bool,
- }
-}
-
/// Messages could have optional fields to use with extended features
/// As we wish to serialize these differently from Option<T>s (Options get a tag byte, but
/// OptionalFeild simply gets Present if there are enough bytes to read into it), we have a
/// Handle an incoming channel_update message, returning true if it should be forwarded on,
/// false or returning an Err otherwise.
fn handle_channel_update(&self, msg: &ChannelUpdate) -> Result<bool, LightningError>;
- /// Handle some updates to the route graph that we learned due to an outbound failed payment.
- fn handle_htlc_fail_channel_update(&self, update: &HTLCFailChannelUpdate);
/// Gets a subset of the channel announcements and updates required to dump our routing table
/// to a remote node, starting at the short_channel_id indicated by starting_point and
/// including the batch_amount entries immediately higher in numerical value than starting_point.
}
-impl_writeable_len_match!(AcceptChannel, {
- {AcceptChannel{ shutdown_scriptpubkey: OptionalField::Present(ref script), .. }, 270 + 2 + script.len()},
- {_, 270}
- }, {
+impl_writeable_msg!(AcceptChannel, {
temporary_channel_id,
dust_limit_satoshis,
max_htlc_value_in_flight_msat,
htlc_basepoint,
first_per_commitment_point,
shutdown_scriptpubkey
-});
+}, {});
-impl_writeable!(AnnouncementSignatures, 32+8+64*2, {
+impl_writeable_msg!(AnnouncementSignatures, {
channel_id,
short_channel_id,
node_signature,
bitcoin_signature
-});
+}, {});
impl Writeable for ChannelReestablish {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(if let OptionalField::Present(..) = self.data_loss_protect { 32+2*8+33+32 } else { 32+2*8 });
self.channel_id.write(w)?;
self.next_local_commitment_number.write(w)?;
self.next_remote_commitment_number.write(w)?;
}
}
-impl Writeable for ClosingSigned {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(32 + 8 + 64 + if self.fee_range.is_some() { 1+1+ 2*8 } else { 0 });
- self.channel_id.write(w)?;
- self.fee_satoshis.write(w)?;
- self.signature.write(w)?;
- encode_tlv_stream!(w, {
- (1, self.fee_range, option),
- });
- Ok(())
- }
-}
+impl_writeable_msg!(ClosingSigned,
+ { channel_id, fee_satoshis, signature },
+ { (1, fee_range, option) }
+);
-impl Readable for ClosingSigned {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let channel_id = Readable::read(r)?;
- let fee_satoshis = Readable::read(r)?;
- let signature = Readable::read(r)?;
- let mut fee_range = None;
- decode_tlv_stream!(r, {
- (1, fee_range, option),
- });
- Ok(Self { channel_id, fee_satoshis, signature, fee_range })
- }
-}
-
-impl_writeable!(ClosingSignedFeeRange, 2*8, {
+impl_writeable!(ClosingSignedFeeRange, {
min_fee_satoshis,
max_fee_satoshis
});
-impl_writeable_len_match!(CommitmentSigned, {
- { CommitmentSigned { ref htlc_signatures, .. }, 32+64+2+htlc_signatures.len()*64 }
- }, {
+impl_writeable_msg!(CommitmentSigned, {
channel_id,
signature,
htlc_signatures
-});
+}, {});
-impl_writeable_len_match!(DecodedOnionErrorPacket, {
- { DecodedOnionErrorPacket { ref failuremsg, ref pad, .. }, 32 + 4 + failuremsg.len() + pad.len() }
- }, {
+impl_writeable!(DecodedOnionErrorPacket, {
hmac,
failuremsg,
pad
});
-impl_writeable!(FundingCreated, 32+32+2+64, {
+impl_writeable_msg!(FundingCreated, {
temporary_channel_id,
funding_txid,
funding_output_index,
signature
-});
+}, {});
-impl_writeable!(FundingSigned, 32+64, {
+impl_writeable_msg!(FundingSigned, {
channel_id,
signature
-});
+}, {});
-impl_writeable!(FundingLocked, 32+33, {
+impl_writeable_msg!(FundingLocked, {
channel_id,
- next_per_commitment_point
-});
+ next_per_commitment_point,
+}, {});
impl Writeable for Init {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
}
}
-impl_writeable_len_match!(OpenChannel, {
- { OpenChannel { shutdown_scriptpubkey: OptionalField::Present(ref script), .. }, 319 + 2 + script.len() },
- { _, 319 }
- }, {
+impl_writeable_msg!(OpenChannel, {
chain_hash,
temporary_channel_id,
funding_satoshis,
first_per_commitment_point,
channel_flags,
shutdown_scriptpubkey
-});
+}, {});
-impl_writeable!(RevokeAndACK, 32+32+33, {
+impl_writeable_msg!(RevokeAndACK, {
channel_id,
per_commitment_secret,
next_per_commitment_point
-});
+}, {});
-impl_writeable_len_match!(Shutdown, {
- { Shutdown { ref scriptpubkey, .. }, 32 + 2 + scriptpubkey.len() }
- }, {
+impl_writeable_msg!(Shutdown, {
channel_id,
scriptpubkey
-});
+}, {});
-impl_writeable_len_match!(UpdateFailHTLC, {
- { UpdateFailHTLC { ref reason, .. }, 32 + 10 + reason.data.len() }
- }, {
+impl_writeable_msg!(UpdateFailHTLC, {
channel_id,
htlc_id,
reason
-});
+}, {});
-impl_writeable!(UpdateFailMalformedHTLC, 32+8+32+2, {
+impl_writeable_msg!(UpdateFailMalformedHTLC, {
channel_id,
htlc_id,
sha256_of_onion,
failure_code
-});
+}, {});
-impl_writeable!(UpdateFee, 32+4, {
+impl_writeable_msg!(UpdateFee, {
channel_id,
feerate_per_kw
-});
+}, {});
-impl_writeable!(UpdateFulfillHTLC, 32+8+32, {
+impl_writeable_msg!(UpdateFulfillHTLC, {
channel_id,
htlc_id,
payment_preimage
-});
+}, {});
-impl_writeable_len_match!(OnionErrorPacket, {
- { OnionErrorPacket { ref data, .. }, 2 + data.len() }
- }, {
+// Note that this is written as a part of ChannelManager objects, and thus cannot change its
+// serialization format in a way which assumes we know the total serialized length/message end
+// position.
+impl_writeable!(OnionErrorPacket, {
data
});
+// Note that this is written as a part of ChannelManager objects, and thus cannot change its
+// serialization format in a way which assumes we know the total serialized length/message end
+// position.
impl Writeable for OnionPacket {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(1 + 33 + 20*65 + 32);
self.version.write(w)?;
match self.public_key {
Ok(pubkey) => pubkey.write(w)?,
}
}
-impl_writeable!(UpdateAddHTLC, 32+8+8+32+4+1366, {
+impl_writeable_msg!(UpdateAddHTLC, {
channel_id,
htlc_id,
amount_msat,
payment_hash,
cltv_expiry,
onion_routing_packet
-});
+}, {});
impl Writeable for FinalOnionHopData {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(32 + 8 - (self.total_msat.leading_zeros()/8) as usize);
self.payment_secret.0.write(w)?;
HighZeroBytesDroppedVarInt(self.total_msat).write(w)
}
impl Writeable for OnionHopData {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(33);
// Note that this should never be reachable if Rust-Lightning generated the message, as we
// check values are sane long before we get here, though its possible in the future
// user-generated messages may hit this.
impl Writeable for Ping {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(self.byteslen as usize + 4);
self.ponglen.write(w)?;
vec![0u8; self.byteslen as usize].write(w)?; // size-unchecked write
Ok(())
impl Writeable for Pong {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(self.byteslen as usize + 2);
vec![0u8; self.byteslen as usize].write(w)?; // size-unchecked write
Ok(())
}
impl Writeable for UnsignedChannelAnnouncement {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(2 + 32 + 8 + 4*33 + self.features.byte_count() + self.excess_data.len());
self.features.write(w)?;
self.chain_hash.write(w)?;
self.short_channel_id.write(w)?;
}
}
-impl_writeable_len_match!(ChannelAnnouncement, {
- { ChannelAnnouncement { contents: UnsignedChannelAnnouncement {ref features, ref excess_data, ..}, .. },
- 2 + 32 + 8 + 4*33 + features.byte_count() + excess_data.len() + 4*64 }
- }, {
+impl_writeable!(ChannelAnnouncement, {
node_signature_1,
node_signature_2,
bitcoin_signature_1,
impl Writeable for UnsignedChannelUpdate {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- let mut size = 64 + self.excess_data.len();
let mut message_flags: u8 = 0;
if let OptionalField::Present(_) = self.htlc_maximum_msat {
- size += 8;
message_flags = 1;
}
- w.size_hint(size);
self.chain_hash.write(w)?;
self.short_channel_id.write(w)?;
self.timestamp.write(w)?;
}
}
-impl_writeable_len_match!(ChannelUpdate, {
- { ChannelUpdate { contents: UnsignedChannelUpdate {ref excess_data, ref htlc_maximum_msat, ..}, .. },
- 64 + 64 + excess_data.len() + if let OptionalField::Present(_) = htlc_maximum_msat { 8 } else { 0 } }
- }, {
+impl_writeable!(ChannelUpdate, {
signature,
contents
});
impl Writeable for ErrorMessage {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(32 + 2 + self.data.len());
self.channel_id.write(w)?;
(self.data.len() as u16).write(w)?;
w.write_all(self.data.as_bytes())?;
impl Writeable for UnsignedNodeAnnouncement {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(76 + self.features.byte_count() + self.addresses.len()*38 + self.excess_address_data.len() + self.excess_data.len());
self.features.write(w)?;
self.timestamp.write(w)?;
self.node_id.write(w)?;
}
}
-impl_writeable_len_match!(NodeAnnouncement, <=, {
- { NodeAnnouncement { contents: UnsignedNodeAnnouncement { ref features, ref addresses, ref excess_address_data, ref excess_data, ..}, .. },
- 64 + 76 + features.byte_count() + addresses.len()*(NetAddress::MAX_LEN as usize + 1) + excess_address_data.len() + excess_data.len() }
- }, {
+impl_writeable!(NodeAnnouncement, {
signature,
contents
});
// Calculated from 1-byte encoding_type plus 8-bytes per short_channel_id
let encoding_len: u16 = 1 + self.short_channel_ids.len() as u16 * 8;
- w.size_hint(32 + 2 + encoding_len as usize);
self.chain_hash.write(w)?;
encoding_len.write(w)?;
}
}
-impl Readable for ReplyShortChannelIdsEnd {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let chain_hash: BlockHash = Readable::read(r)?;
- let full_information: bool = Readable::read(r)?;
- Ok(ReplyShortChannelIdsEnd {
- chain_hash,
- full_information,
- })
- }
-}
-
-impl Writeable for ReplyShortChannelIdsEnd {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(32 + 1);
- self.chain_hash.write(w)?;
- self.full_information.write(w)?;
- Ok(())
- }
-}
+impl_writeable_msg!(ReplyShortChannelIdsEnd, {
+ chain_hash,
+ full_information,
+}, {});
impl QueryChannelRange {
/**
}
}
-impl Readable for QueryChannelRange {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let chain_hash: BlockHash = Readable::read(r)?;
- let first_blocknum: u32 = Readable::read(r)?;
- let number_of_blocks: u32 = Readable::read(r)?;
- Ok(QueryChannelRange {
- chain_hash,
- first_blocknum,
- number_of_blocks
- })
- }
-}
-
-impl Writeable for QueryChannelRange {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(32 + 4 + 4);
- self.chain_hash.write(w)?;
- self.first_blocknum.write(w)?;
- self.number_of_blocks.write(w)?;
- Ok(())
- }
-}
+impl_writeable_msg!(QueryChannelRange, {
+ chain_hash,
+ first_blocknum,
+ number_of_blocks
+}, {});
impl Readable for ReplyChannelRange {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
impl Writeable for ReplyChannelRange {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
let encoding_len: u16 = 1 + self.short_channel_ids.len() as u16 * 8;
- w.size_hint(32 + 4 + 4 + 1 + 2 + encoding_len as usize);
self.chain_hash.write(w)?;
self.first_blocknum.write(w)?;
self.number_of_blocks.write(w)?;
}
}
-impl Readable for GossipTimestampFilter {
- fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
- let chain_hash: BlockHash = Readable::read(r)?;
- let first_timestamp: u32 = Readable::read(r)?;
- let timestamp_range: u32 = Readable::read(r)?;
- Ok(GossipTimestampFilter {
- chain_hash,
- first_timestamp,
- timestamp_range,
- })
- }
-}
-
-impl Writeable for GossipTimestampFilter {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
- w.size_hint(32 + 4 + 4);
- self.chain_hash.write(w)?;
- self.first_timestamp.write(w)?;
- self.timestamp_range.write(w)?;
- Ok(())
- }
-}
-
+impl_writeable_msg!(GossipTimestampFilter, {
+ chain_hash,
+ first_timestamp,
+ timestamp_range,
+}, {});
#[cfg(test)]
mod tests {
use ln::channelmanager::{HTLCForwardInfo, CLTV_FAR_FAR_AWAY};
use ln::onion_utils;
use routing::router::{Route, get_route};
+use routing::network_graph::NetworkUpdate;
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
-use ln::msgs::{ChannelMessageHandler, ChannelUpdate, HTLCFailChannelUpdate, OptionalField};
+use ln::msgs::{ChannelMessageHandler, ChannelUpdate, OptionalField};
use util::test_utils;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use util::ser::{Writeable, Writer};
use ln::functional_test_utils::*;
-fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)
+fn run_onion_failure_test<F1,F2>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, callback_msg: F1, callback_node: F2, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>)
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
F2: FnMut(),
{
// 3: final node fails backward (but tamper onion payloads from node0)
// 100: trigger error in the intermediate node and tamper returning fail_htlc
// 200: trigger error in the final node and tamper returning fail_htlc
-fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<HTLCFailChannelUpdate>)
+fn run_onion_failure_test_with_fail_intercept<F1,F2,F3>(_name: &str, test_case: u8, nodes: &Vec<Node>, route: &Route, payment_hash: &PaymentHash, payment_secret: &PaymentSecret, mut callback_msg: F1, mut callback_fail: F2, mut callback_node: F3, expected_retryable: bool, expected_error_code: Option<u16>, expected_channel_update: Option<NetworkUpdate>)
where F1: for <'a> FnMut(&'a mut msgs::UpdateAddHTLC),
F2: for <'a> FnMut(&'a mut msgs::UpdateFailHTLC),
F3: FnMut(),
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
- if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref error_code, error_data: _ } = &events[0] {
+ if let &Event::PaymentFailed { payment_hash:_, ref rejected_by_dest, ref network_update, ref error_code, error_data: _, ref all_paths_failed } = &events[0] {
assert_eq!(*rejected_by_dest, !expected_retryable);
+ assert_eq!(*all_paths_failed, true);
assert_eq!(*error_code, expected_error_code);
- } else {
- panic!("Uexpected event");
- }
-
- let events = nodes[0].node.get_and_clear_pending_msg_events();
- if expected_channel_update.is_some() {
- assert_eq!(events.len(), 1);
- match events[0] {
- MessageSendEvent::PaymentFailureNetworkUpdate { ref update } => {
- match update {
- &HTLCFailChannelUpdate::ChannelUpdateMessage { .. } => {
- if let HTLCFailChannelUpdate::ChannelUpdateMessage { .. } = expected_channel_update.unwrap() {} else {
+ if expected_channel_update.is_some() {
+ match network_update {
+ Some(update) => match update {
+ &NetworkUpdate::ChannelUpdateMessage { .. } => {
+ if let NetworkUpdate::ChannelUpdateMessage { .. } = expected_channel_update.unwrap() {} else {
panic!("channel_update not found!");
}
},
- &HTLCFailChannelUpdate::ChannelClosed { ref short_channel_id, ref is_permanent } => {
- if let HTLCFailChannelUpdate::ChannelClosed { short_channel_id: ref expected_short_channel_id, is_permanent: ref expected_is_permanent } = expected_channel_update.unwrap() {
+ &NetworkUpdate::ChannelClosed { ref short_channel_id, ref is_permanent } => {
+ if let NetworkUpdate::ChannelClosed { short_channel_id: ref expected_short_channel_id, is_permanent: ref expected_is_permanent } = expected_channel_update.unwrap() {
assert!(*short_channel_id == *expected_short_channel_id);
assert!(*is_permanent == *expected_is_permanent);
} else {
panic!("Unexpected message event");
}
},
- &HTLCFailChannelUpdate::NodeFailure { ref node_id, ref is_permanent } => {
- if let HTLCFailChannelUpdate::NodeFailure { node_id: ref expected_node_id, is_permanent: ref expected_is_permanent } = expected_channel_update.unwrap() {
+ &NetworkUpdate::NodeFailure { ref node_id, ref is_permanent } => {
+ if let NetworkUpdate::NodeFailure { node_id: ref expected_node_id, is_permanent: ref expected_is_permanent } = expected_channel_update.unwrap() {
assert!(*node_id == *expected_node_id);
assert!(*is_permanent == *expected_is_permanent);
} else {
}
},
}
- },
- _ => panic!("Unexpected message event"),
+ None => panic!("Expected channel update"),
+ }
+ } else {
+ assert!(network_update.is_none());
}
} else {
- assert_eq!(events.len(), 0);
+ panic!("Unexpected event");
}
}
let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
let channels = [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 logger = test_utils::TestLogger::new();
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40_000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40_000, TEST_FINAL_CLTV, &logger).unwrap();
// positive case
let (payment_preimage_success, payment_hash_success, payment_secret_success) = get_payment_preimage_hash!(nodes[2]);
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
run_onion_failure_test("fee_insufficient", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
msg.amount_msat -= 1;
- }, || {}, true, Some(UPDATE|12), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+ }, || {}, true, Some(UPDATE|12), Some(NetworkUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
// In an earlier version, we spuriously failed to forward payments if the expected feerate
// changed between the channel open and the payment.
let channels = [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 (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
let logger = test_utils::TestLogger::new();
- let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &nodes[0].net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 40000, TEST_FINAL_CLTV, &logger).unwrap();
// positve case
send_payment(&nodes[0], &vec!(&nodes[1], &nodes[2])[..], 40000);
// describing a length-1 TLV payload, which is obviously bogus.
new_payloads[0].data[0] = 1;
msg.onion_routing_packet = onion_utils::construct_onion_packet_bogus_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
- }, ||{}, true, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));//XXX incremented channels idx here
+ }, ||{}, true, Some(PERM|22), Some(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
// final node failure
run_onion_failure_test("invalid_realm", 3, &nodes, &route, &payment_hash, &payment_secret, |msg| {
// length-1 TLV payload, which is obviously bogus.
new_payloads[1].data[0] = 1;
msg.onion_routing_packet = onion_utils::construct_onion_packet_bogus_hopdata(new_payloads, onion_keys, [0; 32], &payment_hash);
- }, ||{}, false, Some(PERM|22), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+ }, ||{}, false, Some(PERM|22), Some(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
// the following three with run_onion_failure_test_with_fail_intercept() test only the origin node
// receiving simulated fail messages
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], NODE|2, &[0;0]);
- }, ||{}, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}));
+ }, ||{}, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: false}));
// final node failure
run_onion_failure_test_with_fail_intercept("temporary_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], NODE|2, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
- }, true, Some(NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}));
+ }, true, Some(NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: false}));
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
// intermediate node failure
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|NODE|2, &[0;0]);
- }, ||{}, true, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
+ }, ||{}, true, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
// final node failure
run_onion_failure_test_with_fail_intercept("permanent_node_failure", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], PERM|NODE|2, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
- }, false, Some(PERM|NODE|2), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
+ }, false, Some(PERM|NODE|2), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
// intermediate node failure
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|NODE|3, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
- }, true, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
+ }, true, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][0].pubkey, is_permanent: true}));
// final node failure
run_onion_failure_test_with_fail_intercept("required_node_feature_missing", 200, &nodes, &route, &payment_hash, &payment_secret, |_msg| {}, |msg| {
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[1].shared_secret[..], PERM|NODE|3, &[0;0]);
}, ||{
nodes[2].node.fail_htlc_backwards(&payment_hash);
- }, false, Some(PERM|NODE|3), Some(msgs::HTLCFailChannelUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
+ }, false, Some(PERM|NODE|3), Some(NetworkUpdate::NodeFailure{node_id: route.paths[0][1].pubkey, is_permanent: true}));
let (_, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
run_onion_failure_test("invalid_onion_version", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| { msg.onion_routing_packet.version = 1; }, ||{}, true,
let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], UPDATE|7, &ChannelUpdate::dummy().encode_with_len()[..]);
- }, ||{}, true, Some(UPDATE|7), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+ }, ||{}, true, Some(UPDATE|7), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
run_onion_failure_test_with_fail_intercept("permanent_channel_failure", 100, &nodes, &route, &payment_hash, &payment_secret, |msg| {
msg.amount_msat -= 1;
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|8, &[0;0]);
// short_channel_id from the processing node
- }, ||{}, true, Some(PERM|8), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+ }, ||{}, true, Some(PERM|8), Some(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
run_onion_failure_test_with_fail_intercept("required_channel_feature_missing", 100, &nodes, &route, &payment_hash, &payment_secret, |msg| {
msg.amount_msat -= 1;
let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
msg.reason = onion_utils::build_first_hop_failure_packet(&onion_keys[0].shared_secret[..], PERM|9, &[0;0]);
// short_channel_id from the processing node
- }, ||{}, true, Some(PERM|9), Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
+ }, ||{}, true, Some(PERM|9), Some(NetworkUpdate::ChannelClosed{short_channel_id: channels[1].0.contents.short_channel_id, is_permanent: true}));
let mut bogus_route = route.clone();
bogus_route.paths[0][1].short_channel_id -= 1;
run_onion_failure_test("unknown_next_peer", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(PERM|10),
- Some(msgs::HTLCFailChannelUpdate::ChannelClosed{short_channel_id: bogus_route.paths[0][1].short_channel_id, is_permanent:true}));
+ Some(NetworkUpdate::ChannelClosed{short_channel_id: bogus_route.paths[0][1].short_channel_id, is_permanent:true}));
let amt_to_forward = nodes[1].node.channel_state.lock().unwrap().by_id.get(&channels[1].2).unwrap().get_counterparty_htlc_minimum_msat() - 1;
let mut bogus_route = route.clone();
let route_len = bogus_route.paths[0].len();
bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward;
- run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(UPDATE|11), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+ run_onion_failure_test("amount_below_minimum", 0, &nodes, &bogus_route, &payment_hash, &payment_secret, |_| {}, ||{}, true, Some(UPDATE|11), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
// Test a positive test-case with one extra msat, meeting the minimum.
bogus_route.paths[0][route_len-1].fee_msat = amt_to_forward + 1;
//invalid channel_update cases.
run_onion_failure_test("fee_insufficient", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
msg.amount_msat -= 1;
- }, || {}, true, Some(UPDATE|12), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+ }, || {}, true, Some(UPDATE|12), Some(NetworkUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
run_onion_failure_test("incorrect_cltv_expiry", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
// need to violate: cltv_expiry - cltv_expiry_delta >= outgoing_cltv_value
msg.cltv_expiry -= 1;
- }, || {}, true, Some(UPDATE|13), Some(msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
+ }, || {}, true, Some(UPDATE|13), Some(NetworkUpdate::ChannelClosed { short_channel_id: channels[0].0.contents.short_channel_id, is_permanent: true}));
run_onion_failure_test("expiry_too_soon", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
let height = msg.cltv_expiry - CLTV_CLAIM_BUFFER - LATENCY_GRACE_PERIOD_BLOCKS + 1;
connect_blocks(&nodes[0], height - nodes[0].best_block_info().1);
connect_blocks(&nodes[1], height - nodes[1].best_block_info().1);
connect_blocks(&nodes[2], height - nodes[2].best_block_info().1);
- }, ||{}, true, Some(UPDATE|14), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+ }, ||{}, true, Some(UPDATE|14), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
run_onion_failure_test("unknown_payment_hash", 2, &nodes, &route, &payment_hash, &payment_secret, |_| {}, || {
nodes[2].node.fail_htlc_backwards(&payment_hash);
// disconnect event to the channel between nodes[1] ~ nodes[2]
nodes[1].node.peer_disconnected(&nodes[2].node.get_our_node_id(), false);
nodes[2].node.peer_disconnected(&nodes[1].node.get_our_node_id(), false);
- }, true, Some(UPDATE|20), Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
+ }, true, Some(UPDATE|20), Some(NetworkUpdate::ChannelUpdateMessage{msg: ChannelUpdate::dummy()}));
reconnect_nodes(&nodes[1], &nodes[2], (false, false), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));
run_onion_failure_test("expiry_too_far", 0, &nodes, &route, &payment_hash, &payment_secret, |msg| {
use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use ln::channelmanager::HTLCSource;
use ln::msgs;
+use routing::network_graph::NetworkUpdate;
use routing::router::RouteHop;
use util::chacha20::ChaCha20;
use util::errors::{self, APIError};
/// OutboundRoute).
/// Returns update, a boolean indicating that the payment itself failed, and the error code.
#[inline]
-pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<msgs::HTLCFailChannelUpdate>, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
- if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat } = htlc_source {
+pub(super) fn process_onion_failure<T: secp256k1::Signing, L: Deref>(secp_ctx: &Secp256k1<T>, logger: &L, htlc_source: &HTLCSource, mut packet_decrypted: Vec<u8>) -> (Option<NetworkUpdate>, bool, Option<u16>, Option<Vec<u8>>) where L::Target: Logger {
+ if let &HTLCSource::OutboundRoute { ref path, ref session_priv, ref first_hop_htlc_msat, .. } = htlc_source {
let mut res = None;
let mut htlc_msat = *first_hop_htlc_msat;
let mut error_code_ret = None;
} && is_from_final_node) // PERM bit observed below even this error is from the intermediate nodes
|| error_code == 21; // Special case error 21 as the Route object is bogus, TODO: Maybe fail the node if the CLTV was reasonable?
- let mut fail_channel_update = None;
+ let mut network_update = None;
if error_code & NODE == NODE {
- fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: error_code & PERM == PERM });
+ network_update = Some(NetworkUpdate::NodeFailure { node_id: route_hop.pubkey, is_permanent: error_code & PERM == PERM });
}
else if error_code & PERM == PERM {
- fail_channel_update = if payment_failed {None} else {Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
+ network_update = if payment_failed { None } else { Some(NetworkUpdate::ChannelClosed {
short_channel_id: path[next_route_hop_ix - if next_route_hop_ix == path.len() { 1 } else { 0 }].short_channel_id,
is_permanent: true,
})};
20 => chan_update.contents.flags & 2 == 0,
_ => false, // unknown error code; take channel_update as valid
};
- fail_channel_update = if is_chan_update_invalid {
+ network_update = if is_chan_update_invalid {
// This probably indicates the node which forwarded
// to the node in question corrupted something.
- Some(msgs::HTLCFailChannelUpdate::ChannelClosed {
+ Some(NetworkUpdate::ChannelClosed {
short_channel_id: route_hop.short_channel_id,
is_permanent: true,
})
} else {
- Some(msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {
+ Some(NetworkUpdate::ChannelUpdateMessage {
msg: chan_update,
})
};
}
}
}
- if fail_channel_update.is_none() {
+ if network_update.is_none() {
// They provided an UPDATE which was obviously bogus, not worth
// trying to relay through them anymore.
- fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+ network_update = Some(NetworkUpdate::NodeFailure {
node_id: route_hop.pubkey,
is_permanent: true,
});
// We can't understand their error messages and they failed to
// forward...they probably can't understand our forwards so its
// really not worth trying any further.
- fail_channel_update = Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+ network_update = Some(NetworkUpdate::NodeFailure {
node_id: route_hop.pubkey,
is_permanent: true,
});
// TODO: Here (and a few other places) we assume that BADONION errors
// are always "sourced" from the node previous to the one which failed
// to decode the onion.
- res = Some((fail_channel_update, !(error_code & PERM == PERM && is_from_final_node)));
+ res = Some((network_update, !(error_code & PERM == PERM && is_from_final_node)));
let (description, title) = errors::get_onion_error_description(error_code);
if debug_field_size > 0 && err_packet.failuremsg.len() >= 4 + debug_field_size {
} else {
// Useless packet that we can't use but it passed HMAC, so it
// definitely came from the peer in question
- res = Some((Some(msgs::HTLCFailChannelUpdate::NodeFailure {
+ res = Some((Some(NetworkUpdate::NodeFailure {
node_id: route_hop.pubkey,
is_permanent: true,
}), !is_from_final_node));
pub trait CustomMessageHandler: wire::CustomMessageReader {
/// Called with the message type that was received and the buffer to be read.
/// Can return a `MessageHandlingError` if the message could not be handled.
- fn handle_custom_message(&self, msg: Self::CustomMessage) -> Result<(), LightningError>;
+ fn handle_custom_message(&self, msg: Self::CustomMessage, sender_node_id: &PublicKey) -> Result<(), LightningError>;
/// Gets the list of pending messages which were generated by the custom message
/// handler, clearing the list in the process. The first tuple element must
fn handle_node_announcement(&self, _msg: &msgs::NodeAnnouncement) -> Result<bool, LightningError> { Ok(false) }
fn handle_channel_announcement(&self, _msg: &msgs::ChannelAnnouncement) -> Result<bool, LightningError> { Ok(false) }
fn handle_channel_update(&self, _msg: &msgs::ChannelUpdate) -> Result<bool, LightningError> { Ok(false) }
- fn handle_htlc_fail_channel_update(&self, _update: &msgs::HTLCFailChannelUpdate) {}
fn get_next_channel_announcements(&self, _starting_point: u64, _batch_amount: u8) ->
Vec<(msgs::ChannelAnnouncement, Option<msgs::ChannelUpdate>, Option<msgs::ChannelUpdate>)> { Vec::new() }
fn get_next_node_announcements(&self, _starting_point: Option<&PublicKey>, _batch_amount: u8) -> Vec<msgs::NodeAnnouncement> { Vec::new() }
}
impl CustomMessageHandler for IgnoringMessageHandler {
- fn handle_custom_message(&self, _msg: Self::CustomMessage) -> Result<(), LightningError> {
+ fn handle_custom_message(&self, _msg: Self::CustomMessage, _sender_node_id: &PublicKey) -> Result<(), LightningError> {
// Since we always return `None` in the read the handle method should never be called.
unreachable!();
}
/// Append a message to a peer's pending outbound/write buffer, and update the map of peers needing sends accordingly.
fn enqueue_message<M: wire::Type + Writeable + Debug>(&self, peer: &mut Peer, message: &M) {
- let mut buffer = VecWriter(Vec::new());
+ let mut buffer = VecWriter(Vec::with_capacity(2048));
wire::write(message, &mut buffer).unwrap(); // crash if the write failed
let encoded_message = buffer.0;
log_trace!(self.logger, "Received unknown odd message of type {}, ignoring", type_id);
},
wire::Message::Custom(custom) => {
- self.custom_message_handler.handle_custom_message(custom)?;
+ self.custom_message_handler.handle_custom_message(custom, &peer.their_node_id.unwrap())?;
},
};
Ok(should_forward)
let peer = get_peer_for_forwarding!(node_id);
peer.pending_outbound_buffer.push_back(peer.channel_encryptor.encrypt_message(&encode_msg!(msg)));
},
- MessageSendEvent::PaymentFailureNetworkUpdate { ref update } => {
- self.message_handler.route_handler.handle_htlc_fail_channel_update(update);
- },
MessageSendEvent::HandleError { ref node_id, ref action } => {
match *action {
msgs::ErrorAction::DisconnectPeer { ref msg } => {
use chain::{Confirm, Watch};
use ln::channelmanager::{ChannelManager, ChannelManagerReadArgs};
use ln::features::InitFeatures;
-use ln::msgs::{ChannelMessageHandler, ErrorAction, HTLCFailChannelUpdate};
+use ln::msgs::{ChannelMessageHandler, ErrorAction};
+use routing::network_graph::NetworkUpdate;
use util::enforcing_trait_impls::EnforcingSigner;
use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
use util::test_utils;
if claim {
expect_payment_sent!(nodes[0], our_payment_preimage);
} else {
- let events = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(events.len(), 1);
- if let MessageSendEvent::PaymentFailureNetworkUpdate { update: HTLCFailChannelUpdate::ChannelClosed { ref is_permanent, .. } } = events[0] {
- assert!(is_permanent);
- } else { panic!("Unexpected event!"); }
- expect_payment_failed!(nodes[0], our_payment_hash, false);
+ expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_2.0.contents.short_channel_id, true);
}
}
use chain::transaction::OutPoint;
use ln::{PaymentPreimage, PaymentHash};
use ln::channelmanager::PaymentSendFailure;
+use routing::network_graph::NetworkUpdate;
use routing::router::get_route;
use ln::features::{InitFeatures, InvoiceFeatures};
use ln::msgs;
let net_graph_msg_handler0 = &nodes[0].net_graph_msg_handler;
let net_graph_msg_handler1 = &nodes[1].net_graph_msg_handler;
- let route_1 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler0.network_graph.read().unwrap(), &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
- let route_2 = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler1.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route_1 = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler0.network_graph, &nodes[1].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route_2 = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler1.network_graph, &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
unwrap_send_err!(nodes[0].node.send_payment(&route_1, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {});
unwrap_send_err!(nodes[1].node.send_payment(&route_2, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable {..}, {});
let (_, our_payment_hash, our_payment_secret) = get_payment_preimage_hash!(nodes[2]);
let net_graph_msg_handler = &nodes[0].net_graph_msg_handler;
- let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
+ let route = get_route(&nodes[0].node.get_our_node_id(), &net_graph_msg_handler.network_graph, &nodes[2].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &[], 100000, TEST_FINAL_CLTV, &logger).unwrap();
nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret)).unwrap();
check_added_monitors!(nodes[0], 1);
let updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id());
nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &updates_2.update_fail_htlcs[0]);
commitment_signed_dance!(nodes[0], nodes[1], updates_2.commitment_signed, false, true);
- expect_payment_failed!(nodes[0], our_payment_hash, false);
+ expect_payment_failed_with_update!(nodes[0], our_payment_hash, false, chan_2.0.contents.short_channel_id, true);
let msg_events = nodes[0].node.get_and_clear_pending_msg_events();
- assert_eq!(msg_events.len(), 2);
- match msg_events[0] {
- MessageSendEvent::PaymentFailureNetworkUpdate { update: msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg }} => {
- assert_eq!(msg.contents.short_channel_id, chan_1.0.contents.short_channel_id);
- },
- _ => panic!("Unexpected event"),
- }
- let node_0_closing_signed = match msg_events[1] {
+ assert_eq!(msg_events.len(), 1);
+ let node_0_closing_signed = match msg_events[0] {
MessageSendEvent::SendClosingSigned { ref node_id, ref msg } => {
assert_eq!(*node_id, nodes[1].node.get_our_node_id());
(*msg).clone()
use ln::msgs;
use util::ser::{Writeable, Readable, Writer};
use util::logger::{Logger, Level};
-use util::events::{MessageSendEvent, MessageSendEventsProvider};
+use util::events::{Event, EventHandler, MessageSendEvent, MessageSendEventsProvider};
use util::scid_utils::{block_from_scid, scid_from_parts, MAX_SCID_BLOCK};
use io;
const MAX_SCIDS_PER_REPLY: usize = 8000;
/// Represents the network as nodes and channels between them
-#[derive(Clone, PartialEq)]
pub struct NetworkGraph {
genesis_hash: BlockHash,
- channels: BTreeMap<u64, ChannelInfo>,
- nodes: BTreeMap<PublicKey, NodeInfo>,
+ // Lock order: channels -> nodes
+ channels: RwLock<BTreeMap<u64, ChannelInfo>>,
+ nodes: RwLock<BTreeMap<PublicKey, NodeInfo>>,
}
-/// A simple newtype for RwLockReadGuard<'a, NetworkGraph>.
-/// This exists only to make accessing a RwLock<NetworkGraph> possible from
-/// the C bindings, as it can be done directly in Rust code.
-pub struct LockedNetworkGraph<'a>(pub RwLockReadGuard<'a, NetworkGraph>);
+/// A read-only view of [`NetworkGraph`].
+pub struct ReadOnlyNetworkGraph<'a> {
+ channels: RwLockReadGuard<'a, BTreeMap<u64, ChannelInfo>>,
+ nodes: RwLockReadGuard<'a, BTreeMap<PublicKey, NodeInfo>>,
+}
+
+/// Update to the [`NetworkGraph`] based on payment failure information conveyed via the Onion
+/// return packet by a node along the route. See [BOLT #4] for details.
+///
+/// [BOLT #4]: https://github.com/lightningnetwork/lightning-rfc/blob/master/04-onion-routing.md
+#[derive(Clone, Debug, PartialEq)]
+pub enum NetworkUpdate {
+ /// An error indicating a `channel_update` messages should be applied via
+ /// [`NetworkGraph::update_channel`].
+ ChannelUpdateMessage {
+ /// The update to apply via [`NetworkGraph::update_channel`].
+ msg: ChannelUpdate,
+ },
+ /// An error indicating only that a channel has been closed, which should be applied via
+ /// [`NetworkGraph::close_channel_from_update`].
+ ChannelClosed {
+ /// The short channel id of the closed channel.
+ short_channel_id: u64,
+ /// Whether the channel should be permanently removed or temporarily disabled until a new
+ /// `channel_update` message is received.
+ is_permanent: bool,
+ },
+ /// An error indicating only that a node has failed, which should be applied via
+ /// [`NetworkGraph::fail_node`].
+ NodeFailure {
+ /// The node id of the failed node.
+ node_id: PublicKey,
+ /// Whether the node should be permanently removed from consideration or can be restored
+ /// when a new `channel_update` message is received.
+ is_permanent: bool,
+ }
+}
+
+impl_writeable_tlv_based_enum_upgradable!(NetworkUpdate,
+ (0, ChannelUpdateMessage) => {
+ (0, msg, required),
+ },
+ (2, ChannelClosed) => {
+ (0, short_channel_id, required),
+ (2, is_permanent, required),
+ },
+ (4, NodeFailure) => {
+ (0, node_id, required),
+ (2, is_permanent, required),
+ },
+);
+
+impl<C: Deref, L: Deref> EventHandler for NetGraphMsgHandler<C, L>
+where C::Target: chain::Access, L::Target: Logger {
+ fn handle_event(&self, event: &Event) {
+ if let Event::PaymentFailed { payment_hash: _, rejected_by_dest: _, network_update, .. } = event {
+ if let Some(network_update) = network_update {
+ self.handle_network_update(network_update);
+ }
+ }
+ }
+}
/// Receives and validates network updates from peers,
/// stores authentic and relevant data as a network graph.
/// This network graph is then used for routing payments.
/// Provides interface to help with initial routing sync by
/// serving historical announcements.
-pub struct NetGraphMsgHandler<C: Deref, L: Deref> where C::Target: chain::Access, L::Target: Logger {
+///
+/// Serves as an [`EventHandler`] for applying updates from [`Event::PaymentFailed`] to the
+/// [`NetworkGraph`].
+pub struct NetGraphMsgHandler<C: Deref, L: Deref>
+where C::Target: chain::Access, L::Target: Logger
+{
secp_ctx: Secp256k1<secp256k1::VerifyOnly>,
/// Representation of the payment channel network
- pub network_graph: RwLock<NetworkGraph>,
+ pub network_graph: NetworkGraph,
chain_access: Option<C>,
full_syncs_requested: AtomicUsize,
pending_events: Mutex<Vec<MessageSendEvent>>,
logger: L,
}
-impl<C: Deref, L: Deref> NetGraphMsgHandler<C, L> where C::Target: chain::Access, L::Target: Logger {
+impl<C: Deref, L: Deref> NetGraphMsgHandler<C, L>
+where C::Target: chain::Access, L::Target: Logger
+{
/// Creates a new tracker of the actual state of the network of channels and nodes,
- /// assuming a fresh network graph.
+ /// assuming an existing Network Graph.
/// Chain monitor is used to make sure announced channels exist on-chain,
/// channel data is correct, and that the announcement is signed with
/// channel owners' keys.
- pub fn new(genesis_hash: BlockHash, chain_access: Option<C>, logger: L) -> Self {
+ pub fn new(network_graph: NetworkGraph, chain_access: Option<C>, logger: L) -> Self {
NetGraphMsgHandler {
secp_ctx: Secp256k1::verification_only(),
- network_graph: RwLock::new(NetworkGraph::new(genesis_hash)),
- full_syncs_requested: AtomicUsize::new(0),
- chain_access,
- pending_events: Mutex::new(vec![]),
- logger,
- }
- }
-
- /// Creates a new tracker of the actual state of the network of channels and nodes,
- /// assuming an existing Network Graph.
- pub fn from_net_graph(chain_access: Option<C>, logger: L, network_graph: NetworkGraph) -> Self {
- NetGraphMsgHandler {
- secp_ctx: Secp256k1::verification_only(),
- network_graph: RwLock::new(network_graph),
+ network_graph,
full_syncs_requested: AtomicUsize::new(0),
chain_access,
pending_events: Mutex::new(vec![]),
self.chain_access = chain_access;
}
- /// Take a read lock on the network_graph and return it in the C-bindings
- /// newtype helper. This is likely only useful when called via the C
- /// bindings as you can call `self.network_graph.read().unwrap()` in Rust
- /// yourself.
- pub fn read_locked_graph<'a>(&'a self) -> LockedNetworkGraph<'a> {
- LockedNetworkGraph(self.network_graph.read().unwrap())
- }
-
/// Returns true when a full routing table sync should be performed with a peer.
fn should_request_full_sync(&self, _node_id: &PublicKey) -> bool {
//TODO: Determine whether to request a full sync based on the network map.
false
}
}
-}
-impl<'a> LockedNetworkGraph<'a> {
- /// Get a reference to the NetworkGraph which this read-lock contains.
- pub fn graph(&self) -> &NetworkGraph {
- &*self.0
+ /// Applies changes to the [`NetworkGraph`] from the given update.
+ fn handle_network_update(&self, update: &NetworkUpdate) {
+ match *update {
+ NetworkUpdate::ChannelUpdateMessage { ref msg } => {
+ 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.network_graph.update_channel(msg, &self.secp_ctx);
+ },
+ NetworkUpdate::ChannelClosed { short_channel_id, is_permanent } => {
+ let action = if is_permanent { "Removing" } else { "Disabling" };
+ log_debug!(self.logger, "{} channel graph entry for {} due to a payment failure.", action, short_channel_id);
+ self.network_graph.close_channel_from_update(short_channel_id, is_permanent);
+ },
+ NetworkUpdate::NodeFailure { ref node_id, is_permanent } => {
+ let action = if is_permanent { "Removing" } else { "Disabling" };
+ log_debug!(self.logger, "{} node graph entry for {} due to a payment failure.", action, node_id);
+ self.network_graph.fail_node(node_id, is_permanent);
+ },
+ }
}
}
-
macro_rules! secp_verify_sig {
( $secp_ctx: expr, $msg: expr, $sig: expr, $pubkey: expr ) => {
match $secp_ctx.verify($msg, $sig, $pubkey) {
};
}
-impl<C: Deref , L: Deref > RoutingMessageHandler for NetGraphMsgHandler<C, L> where C::Target: chain::Access, L::Target: Logger {
+impl<C: Deref, L: Deref> RoutingMessageHandler for NetGraphMsgHandler<C, L>
+where C::Target: chain::Access, L::Target: Logger
+{
fn handle_node_announcement(&self, msg: &msgs::NodeAnnouncement) -> Result<bool, LightningError> {
- self.network_graph.write().unwrap().update_node_from_announcement(msg, &self.secp_ctx)?;
+ self.network_graph.update_node_from_announcement(msg, &self.secp_ctx)?;
Ok(msg.contents.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY &&
msg.contents.excess_address_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY &&
msg.contents.excess_data.len() + msg.contents.excess_address_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY)
}
fn handle_channel_announcement(&self, msg: &msgs::ChannelAnnouncement) -> Result<bool, LightningError> {
- self.network_graph.write().unwrap().update_channel_from_announcement(msg, &self.chain_access, &self.secp_ctx)?;
+ self.network_graph.update_channel_from_announcement(msg, &self.chain_access, &self.secp_ctx)?;
log_trace!(self.logger, "Added channel_announcement for {}{}", msg.contents.short_channel_id, if !msg.contents.excess_data.is_empty() { " with excess uninterpreted data!" } else { "" });
Ok(msg.contents.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY)
}
- fn handle_htlc_fail_channel_update(&self, update: &msgs::HTLCFailChannelUpdate) {
- match update {
- &msgs::HTLCFailChannelUpdate::ChannelUpdateMessage { ref msg } => {
- let chan_enabled = msg.contents.flags & (1 << 1) != (1 << 1);
- log_debug!(self.logger, "Updating channel with channel_update from a payment failure. Channel {} is {}abled.", msg.contents.short_channel_id, if chan_enabled { "en" } else { "dis" });
- let _ = self.network_graph.write().unwrap().update_channel(msg, &self.secp_ctx);
- },
- &msgs::HTLCFailChannelUpdate::ChannelClosed { short_channel_id, is_permanent } => {
- log_debug!(self.logger, "{} channel graph entry for {} due to a payment failure.", if is_permanent { "Removing" } else { "Disabling" }, short_channel_id);
- self.network_graph.write().unwrap().close_channel_from_update(short_channel_id, is_permanent);
- },
- &msgs::HTLCFailChannelUpdate::NodeFailure { ref node_id, is_permanent } => {
- log_debug!(self.logger, "{} node graph entry for {} due to a payment failure.", if is_permanent { "Removing" } else { "Disabling" }, node_id);
- self.network_graph.write().unwrap().fail_node(node_id, is_permanent);
- },
- }
- }
-
fn handle_channel_update(&self, msg: &msgs::ChannelUpdate) -> Result<bool, LightningError> {
- self.network_graph.write().unwrap().update_channel(msg, &self.secp_ctx)?;
+ self.network_graph.update_channel(msg, &self.secp_ctx)?;
Ok(msg.contents.excess_data.len() <= MAX_EXCESS_BYTES_FOR_RELAY)
}
fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> {
- let network_graph = self.network_graph.read().unwrap();
let mut result = Vec::with_capacity(batch_amount as usize);
- let mut iter = network_graph.get_channels().range(starting_point..);
+ let channels = self.network_graph.channels.read().unwrap();
+ let mut iter = channels.range(starting_point..);
while result.len() < batch_amount as usize {
if let Some((_, ref chan)) = iter.next() {
if chan.announcement_message.is_some() {
}
fn get_next_node_announcements(&self, starting_point: Option<&PublicKey>, batch_amount: u8) -> Vec<NodeAnnouncement> {
- let network_graph = self.network_graph.read().unwrap();
let mut result = Vec::with_capacity(batch_amount as usize);
+ let nodes = self.network_graph.nodes.read().unwrap();
let mut iter = if let Some(pubkey) = starting_point {
- let mut iter = network_graph.get_nodes().range((*pubkey)..);
+ let mut iter = nodes.range((*pubkey)..);
iter.next();
iter
} else {
- network_graph.get_nodes().range(..)
+ nodes.range(..)
};
while result.len() < batch_amount as usize {
if let Some((_, ref node)) = iter.next() {
pending_events.push(MessageSendEvent::SendChannelRangeQuery {
node_id: their_node_id.clone(),
msg: QueryChannelRange {
- chain_hash: self.network_graph.read().unwrap().genesis_hash,
+ chain_hash: self.network_graph.genesis_hash,
first_blocknum,
number_of_blocks,
},
fn handle_query_channel_range(&self, their_node_id: &PublicKey, msg: QueryChannelRange) -> Result<(), LightningError> {
log_debug!(self.logger, "Handling query_channel_range peer={}, first_blocknum={}, number_of_blocks={}", log_pubkey!(their_node_id), msg.first_blocknum, msg.number_of_blocks);
- let network_graph = self.network_graph.read().unwrap();
-
let inclusive_start_scid = scid_from_parts(msg.first_blocknum as u64, 0, 0);
// We might receive valid queries with end_blocknum that would overflow SCID conversion.
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 != 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.genesis_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(),
// (has at least one update). A peer may still want to know the channel
// exists even if its not yet routable.
let mut batches: Vec<Vec<u64>> = vec![Vec::with_capacity(MAX_SCIDS_PER_REPLY)];
- for (_, ref chan) in network_graph.get_channels().range(inclusive_start_scid.unwrap()..exclusive_end_scid.unwrap()) {
+ let channels = self.network_graph.channels.read().unwrap();
+ for (_, ref chan) in channels.range(inclusive_start_scid.unwrap()..exclusive_end_scid.unwrap()) {
if let Some(chan_announcement) = &chan.announcement_message {
// Construct a new batch if last one is full
if batches.last().unwrap().len() == batches.last().unwrap().capacity() {
batch.push(chan_announcement.contents.short_channel_id);
}
}
- drop(network_graph);
+ drop(channels);
let mut pending_events = self.pending_events.lock().unwrap();
let batch_count = batches.len();
/// Fees for routing via a given channel or a node
-#[derive(Eq, PartialEq, Copy, Clone, Debug)]
+#[derive(Eq, PartialEq, Copy, Clone, Debug, Hash)]
pub struct RoutingFees {
/// Flat routing fee in satoshis
pub base_msat: u32,
write_ver_prefix!(writer, SERIALIZATION_VERSION, MIN_SERIALIZATION_VERSION);
self.genesis_hash.write(writer)?;
- (self.channels.len() as u64).write(writer)?;
- for (ref chan_id, ref chan_info) in self.channels.iter() {
+ let channels = self.channels.read().unwrap();
+ (channels.len() as u64).write(writer)?;
+ for (ref chan_id, ref chan_info) in channels.iter() {
(*chan_id).write(writer)?;
chan_info.write(writer)?;
}
- (self.nodes.len() as u64).write(writer)?;
- for (ref node_id, ref node_info) in self.nodes.iter() {
+ let nodes = self.nodes.read().unwrap();
+ (nodes.len() as u64).write(writer)?;
+ for (ref node_id, ref node_info) in nodes.iter() {
node_id.write(writer)?;
node_info.write(writer)?;
}
Ok(NetworkGraph {
genesis_hash,
- channels,
- nodes,
+ channels: RwLock::new(channels),
+ nodes: RwLock::new(nodes),
})
}
}
impl fmt::Display for NetworkGraph {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
writeln!(f, "Network map\n[Channels]")?;
- for (key, val) in self.channels.iter() {
+ for (key, val) in self.channels.read().unwrap().iter() {
writeln!(f, " {}: {}", key, val)?;
}
writeln!(f, "[Nodes]")?;
- for (key, val) in self.nodes.iter() {
+ for (key, val) in self.nodes.read().unwrap().iter() {
writeln!(f, " {}: {}", log_pubkey!(key), val)?;
}
Ok(())
}
}
-impl NetworkGraph {
- /// Returns all known valid channels' short ids along with announced channel info.
- ///
- /// (C-not exported) because we have no mapping for `BTreeMap`s
- pub fn get_channels<'a>(&'a self) -> &'a BTreeMap<u64, ChannelInfo> { &self.channels }
- /// Returns all known nodes' public keys along with announced node info.
- ///
- /// (C-not exported) because we have no mapping for `BTreeMap`s
- pub fn get_nodes<'a>(&'a self) -> &'a BTreeMap<PublicKey, NodeInfo> { &self.nodes }
-
- /// Get network addresses by node id.
- /// Returns None if the requested node is completely unknown,
- /// or if node announcement for the node was never received.
- ///
- /// (C-not exported) as there is no practical way to track lifetimes of returned values.
- pub fn get_addresses<'a>(&'a self, pubkey: &PublicKey) -> Option<&'a Vec<NetAddress>> {
- if let Some(node) = self.nodes.get(pubkey) {
- if let Some(node_info) = node.announcement_info.as_ref() {
- return Some(&node_info.addresses)
- }
- }
- None
+impl PartialEq for NetworkGraph {
+ fn eq(&self, other: &Self) -> bool {
+ self.genesis_hash == other.genesis_hash &&
+ *self.channels.read().unwrap() == *other.channels.read().unwrap() &&
+ *self.nodes.read().unwrap() == *other.nodes.read().unwrap()
}
+}
+impl NetworkGraph {
/// Creates a new, empty, network graph.
pub fn new(genesis_hash: BlockHash) -> NetworkGraph {
Self {
genesis_hash,
- channels: BTreeMap::new(),
- nodes: BTreeMap::new(),
+ channels: RwLock::new(BTreeMap::new()),
+ nodes: RwLock::new(BTreeMap::new()),
+ }
+ }
+
+ /// Returns a read-only view of the network graph.
+ pub fn read_only(&'_ self) -> ReadOnlyNetworkGraph<'_> {
+ let channels = self.channels.read().unwrap();
+ let nodes = self.nodes.read().unwrap();
+ ReadOnlyNetworkGraph {
+ channels,
+ nodes,
}
}
/// You probably don't want to call this directly, instead relying on a NetGraphMsgHandler'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.
- pub fn update_node_from_announcement<T: secp256k1::Verification>(&mut self, msg: &msgs::NodeAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<(), LightningError> {
+ pub fn update_node_from_announcement<T: secp256k1::Verification>(&self, msg: &msgs::NodeAnnouncement, secp_ctx: &Secp256k1<T>) -> Result<(), LightningError> {
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
secp_verify_sig!(secp_ctx, &msg_hash, &msg.signature, &msg.contents.node_id);
self.update_node_from_announcement_intern(&msg.contents, Some(&msg))
/// given node announcement without verifying the associated signatures. Because we aren't
/// given the associated signatures here we cannot relay the node announcement to any of our
/// peers.
- pub fn update_node_from_unsigned_announcement(&mut self, msg: &msgs::UnsignedNodeAnnouncement) -> Result<(), LightningError> {
+ pub fn update_node_from_unsigned_announcement(&self, msg: &msgs::UnsignedNodeAnnouncement) -> Result<(), LightningError> {
self.update_node_from_announcement_intern(msg, None)
}
- fn update_node_from_announcement_intern(&mut self, msg: &msgs::UnsignedNodeAnnouncement, full_msg: Option<&msgs::NodeAnnouncement>) -> Result<(), LightningError> {
- match self.nodes.get_mut(&msg.node_id) {
+ fn update_node_from_announcement_intern(&self, msg: &msgs::UnsignedNodeAnnouncement, full_msg: Option<&msgs::NodeAnnouncement>) -> Result<(), LightningError> {
+ match self.nodes.write().unwrap().get_mut(&msg.node_id) {
None => Err(LightningError{err: "No existing channels for node_announcement".to_owned(), action: ErrorAction::IgnoreError}),
Some(node) => {
if let Some(node_info) = node.announcement_info.as_ref() {
///
/// If a `chain::Access` object is provided via `chain_access`, it will be called to verify
/// the corresponding UTXO exists on chain and is correctly-formatted.
- pub fn update_channel_from_announcement<T: secp256k1::Verification, C: Deref>
- (&mut self, msg: &msgs::ChannelAnnouncement, chain_access: &Option<C>, secp_ctx: &Secp256k1<T>)
- -> Result<(), LightningError>
- where C::Target: chain::Access {
+ pub fn update_channel_from_announcement<T: secp256k1::Verification, C: Deref>(
+ &self, msg: &msgs::ChannelAnnouncement, chain_access: &Option<C>, secp_ctx: &Secp256k1<T>
+ ) -> Result<(), LightningError>
+ where
+ C::Target: chain::Access,
+ {
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
secp_verify_sig!(secp_ctx, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1);
secp_verify_sig!(secp_ctx, &msg_hash, &msg.node_signature_2, &msg.contents.node_id_2);
///
/// If a `chain::Access` object is provided via `chain_access`, it will be called to verify
/// the corresponding UTXO exists on chain and is correctly-formatted.
- pub fn update_channel_from_unsigned_announcement<C: Deref>
- (&mut self, msg: &msgs::UnsignedChannelAnnouncement, chain_access: &Option<C>)
- -> Result<(), LightningError>
- where C::Target: chain::Access {
+ pub fn update_channel_from_unsigned_announcement<C: Deref>(
+ &self, msg: &msgs::UnsignedChannelAnnouncement, chain_access: &Option<C>
+ ) -> Result<(), LightningError>
+ where
+ C::Target: chain::Access,
+ {
self.update_channel_from_unsigned_announcement_intern(msg, None, chain_access)
}
- fn update_channel_from_unsigned_announcement_intern<C: Deref>
- (&mut self, msg: &msgs::UnsignedChannelAnnouncement, full_msg: Option<&msgs::ChannelAnnouncement>, chain_access: &Option<C>)
- -> Result<(), LightningError>
- where C::Target: chain::Access {
+ fn update_channel_from_unsigned_announcement_intern<C: Deref>(
+ &self, msg: &msgs::UnsignedChannelAnnouncement, full_msg: Option<&msgs::ChannelAnnouncement>, chain_access: &Option<C>
+ ) -> Result<(), LightningError>
+ where
+ C::Target: chain::Access,
+ {
if msg.node_id_1 == msg.node_id_2 || msg.bitcoin_key_1 == msg.bitcoin_key_2 {
return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
}
{ full_msg.cloned() } else { None },
};
- match self.channels.entry(msg.short_channel_id) {
+ let mut channels = self.channels.write().unwrap();
+ let mut nodes = self.nodes.write().unwrap();
+ match channels.entry(msg.short_channel_id) {
BtreeEntry::Occupied(mut entry) => {
//TODO: because asking the blockchain if short_channel_id is valid is only optional
//in the blockchain API, we need to handle it smartly here, though it's unclear
// b) we don't track UTXOs of channels we know about and remove them if they
// get reorg'd out.
// c) it's unclear how to do so without exposing ourselves to massive DoS risk.
- Self::remove_channel_in_nodes(&mut self.nodes, &entry.get(), msg.short_channel_id);
+ Self::remove_channel_in_nodes(&mut nodes, &entry.get(), msg.short_channel_id);
*entry.get_mut() = chan_info;
} else {
return Err(LightningError{err: "Already have knowledge of channel".to_owned(), action: ErrorAction::IgnoreAndLog(Level::Trace)})
macro_rules! add_channel_to_node {
( $node_id: expr ) => {
- match self.nodes.entry($node_id) {
+ match nodes.entry($node_id) {
BtreeEntry::Occupied(node_entry) => {
node_entry.into_mut().channels.push(msg.short_channel_id);
},
/// If permanent, removes a channel from the local storage.
/// May cause the removal of nodes too, if this was their last channel.
/// If not permanent, makes channels unavailable for routing.
- pub fn close_channel_from_update(&mut self, short_channel_id: u64, is_permanent: bool) {
+ pub fn close_channel_from_update(&self, short_channel_id: u64, is_permanent: bool) {
+ let mut channels = self.channels.write().unwrap();
if is_permanent {
- if let Some(chan) = self.channels.remove(&short_channel_id) {
- Self::remove_channel_in_nodes(&mut self.nodes, &chan, short_channel_id);
+ if let Some(chan) = channels.remove(&short_channel_id) {
+ let mut nodes = self.nodes.write().unwrap();
+ Self::remove_channel_in_nodes(&mut nodes, &chan, short_channel_id);
}
} else {
- if let Some(chan) = self.channels.get_mut(&short_channel_id) {
+ if let Some(chan) = channels.get_mut(&short_channel_id) {
if let Some(one_to_two) = chan.one_to_two.as_mut() {
one_to_two.enabled = false;
}
}
}
- fn fail_node(&mut self, _node_id: &PublicKey, is_permanent: bool) {
+ /// Marks a node in the graph as failed.
+ pub fn fail_node(&self, _node_id: &PublicKey, is_permanent: bool) {
if is_permanent {
// TODO: Wholly remove the node
} else {
/// You probably don't want to call this directly, instead relying on a NetGraphMsgHandler'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.
- pub fn update_channel<T: secp256k1::Verification>(&mut self, msg: &msgs::ChannelUpdate, secp_ctx: &Secp256k1<T>) -> Result<(), LightningError> {
+ pub fn update_channel<T: secp256k1::Verification>(&self, msg: &msgs::ChannelUpdate, secp_ctx: &Secp256k1<T>) -> Result<(), LightningError> {
self.update_channel_intern(&msg.contents, Some(&msg), Some((&msg.signature, secp_ctx)))
}
/// For an already known (from announcement) channel, update info about one of the directions
/// of the channel without verifying the associated signatures. Because we aren't given the
/// associated signatures here we cannot relay the channel update to any of our peers.
- pub fn update_channel_unsigned(&mut self, msg: &msgs::UnsignedChannelUpdate) -> Result<(), LightningError> {
+ pub fn update_channel_unsigned(&self, msg: &msgs::UnsignedChannelUpdate) -> Result<(), LightningError> {
self.update_channel_intern(msg, None, None::<(&secp256k1::Signature, &Secp256k1<secp256k1::VerifyOnly>)>)
}
- fn update_channel_intern<T: secp256k1::Verification>(&mut self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig_info: Option<(&secp256k1::Signature, &Secp256k1<T>)>) -> Result<(), LightningError> {
+ fn update_channel_intern<T: secp256k1::Verification>(&self, msg: &msgs::UnsignedChannelUpdate, full_msg: Option<&msgs::ChannelUpdate>, sig_info: Option<(&secp256k1::Signature, &Secp256k1<T>)>) -> Result<(), LightningError> {
let dest_node_id;
let chan_enabled = msg.flags & (1 << 1) != (1 << 1);
let chan_was_enabled;
- match self.channels.get_mut(&msg.short_channel_id) {
+ let mut channels = self.channels.write().unwrap();
+ match channels.get_mut(&msg.short_channel_id) {
None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}),
Some(channel) => {
if let OptionalField::Present(htlc_maximum_msat) = msg.htlc_maximum_msat {
}
}
+ let mut nodes = self.nodes.write().unwrap();
if chan_enabled {
- let node = self.nodes.get_mut(&dest_node_id).unwrap();
+ let node = nodes.get_mut(&dest_node_id).unwrap();
let mut base_msat = msg.fee_base_msat;
let mut proportional_millionths = msg.fee_proportional_millionths;
if let Some(fees) = node.lowest_inbound_channel_fees {
proportional_millionths
});
} else if chan_was_enabled {
- let node = self.nodes.get_mut(&dest_node_id).unwrap();
+ let node = nodes.get_mut(&dest_node_id).unwrap();
let mut lowest_inbound_channel_fees = None;
for chan_id in node.channels.iter() {
- let chan = self.channels.get(chan_id).unwrap();
+ let chan = channels.get(chan_id).unwrap();
let chan_info_opt;
if chan.node_one == dest_node_id {
chan_info_opt = chan.two_to_one.as_ref();
}
}
+impl ReadOnlyNetworkGraph<'_> {
+ /// Returns all known valid channels' short ids along with announced channel info.
+ ///
+ /// (C-not exported) because we have no mapping for `BTreeMap`s
+ pub fn channels(&self) -> &BTreeMap<u64, ChannelInfo> {
+ &*self.channels
+ }
+
+ /// Returns all known nodes' public keys along with announced node info.
+ ///
+ /// (C-not exported) because we have no mapping for `BTreeMap`s
+ pub fn nodes(&self) -> &BTreeMap<PublicKey, NodeInfo> {
+ &*self.nodes
+ }
+
+ /// Get network addresses by node id.
+ /// Returns None if the requested node is completely unknown,
+ /// or if node announcement for the node was never received.
+ ///
+ /// (C-not exported) as there is no practical way to track lifetimes of returned values.
+ pub fn get_addresses(&self, pubkey: &PublicKey) -> Option<&Vec<NetAddress>> {
+ if let Some(node) = self.nodes.get(pubkey) {
+ if let Some(node_info) = node.announcement_info.as_ref() {
+ return Some(&node_info.addresses)
+ }
+ }
+ None
+ }
+}
+
#[cfg(test)]
mod tests {
use chain;
+ use ln::PaymentHash;
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
- use routing::network_graph::{NetGraphMsgHandler, NetworkGraph, MAX_EXCESS_BYTES_FOR_RELAY};
+ use routing::network_graph::{NetGraphMsgHandler, NetworkGraph, NetworkUpdate, MAX_EXCESS_BYTES_FOR_RELAY};
use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
- UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, HTLCFailChannelUpdate,
+ UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
ReplyChannelRange, ReplyShortChannelIdsEnd, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
use util::test_utils;
use util::logger::Logger;
use util::ser::{Readable, Writeable};
- use util::events::{MessageSendEvent, MessageSendEventsProvider};
+ use util::events::{Event, EventHandler, MessageSendEvent, MessageSendEventsProvider};
use util::scid_utils::scid_from_parts;
use bitcoin::hashes::sha256d::Hash as Sha256dHash;
let secp_ctx = Secp256k1::new();
let logger = Arc::new(test_utils::TestLogger::new());
let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
- let net_graph_msg_handler = NetGraphMsgHandler::new(genesis_hash, None, Arc::clone(&logger));
+ let network_graph = NetworkGraph::new(genesis_hash);
+ let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger));
(secp_ctx, net_graph_msg_handler)
}
};
// Test if the UTXO lookups were not supported
- let mut net_graph_msg_handler = NetGraphMsgHandler::new(genesis_block(Network::Testnet).header.block_hash(), None, Arc::clone(&logger));
+ let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
+ let mut net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger));
match net_graph_msg_handler.handle_channel_announcement(&valid_announcement) {
Ok(res) => assert!(res),
_ => panic!()
};
{
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- match network.get_channels().get(&unsigned_announcement.short_channel_id) {
+ let network = &net_graph_msg_handler.network_graph;
+ match network.read_only().channels().get(&unsigned_announcement.short_channel_id) {
None => panic!(),
Some(_) => ()
- }
+ };
}
// If we receive announcement for the same channel (with UTXO lookups disabled),
// Test if an associated transaction were not on-chain (or not confirmed).
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
*chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx);
- net_graph_msg_handler = NetGraphMsgHandler::new(chain_source.clone().genesis_hash, Some(chain_source.clone()), Arc::clone(&logger));
+ let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
+ net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, Some(chain_source.clone()), Arc::clone(&logger));
unsigned_announcement.short_channel_id += 1;
msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_announcement.encode()[..])[..]);
};
{
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- match network.get_channels().get(&unsigned_announcement.short_channel_id) {
+ let network = &net_graph_msg_handler.network_graph;
+ match network.read_only().channels().get(&unsigned_announcement.short_channel_id) {
None => panic!(),
Some(_) => ()
- }
+ };
}
// If we receive announcement for the same channel (but TX is not confirmed),
_ => panic!()
};
{
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- match network.get_channels().get(&unsigned_announcement.short_channel_id) {
+ let network = &net_graph_msg_handler.network_graph;
+ match network.read_only().channels().get(&unsigned_announcement.short_channel_id) {
Some(channel_entry) => {
assert_eq!(channel_entry.features, ChannelFeatures::empty());
},
_ => panic!()
- }
+ };
}
// Don't relay valid channels with excess data
let secp_ctx = Secp256k1::new();
let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
- let net_graph_msg_handler = NetGraphMsgHandler::new(genesis_block(Network::Testnet).header.block_hash(), Some(chain_source.clone()), Arc::clone(&logger));
+ let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
+ let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, Some(chain_source.clone()), Arc::clone(&logger));
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
};
{
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- match network.get_channels().get(&short_channel_id) {
+ let network = &net_graph_msg_handler.network_graph;
+ match network.read_only().channels().get(&short_channel_id) {
None => panic!(),
Some(channel_info) => {
assert_eq!(channel_info.one_to_two.as_ref().unwrap().cltv_expiry_delta, 144);
assert!(channel_info.two_to_one.is_none());
}
- }
+ };
}
unsigned_channel_update.timestamp += 100;
}
#[test]
- fn handling_htlc_fail_channel_update() {
- let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler();
+ fn handling_network_update() {
+ let logger = test_utils::TestLogger::new();
+ let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
+ let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
+ let network_graph = NetworkGraph::new(genesis_hash);
+ let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, Some(chain_source.clone()), &logger);
+ let secp_ctx = Secp256k1::new();
+
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey);
let short_channel_id = 0;
let chain_hash = genesis_block(Network::Testnet).header.block_hash();
+ let network_graph = &net_graph_msg_handler.network_graph;
{
// There is no nodes in the table at the beginning.
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- assert_eq!(network.get_nodes().len(), 0);
+ assert_eq!(network_graph.read_only().nodes().len(), 0);
}
{
bitcoin_signature_2: secp_ctx.sign(&msghash, node_2_btckey),
contents: unsigned_announcement.clone(),
};
- match net_graph_msg_handler.handle_channel_announcement(&valid_channel_announcement) {
- Ok(_) => (),
- Err(_) => panic!()
- };
+ let chain_source: Option<&test_utils::TestChainSource> = None;
+ assert!(network_graph.update_channel_from_announcement(&valid_channel_announcement, &chain_source, &secp_ctx).is_ok());
+ assert!(network_graph.read_only().channels().get(&short_channel_id).is_some());
let unsigned_channel_update = UnsignedChannelUpdate {
chain_hash,
contents: unsigned_channel_update.clone()
};
- match net_graph_msg_handler.handle_channel_update(&valid_channel_update) {
- Ok(res) => assert!(res),
- _ => panic!()
- };
+ assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_none());
+
+ net_graph_msg_handler.handle_event(&Event::PaymentFailed {
+ payment_hash: PaymentHash([0; 32]),
+ rejected_by_dest: false,
+ all_paths_failed: true,
+ network_update: Some(NetworkUpdate::ChannelUpdateMessage {
+ msg: valid_channel_update,
+ }),
+ error_code: None,
+ error_data: None,
+ });
+
+ assert!(network_graph.read_only().channels().get(&short_channel_id).unwrap().one_to_two.is_some());
}
// Non-permanent closing just disables a channel
{
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- match network.get_channels().get(&short_channel_id) {
+ match network_graph.read_only().channels().get(&short_channel_id) {
None => panic!(),
Some(channel_info) => {
- assert!(channel_info.one_to_two.is_some());
+ assert!(channel_info.one_to_two.as_ref().unwrap().enabled);
}
- }
- }
-
- let channel_close_msg = HTLCFailChannelUpdate::ChannelClosed {
- short_channel_id,
- is_permanent: false
- };
+ };
- net_graph_msg_handler.handle_htlc_fail_channel_update(&channel_close_msg);
+ net_graph_msg_handler.handle_event(&Event::PaymentFailed {
+ payment_hash: PaymentHash([0; 32]),
+ rejected_by_dest: false,
+ all_paths_failed: true,
+ network_update: Some(NetworkUpdate::ChannelClosed {
+ short_channel_id,
+ is_permanent: false,
+ }),
+ error_code: None,
+ error_data: None,
+ });
- // Non-permanent closing just disables a channel
- {
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- match network.get_channels().get(&short_channel_id) {
+ match network_graph.read_only().channels().get(&short_channel_id) {
None => panic!(),
Some(channel_info) => {
assert!(!channel_info.one_to_two.as_ref().unwrap().enabled);
}
- }
+ };
}
- let channel_close_msg = HTLCFailChannelUpdate::ChannelClosed {
- short_channel_id,
- is_permanent: true
- };
-
- net_graph_msg_handler.handle_htlc_fail_channel_update(&channel_close_msg);
-
// Permanent closing deletes a channel
{
- let network = net_graph_msg_handler.network_graph.read().unwrap();
- assert_eq!(network.get_channels().len(), 0);
+ net_graph_msg_handler.handle_event(&Event::PaymentFailed {
+ payment_hash: PaymentHash([0; 32]),
+ rejected_by_dest: false,
+ all_paths_failed: true,
+ network_update: Some(NetworkUpdate::ChannelClosed {
+ short_channel_id,
+ is_permanent: true,
+ }),
+ error_code: None,
+ error_data: None,
+ });
+
+ assert_eq!(network_graph.read_only().channels().len(), 0);
// Nodes are also deleted because there are no associated channels anymore
- assert_eq!(network.get_nodes().len(), 0);
+ assert_eq!(network_graph.read_only().nodes().len(), 0);
}
- // TODO: Test HTLCFailChannelUpdate::NodeFailure, which is not implemented yet.
+ // TODO: Test NetworkUpdate::NodeFailure, which is not implemented yet.
}
#[test]
Err(_) => panic!()
};
- let network = net_graph_msg_handler.network_graph.write().unwrap();
+ let network = &net_graph_msg_handler.network_graph;
let mut w = test_utils::TestVecWriter(Vec::new());
- assert!(!network.get_nodes().is_empty());
- assert!(!network.get_channels().is_empty());
+ assert!(!network.read_only().nodes().is_empty());
+ assert!(!network.read_only().channels().is_empty());
network.write(&mut w).unwrap();
assert!(<NetworkGraph>::read(&mut io::Cursor::new(&w.0)).unwrap() == *network);
}
use core::ops::Deref;
/// A hop in a route
-#[derive(Clone, PartialEq)]
+#[derive(Clone, Hash, PartialEq, Eq)]
pub struct RouteHop {
/// The node_id of the node at this hop.
pub pubkey: PublicKey,
/// A route directs a payment from the sender (us) to the recipient. If the recipient supports MPP,
/// it can take multiple paths. Each path is composed of one or more hops through the network.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, Hash, PartialEq, Eq)]
pub struct Route {
/// The list of routes taken for a single (potentially-)multi-part payment. The pubkey of the
/// last RouteHop in each path must be the same.
}
/// A list of hops along a payment path terminating with a channel to the recipient.
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct RouteHint(pub Vec<RouteHintHop>);
/// A channel descriptor for a hop along a payment path.
-#[derive(Eq, PartialEq, Debug, Clone)]
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct RouteHintHop {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,
return Err(LightningError{err: "Cannot send a payment of 0 msat".to_owned(), action: ErrorAction::IgnoreError});
}
- let last_hops = last_hops.iter().filter_map(|hops| hops.0.last()).collect::<Vec<_>>();
- for last_hop in last_hops.iter() {
- if last_hop.src_node_id == *payee {
- return Err(LightningError{err: "Last hop cannot have a payee as a source.".to_owned(), action: ErrorAction::IgnoreError});
+ for route in last_hops.iter() {
+ for hop in &route.0 {
+ if hop.src_node_id == *payee {
+ return Err(LightningError{err: "Last hop cannot have a payee as a source.".to_owned(), action: ErrorAction::IgnoreError});
+ }
}
}
// to use as the A* heuristic beyond just the cost to get one node further than the current
// one.
+ let network_graph = network.read_only();
+ let network_channels = network_graph.channels();
+ let network_nodes = network_graph.nodes();
let dummy_directional_info = DummyDirectionalChannelInfo { // used for first_hops routes
cltv_expiry_delta: 0,
htlc_minimum_msat: 0,
// work reliably.
let allow_mpp = if let Some(features) = &payee_features {
features.supports_basic_mpp()
- } else if let Some(node) = network.get_nodes().get(&payee) {
+ } else if let Some(node) = network_nodes.get(&payee) {
if let Some(node_info) = node.announcement_info.as_ref() {
node_info.features.supports_basic_mpp()
} else { false }
// Map from node_id to information about the best current path to that node, including feerate
// information.
- let mut dist = HashMap::with_capacity(network.get_nodes().len());
+ let mut dist = HashMap::with_capacity(network_nodes.len());
// During routing, if we ignore a path due to an htlc_minimum_msat limit, we set this,
// indicating that we may wish to try again with a higher value, potentially paying to meet an
// This map allows paths to be aware of the channel use by other paths in the same call.
// This would help to make a better path finding decisions and not "overbook" channels.
// It is unaware of the directions (except for `outbound_capacity_msat` in `first_hops`).
- let mut bookkeeped_channels_liquidity_available_msat = HashMap::with_capacity(network.get_nodes().len());
+ let mut bookkeeped_channels_liquidity_available_msat = HashMap::with_capacity(network_nodes.len());
// Keeping track of how much value we already collected across other paths. Helps to decide:
// - how much a new path should be transferring (upper bound);
// as a way to reach the $dest_node_id.
let mut fee_base_msat = u32::max_value();
let mut fee_proportional_millionths = u32::max_value();
- if let Some(Some(fees)) = network.get_nodes().get(&$src_node_id).map(|node| node.lowest_inbound_channel_fees) {
+ if let Some(Some(fees)) = network_nodes.get(&$src_node_id).map(|node| node.lowest_inbound_channel_fees) {
fee_base_msat = fees.base_msat;
fee_proportional_millionths = fees.proportional_millionths;
}
if !features.requires_unknown_bits() {
for chan_id in $node.channels.iter() {
- let chan = network.get_channels().get(chan_id).unwrap();
+ let chan = network_channels.get(chan_id).unwrap();
if !chan.features.requires_unknown_bits() {
if chan.node_one == *$node_id {
// ie $node is one, ie next hop in A* is two, via the two_to_one channel
// Add the payee as a target, so that the payee-to-payer
// search algorithm knows what to start with.
- match network.get_nodes().get(payee) {
+ match network_nodes.get(payee) {
// The payee is not in our network graph, so nothing to add here.
// There is still a chance of reaching them via last_hops though,
// so don't yet fail the payment here.
// If a caller provided us with last hops, add them to routing targets. Since this happens
// earlier than general path finding, they will be somewhat prioritized, although currently
// it matters only if the fees are exactly the same.
- for hop in last_hops.iter() {
+ for route in last_hops.iter().filter(|route| !route.0.is_empty()) {
+ let first_hop_in_route = &(route.0)[0];
let have_hop_src_in_graph =
- // Only add the last hop to our candidate set if either we have a direct channel or
- // they are in the regular network graph.
- first_hop_targets.get(&hop.src_node_id).is_some() ||
- network.get_nodes().get(&hop.src_node_id).is_some();
+ // Only add the hops in this route to our candidate set if either
+ // we have a direct channel to the first hop or the first hop is
+ // in the regular network graph.
+ first_hop_targets.get(&first_hop_in_route.src_node_id).is_some() ||
+ network_nodes.get(&first_hop_in_route.src_node_id).is_some();
if have_hop_src_in_graph {
- // BOLT 11 doesn't allow inclusion of features for the last hop hints, which
- // really sucks, cause we're gonna need that eventually.
- let last_hop_htlc_minimum_msat: u64 = match hop.htlc_minimum_msat {
- Some(htlc_minimum_msat) => htlc_minimum_msat,
- None => 0
- };
- let directional_info = DummyDirectionalChannelInfo {
- cltv_expiry_delta: hop.cltv_expiry_delta as u32,
- htlc_minimum_msat: last_hop_htlc_minimum_msat,
- htlc_maximum_msat: hop.htlc_maximum_msat,
- fees: hop.fees,
- };
- // We assume that the recipient only included route hints for routes which had
- // sufficient value to route `final_value_msat`. Note that in the case of "0-value"
- // invoices where the invoice does not specify value this may not be the case, but
- // better to include the hints than not.
- if add_entry!(hop.short_channel_id, hop.src_node_id, payee, directional_info, Some((final_value_msat + 999) / 1000), &empty_channel_features, 0, path_value_msat, 0) {
- // If this hop connects to a node with which we have a direct channel,
- // ignore the network graph and, if the last hop was added, add our
- // direct channel to the candidate set.
- //
- // Note that we *must* check if the last hop was added as `add_entry`
- // always assumes that the third argument is a node to which we have a
- // path.
- if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat, _)) = first_hop_targets.get(&hop.src_node_id) {
- add_entry!(first_hop, *our_node_id , hop.src_node_id, dummy_directional_info, Some(outbound_capacity_msat / 1000), features, 0, path_value_msat, 0);
+ // We start building the path from reverse, i.e., from payee
+ // to the first RouteHintHop in the path.
+ let hop_iter = route.0.iter().rev();
+ let prev_hop_iter = core::iter::once(payee).chain(
+ route.0.iter().skip(1).rev().map(|hop| &hop.src_node_id));
+ let mut hop_used = true;
+ let mut aggregate_next_hops_fee_msat: u64 = 0;
+ let mut aggregate_next_hops_path_htlc_minimum_msat: u64 = 0;
+
+ for (idx, (hop, prev_hop_id)) in hop_iter.zip(prev_hop_iter).enumerate() {
+ // BOLT 11 doesn't allow inclusion of features for the last hop hints, which
+ // really sucks, cause we're gonna need that eventually.
+ let hop_htlc_minimum_msat: u64 = hop.htlc_minimum_msat.unwrap_or(0);
+
+ let directional_info = DummyDirectionalChannelInfo {
+ cltv_expiry_delta: hop.cltv_expiry_delta as u32,
+ htlc_minimum_msat: hop_htlc_minimum_msat,
+ htlc_maximum_msat: hop.htlc_maximum_msat,
+ fees: hop.fees,
+ };
+
+ let reqd_channel_cap = if let Some (val) = final_value_msat.checked_add(match idx {
+ 0 => 999,
+ _ => aggregate_next_hops_fee_msat.checked_add(999).unwrap_or(u64::max_value())
+ }) { Some( val / 1000 ) } else { break; }; // converting from msat or breaking if max ~ infinity
+
+
+ // We assume that the recipient only included route hints for routes which had
+ // sufficient value to route `final_value_msat`. Note that in the case of "0-value"
+ // invoices where the invoice does not specify value this may not be the case, but
+ // better to include the hints than not.
+ if !add_entry!(hop.short_channel_id, hop.src_node_id, prev_hop_id, directional_info, reqd_channel_cap, &empty_channel_features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat) {
+ // If this hop was not used then there is no use checking the preceding hops
+ // in the RouteHint. We can break by just searching for a direct channel between
+ // last checked hop and first_hop_targets
+ hop_used = false;
+ }
+
+ // Searching for a direct channel between last checked hop and first_hop_targets
+ if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat, _)) = first_hop_targets.get(&prev_hop_id) {
+ add_entry!(first_hop, *our_node_id , prev_hop_id, dummy_directional_info, Some(outbound_capacity_msat / 1000), features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat);
+ }
+
+ if !hop_used {
+ break;
+ }
+
+ // In the next values of the iterator, the aggregate fees already reflects
+ // the sum of value sent from payer (final_value_msat) and routing fees
+ // for the last node in the RouteHint. We need to just add the fees to
+ // route through the current node so that the preceeding node (next iteration)
+ // can use it.
+ let hops_fee = compute_fees(aggregate_next_hops_fee_msat + final_value_msat, hop.fees)
+ .map_or(None, |inc| inc.checked_add(aggregate_next_hops_fee_msat));
+ aggregate_next_hops_fee_msat = if let Some(val) = hops_fee { val } else { break; };
+
+ let hop_htlc_minimum_msat_inc = if let Some(val) = compute_fees(aggregate_next_hops_path_htlc_minimum_msat, hop.fees) { val } else { break; };
+ let hops_path_htlc_minimum = aggregate_next_hops_path_htlc_minimum_msat
+ .checked_add(hop_htlc_minimum_msat_inc);
+ aggregate_next_hops_path_htlc_minimum_msat = if let Some(val) = hops_path_htlc_minimum { cmp::max(hop_htlc_minimum_msat, val) } else { break; };
+
+ if idx == route.0.len() - 1 {
+ // The last hop in this iterator is the first hop in
+ // overall RouteHint.
+ // If this hop connects to a node with which we have a direct channel,
+ // ignore the network graph and, if the last hop was added, add our
+ // direct channel to the candidate set.
+ //
+ // Note that we *must* check if the last hop was added as `add_entry`
+ // always assumes that the third argument is a node to which we have a
+ // path.
+ if let Some(&(ref first_hop, ref features, ref outbound_capacity_msat, _)) = first_hop_targets.get(&hop.src_node_id) {
+ add_entry!(first_hop, *our_node_id , hop.src_node_id, dummy_directional_info, Some(outbound_capacity_msat / 1000), features, aggregate_next_hops_fee_msat, path_value_msat, aggregate_next_hops_path_htlc_minimum_msat);
+ }
}
}
}
'path_walk: loop {
if let Some(&(_, _, _, ref features)) = first_hop_targets.get(&ordered_hops.last().unwrap().0.pubkey) {
ordered_hops.last_mut().unwrap().1 = features.clone();
- } else if let Some(node) = network.get_nodes().get(&ordered_hops.last().unwrap().0.pubkey) {
+ } else if let Some(node) = network_nodes.get(&ordered_hops.last().unwrap().0.pubkey) {
if let Some(node_info) = node.announcement_info.as_ref() {
ordered_hops.last_mut().unwrap().1 = node_info.features.clone();
} else {
// Otherwise, since the current target node is not us,
// keep "unrolling" the payment graph from payee to payer by
// finding a way to reach the current target from the payer side.
- match network.get_nodes().get(&pubkey) {
+ match network_nodes.get(&pubkey) {
None => {},
Some(node) => {
add_entries_to_cheapest_to_target_node!(node, &pubkey, lowest_fee_to_node, value_contribution_msat, path_htlc_minimum_msat);
}
// Using the same keys for LN and BTC ids
- fn add_channel(net_graph_msg_handler: &NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>, secp_ctx: &Secp256k1<All>, node_1_privkey: &SecretKey,
- node_2_privkey: &SecretKey, features: ChannelFeatures, short_channel_id: u64) {
+ fn add_channel(
+ net_graph_msg_handler: &NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
+ secp_ctx: &Secp256k1<All>, node_1_privkey: &SecretKey, node_2_privkey: &SecretKey, features: ChannelFeatures, short_channel_id: u64
+ ) {
let node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey);
let node_id_2 = PublicKey::from_secret_key(&secp_ctx, node_2_privkey);
};
}
- fn update_channel(net_graph_msg_handler: &NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>, secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, update: UnsignedChannelUpdate) {
+ fn update_channel(
+ net_graph_msg_handler: &NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
+ secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, update: UnsignedChannelUpdate
+ ) {
let msghash = hash_to_message!(&Sha256dHash::hash(&update.encode()[..])[..]);
let valid_channel_update = ChannelUpdate {
signature: secp_ctx.sign(&msghash, node_privkey),
};
}
- fn add_or_update_node(net_graph_msg_handler: &NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>, secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey,
- features: NodeFeatures, timestamp: u32) {
+ fn add_or_update_node(
+ net_graph_msg_handler: &NetGraphMsgHandler<Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>,
+ secp_ctx: &Secp256k1<All>, node_privkey: &SecretKey, features: NodeFeatures, timestamp: u32
+ ) {
let node_id = PublicKey::from_secret_key(&secp_ctx, node_privkey);
let unsigned_announcement = UnsignedNodeAnnouncement {
features,
let secp_ctx = Secp256k1::new();
let logger = Arc::new(test_utils::TestLogger::new());
let chain_monitor = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
- let net_graph_msg_handler = NetGraphMsgHandler::new(genesis_block(Network::Testnet).header.block_hash(), None, Arc::clone(&logger));
- // Build network from our_id to node7:
+ let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
+ let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger));
+ // Build network from our_id to node6:
//
// -1(1)2- node0 -1(3)2-
// / \
// \ /
// -1(7)2- node5 -1(10)2-
//
+ // Channels 5, 8, 9 and 10 are private channels.
+ //
// chan5 1-to-2: enabled, 100 msat fee
// chan5 2-to-1: enabled, 0 fee
//
// Simple route to 2 via 1
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 0, 42, Arc::clone(&logger)) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 0, 42, Arc::clone(&logger)) {
assert_eq!(err, "Cannot send a payment of 0 msat");
} else { panic!(); }
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
let our_chans = vec![get_channel_details(Some(2), our_id, InitFeatures::from_le_bytes(vec![0b11]), 100000)];
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)) {
assert_eq!(err, "First hop cannot have our_node_id as a destination.");
} else { panic!(); }
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
}
});
// Not possible to send 199_999_999, because the minimum on channel=2 is 200_000_000.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger)) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
});
// A payment above the minimum should pass
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 199_999_999, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
}
excess_data: Vec::new()
});
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger)).unwrap();
// Overpay fees to hit htlc_minimum_msat.
let overpaid_fees = route.paths[0][0].fee_msat + route.paths[1][0].fee_msat;
excess_data: Vec::new()
});
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger)).unwrap();
// Fine to overpay for htlc_minimum_msat if it allows us to save fee.
assert_eq!(route.paths.len(), 1);
let fees = route.paths[0][0].fee_msat;
assert_eq!(fees, 5_000);
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, 42, Arc::clone(&logger)).unwrap();
// Not fine to overpay for htlc_minimum_msat if it requires paying more than fee on
// the other channel.
});
// If all the channels require some features we don't understand, route should fail
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[7]);
add_or_update_node(&net_graph_msg_handler, &secp_ctx, &privkeys[7], unknown_features.clone(), 1);
// If all nodes require some features we don't understand, route should fail
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a path to the given destination");
} else { panic!(); }
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[7]);
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
// Route to 1 via 2 and 3 because our channel to 1 is disabled
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[0], None, None, &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 3);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
// If we specify a channel to node7, that overrides our local channel view and that gets used
let our_chans = vec![get_channel_details(Some(42), nodes[7].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[7]);
cltv_expiry_delta: (8 << 8) | 1,
htlc_minimum_msat: None,
htlc_maximum_msat: None,
+ }
+ ]), RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[4].clone(),
+ short_channel_id: 9,
+ fees: RoutingFees {
+ base_msat: 1001,
+ proportional_millionths: 0,
+ },
+ cltv_expiry_delta: (9 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
}]), RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[5].clone(),
+ short_channel_id: 10,
+ fees: zero_fees,
+ cltv_expiry_delta: (10 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }])]
+ }
+
+ fn last_hops_multi_private_channels(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
+ let zero_fees = RoutingFees {
+ base_msat: 0,
+ proportional_millionths: 0,
+ };
+ vec![RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[2].clone(),
+ short_channel_id: 5,
+ fees: RoutingFees {
+ base_msat: 100,
+ proportional_millionths: 0,
+ },
+ cltv_expiry_delta: (5 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }, RouteHintHop {
+ src_node_id: nodes[3].clone(),
+ short_channel_id: 8,
+ fees: zero_fees,
+ cltv_expiry_delta: (8 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }
+ ]), RouteHint(vec![RouteHintHop {
src_node_id: nodes[4].clone(),
short_channel_id: 9,
fees: RoutingFees {
}
#[test]
- fn last_hops_test() {
+ fn partial_route_hint_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
// Simple test across 2, 3, 5, and 4 via a last_hop channel
+ // Tests the behaviour when the RouteHint contains a suboptimal hop.
+ // RouteHint may be partially used by the algo to build the best path.
// First check that last hop can't have its source as the payee.
let invalid_last_hop = RouteHint(vec![RouteHintHop {
htlc_maximum_msat: None,
}]);
- let mut invalid_last_hops = last_hops(&nodes);
+ let mut invalid_last_hops = last_hops_multi_private_channels(&nodes);
invalid_last_hops.push(invalid_last_hop);
{
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, None, &invalid_last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)) {
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &invalid_last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)) {
assert_eq!(err, "Last hop cannot have a payee as a source.");
} else { panic!(); }
}
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, None, &last_hops(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops_multi_private_channels(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
+ assert_eq!(route.paths[0].len(), 5);
+
+ assert_eq!(route.paths[0][0].pubkey, nodes[1]);
+ assert_eq!(route.paths[0][0].short_channel_id, 2);
+ assert_eq!(route.paths[0][0].fee_msat, 100);
+ assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
+ assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2));
+ assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2));
+
+ assert_eq!(route.paths[0][1].pubkey, nodes[2]);
+ assert_eq!(route.paths[0][1].short_channel_id, 4);
+ assert_eq!(route.paths[0][1].fee_msat, 0);
+ assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 8) | 1);
+ assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3));
+ assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4));
+
+ assert_eq!(route.paths[0][2].pubkey, nodes[4]);
+ assert_eq!(route.paths[0][2].short_channel_id, 6);
+ assert_eq!(route.paths[0][2].fee_msat, 0);
+ assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 8) | 1);
+ assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(5));
+ assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(6));
+
+ assert_eq!(route.paths[0][3].pubkey, nodes[3]);
+ assert_eq!(route.paths[0][3].short_channel_id, 11);
+ assert_eq!(route.paths[0][3].fee_msat, 0);
+ assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 8) | 1);
+ // If we have a peer in the node map, we'll use their features here since we don't have
+ // a way of figuring out their features from the invoice:
+ assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags(4));
+ assert_eq!(route.paths[0][3].channel_features.le_flags(), &id_to_feature_flags(11));
+
+ assert_eq!(route.paths[0][4].pubkey, nodes[6]);
+ assert_eq!(route.paths[0][4].short_channel_id, 8);
+ assert_eq!(route.paths[0][4].fee_msat, 100);
+ assert_eq!(route.paths[0][4].cltv_expiry_delta, 42);
+ assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::<u8>::new()); // We dont pass flags in from invoices yet
+ assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
+ }
+
+ fn empty_last_hop(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
+ let zero_fees = RoutingFees {
+ base_msat: 0,
+ proportional_millionths: 0,
+ };
+ vec![RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[3].clone(),
+ short_channel_id: 8,
+ fees: zero_fees,
+ cltv_expiry_delta: (8 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }]), RouteHint(vec![
+
+ ]), RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[5].clone(),
+ short_channel_id: 10,
+ fees: zero_fees,
+ cltv_expiry_delta: (10 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }])]
+ }
+
+ #[test]
+ fn ignores_empty_last_hops_test() {
+ let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
+ let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+
+ // Test handling of an empty RouteHint passed in Invoice.
+
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &empty_last_hop(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 5);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
}
+ fn multi_hint_last_hops(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
+ let zero_fees = RoutingFees {
+ base_msat: 0,
+ proportional_millionths: 0,
+ };
+ vec![RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[2].clone(),
+ short_channel_id: 5,
+ fees: RoutingFees {
+ base_msat: 100,
+ proportional_millionths: 0,
+ },
+ cltv_expiry_delta: (5 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }, RouteHintHop {
+ src_node_id: nodes[3].clone(),
+ short_channel_id: 8,
+ fees: zero_fees,
+ cltv_expiry_delta: (8 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }]), RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[5].clone(),
+ short_channel_id: 10,
+ fees: zero_fees,
+ cltv_expiry_delta: (10 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }])]
+ }
+
+ #[test]
+ fn multi_hint_last_hops_test() {
+ let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
+ let (_, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
+ // Test through channels 2, 3, 5, 8.
+ // Test shows that multiple hop hints are considered.
+
+ // Disabling channels 6 & 7 by flags=2
+ update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
+ chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ short_channel_id: 6,
+ timestamp: 2,
+ flags: 2, // to disable
+ cltv_expiry_delta: 0,
+ htlc_minimum_msat: 0,
+ htlc_maximum_msat: OptionalField::Absent,
+ fee_base_msat: 0,
+ fee_proportional_millionths: 0,
+ excess_data: Vec::new()
+ });
+ update_channel(&net_graph_msg_handler, &secp_ctx, &privkeys[2], UnsignedChannelUpdate {
+ chain_hash: genesis_block(Network::Testnet).header.block_hash(),
+ short_channel_id: 7,
+ timestamp: 2,
+ flags: 2, // to disable
+ cltv_expiry_delta: 0,
+ htlc_minimum_msat: 0,
+ htlc_maximum_msat: OptionalField::Absent,
+ fee_base_msat: 0,
+ fee_proportional_millionths: 0,
+ excess_data: Vec::new()
+ });
+
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &multi_hint_last_hops(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
+ assert_eq!(route.paths[0].len(), 4);
+
+ assert_eq!(route.paths[0][0].pubkey, nodes[1]);
+ assert_eq!(route.paths[0][0].short_channel_id, 2);
+ assert_eq!(route.paths[0][0].fee_msat, 200);
+ assert_eq!(route.paths[0][0].cltv_expiry_delta, 1025);
+ assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2));
+ assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2));
+
+ assert_eq!(route.paths[0][1].pubkey, nodes[2]);
+ assert_eq!(route.paths[0][1].short_channel_id, 4);
+ assert_eq!(route.paths[0][1].fee_msat, 100);
+ assert_eq!(route.paths[0][1].cltv_expiry_delta, 1281);
+ assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3));
+ assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4));
+
+ assert_eq!(route.paths[0][2].pubkey, nodes[3]);
+ assert_eq!(route.paths[0][2].short_channel_id, 5);
+ assert_eq!(route.paths[0][2].fee_msat, 0);
+ assert_eq!(route.paths[0][2].cltv_expiry_delta, 2049);
+ assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(4));
+ assert_eq!(route.paths[0][2].channel_features.le_flags(), &Vec::<u8>::new());
+
+ assert_eq!(route.paths[0][3].pubkey, nodes[6]);
+ assert_eq!(route.paths[0][3].short_channel_id, 8);
+ assert_eq!(route.paths[0][3].fee_msat, 100);
+ assert_eq!(route.paths[0][3].cltv_expiry_delta, 42);
+ assert_eq!(route.paths[0][3].node_features.le_flags(), &Vec::<u8>::new()); // We dont pass flags in from invoices yet
+ assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
+ }
+
+ fn last_hops_with_public_channel(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
+ let zero_fees = RoutingFees {
+ base_msat: 0,
+ proportional_millionths: 0,
+ };
+ vec![RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[4].clone(),
+ short_channel_id: 11,
+ fees: zero_fees,
+ cltv_expiry_delta: (11 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }, RouteHintHop {
+ src_node_id: nodes[3].clone(),
+ short_channel_id: 8,
+ fees: zero_fees,
+ cltv_expiry_delta: (8 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }]), RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[4].clone(),
+ short_channel_id: 9,
+ fees: RoutingFees {
+ base_msat: 1001,
+ proportional_millionths: 0,
+ },
+ cltv_expiry_delta: (9 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }]), RouteHint(vec![RouteHintHop {
+ src_node_id: nodes[5].clone(),
+ short_channel_id: 10,
+ fees: zero_fees,
+ cltv_expiry_delta: (10 << 8) | 1,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
+ }])]
+ }
+
+ #[test]
+ fn last_hops_with_public_channel_test() {
+ let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
+ let (_, our_id, _, nodes) = get_nodes(&secp_ctx);
+ // This test shows that public routes can be present in the invoice
+ // which would be handled in the same manner.
+
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops_with_public_channel(&nodes).iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
+ assert_eq!(route.paths[0].len(), 5);
+
+ assert_eq!(route.paths[0][0].pubkey, nodes[1]);
+ assert_eq!(route.paths[0][0].short_channel_id, 2);
+ assert_eq!(route.paths[0][0].fee_msat, 100);
+ assert_eq!(route.paths[0][0].cltv_expiry_delta, (4 << 8) | 1);
+ assert_eq!(route.paths[0][0].node_features.le_flags(), &id_to_feature_flags(2));
+ assert_eq!(route.paths[0][0].channel_features.le_flags(), &id_to_feature_flags(2));
+
+ assert_eq!(route.paths[0][1].pubkey, nodes[2]);
+ assert_eq!(route.paths[0][1].short_channel_id, 4);
+ assert_eq!(route.paths[0][1].fee_msat, 0);
+ assert_eq!(route.paths[0][1].cltv_expiry_delta, (6 << 8) | 1);
+ assert_eq!(route.paths[0][1].node_features.le_flags(), &id_to_feature_flags(3));
+ assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(4));
+
+ assert_eq!(route.paths[0][2].pubkey, nodes[4]);
+ assert_eq!(route.paths[0][2].short_channel_id, 6);
+ assert_eq!(route.paths[0][2].fee_msat, 0);
+ assert_eq!(route.paths[0][2].cltv_expiry_delta, (11 << 8) | 1);
+ assert_eq!(route.paths[0][2].node_features.le_flags(), &id_to_feature_flags(5));
+ assert_eq!(route.paths[0][2].channel_features.le_flags(), &id_to_feature_flags(6));
+
+ assert_eq!(route.paths[0][3].pubkey, nodes[3]);
+ assert_eq!(route.paths[0][3].short_channel_id, 11);
+ assert_eq!(route.paths[0][3].fee_msat, 0);
+ assert_eq!(route.paths[0][3].cltv_expiry_delta, (8 << 8) | 1);
+ // If we have a peer in the node map, we'll use their features here since we don't have
+ // a way of figuring out their features from the invoice:
+ assert_eq!(route.paths[0][3].node_features.le_flags(), &id_to_feature_flags(4));
+ assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::<u8>::new());
+
+ assert_eq!(route.paths[0][4].pubkey, nodes[6]);
+ assert_eq!(route.paths[0][4].short_channel_id, 8);
+ assert_eq!(route.paths[0][4].fee_msat, 100);
+ assert_eq!(route.paths[0][4].cltv_expiry_delta, 42);
+ assert_eq!(route.paths[0][4].node_features.le_flags(), &Vec::<u8>::new()); // We dont pass flags in from invoices yet
+ assert_eq!(route.paths[0][4].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
+ }
+
#[test]
fn our_chans_last_hop_connect_test() {
let (secp_ctx, net_graph_msg_handler, _, logger) = build_graph();
// Simple test with outbound channel to 4 to test that last_hops and first_hops connect
let our_chans = vec![get_channel_details(Some(42), nodes[3].clone(), InitFeatures::from_le_bytes(vec![0b11]), 250_000_000)];
let mut last_hops = last_hops(&nodes);
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, Some(&our_chans.iter().collect::<Vec<_>>()), &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, Some(&our_chans.iter().collect::<Vec<_>>()), &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 2);
assert_eq!(route.paths[0][0].pubkey, nodes[3]);
last_hops[0].0[0].fees.base_msat = 1000;
// Revert to via 6 as the fee on 8 goes up
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, None, &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops.iter().collect::<Vec<_>>(), 100, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 4);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
assert_eq!(route.paths[0][3].channel_features.le_flags(), &Vec::<u8>::new()); // We can't learn any flags from invoices, sadly
// ...but still use 8 for larger payments as 6 has a variable feerate
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, None, &last_hops.iter().collect::<Vec<_>>(), 2000, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &last_hops.iter().collect::<Vec<_>>(), 2000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths[0].len(), 5);
assert_eq!(route.paths[0][0].pubkey, nodes[1]);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000_001, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 200_000_001, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), Some(&our_chans.iter().collect::<Vec<_>>()), &Vec::new(), 200_000_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 15_001, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 15_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 15_001, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 15_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 10_001, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route an exact amount we have should be fine.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 10_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let path = route.paths.last().unwrap();
});
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 60_000, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route 49 sats (just a bit below the capacity).
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 49_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
{
// Attempt to route an exact amount is also fine
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
});
{
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 50_000, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 50_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
let mut total_amount_paid_msat = 0;
for path in &route.paths {
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(),
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph,
&nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 300_000, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route 250 sats (just a bit below the capacity).
// Our algorithm should provide us with these 3 paths.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 250_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
{
// Attempt to route an exact amount is also fine
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 290_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 350_000, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route 300 sats (exact amount we can route).
// Our algorithm should provide us with these 3 paths, 100 sats each.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 300_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 3);
{
// Now, attempt to route 180 sats.
// Our algorithm should provide us with these 2 paths.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 180_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 2);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 210_000, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route 200 sats (exact amount we can route).
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[3],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[3],
Some(InvoiceFeatures::known()), None, &Vec::new(), 200_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 2);
{
// Attempt to route more than available results in a failure.
- if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ if let Err(LightningError{err, action: ErrorAction::IgnoreError}) = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 150_000, 42, Arc::clone(&logger)) {
assert_eq!(err, "Failed to find a sufficient route to the given destination");
} else { panic!(); }
{
// Now, attempt to route 125 sats (just a bit below the capacity of 3 channels).
// Our algorithm should provide us with these 3 paths.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 125_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 3);
let mut total_amount_paid_msat = 0;
{
// Attempt to route without the last small cheap channel
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2],
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2],
Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 2);
let mut total_amount_paid_msat = 0;
// "previous hop" being set to node 3, creating a loop in the path.
let secp_ctx = Secp256k1::new();
let logger = Arc::new(test_utils::TestLogger::new());
- let net_graph_msg_handler = NetGraphMsgHandler::new(genesis_block(Network::Testnet).header.block_hash(), None, Arc::clone(&logger));
+ let network_graph = NetworkGraph::new(genesis_block(Network::Testnet).header.block_hash());
+ let net_graph_msg_handler = NetGraphMsgHandler::new(network_graph, None, Arc::clone(&logger));
let (our_privkey, our_id, privkeys, nodes) = get_nodes(&secp_ctx);
add_channel(&net_graph_msg_handler, &secp_ctx, &our_privkey, &privkeys[1], ChannelFeatures::from_le_bytes(id_to_feature_flags(6)), 6);
{
// Now ensure the route flows simply over nodes 1 and 4 to 6.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[6], None, None, &Vec::new(), 10_000, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[6], None, None, &Vec::new(), 10_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 3);
{
// Now, attempt to route 90 sats, which is exactly 90 sats at the last hop, plus the
// 200% fee charged channel 13 in the 1-to-2 direction.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], None, None, &Vec::new(), 90_000, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], None, None, &Vec::new(), 90_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 2);
// Now, attempt to route 90 sats, hitting the htlc_minimum on channel 4, but
// overshooting the htlc_maximum on channel 2. Thus, we should pick the (absurdly
// expensive) channels 12-13 path.
- let route = get_route(&our_id, &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger)).unwrap();
+ let route = get_route(&our_id, &net_graph_msg_handler.network_graph, &nodes[2], Some(InvoiceFeatures::known()), None, &Vec::new(), 90_000, 42, Arc::clone(&logger)).unwrap();
assert_eq!(route.paths.len(), 1);
assert_eq!(route.paths[0].len(), 2);
// First, get 100 (source, destination) pairs for which route-getting actually succeeds...
let mut seed = random_init_seed() as usize;
+ let nodes = graph.read_only().nodes().clone();
'load_endpoints: for _ in 0..10 {
loop {
seed = seed.overflowing_mul(0xdeadbeef).0;
- let src = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let src = nodes.keys().skip(seed % nodes.len()).next().unwrap();
seed = seed.overflowing_mul(0xdeadbeef).0;
- let dst = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let dst = nodes.keys().skip(seed % nodes.len()).next().unwrap();
let amt = seed as u64 % 200_000_000;
if get_route(src, &graph, dst, None, None, &[], amt, 42, &test_utils::TestLogger::new()).is_ok() {
continue 'load_endpoints;
// First, get 100 (source, destination) pairs for which route-getting actually succeeds...
let mut seed = random_init_seed() as usize;
+ let nodes = graph.read_only().nodes().clone();
'load_endpoints: for _ in 0..10 {
loop {
seed = seed.overflowing_mul(0xdeadbeef).0;
- let src = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let src = nodes.keys().skip(seed % nodes.len()).next().unwrap();
seed = seed.overflowing_mul(0xdeadbeef).0;
- let dst = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let dst = nodes.keys().skip(seed % nodes.len()).next().unwrap();
let amt = seed as u64 % 200_000_000;
if get_route(src, &graph, dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &test_utils::TestLogger::new()).is_ok() {
continue 'load_endpoints;
fn generate_routes(bench: &mut Bencher) {
let mut d = test_utils::get_route_file().unwrap();
let graph = NetworkGraph::read(&mut d).unwrap();
+ let nodes = graph.read_only().nodes().clone();
// First, get 100 (source, destination) pairs for which route-getting actually succeeds...
let mut path_endpoints = Vec::new();
'load_endpoints: for _ in 0..100 {
loop {
seed *= 0xdeadbeef;
- let src = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let src = nodes.keys().skip(seed % nodes.len()).next().unwrap();
seed *= 0xdeadbeef;
- let dst = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let dst = nodes.keys().skip(seed % nodes.len()).next().unwrap();
let amt = seed as u64 % 1_000_000;
if get_route(src, &graph, dst, None, None, &[], amt, 42, &DummyLogger{}).is_ok() {
path_endpoints.push((src, dst, amt));
fn generate_mpp_routes(bench: &mut Bencher) {
let mut d = test_utils::get_route_file().unwrap();
let graph = NetworkGraph::read(&mut d).unwrap();
+ let nodes = graph.read_only().nodes().clone();
// First, get 100 (source, destination) pairs for which route-getting actually succeeds...
let mut path_endpoints = Vec::new();
'load_endpoints: for _ in 0..100 {
loop {
seed *= 0xdeadbeef;
- let src = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let src = nodes.keys().skip(seed % nodes.len()).next().unwrap();
seed *= 0xdeadbeef;
- let dst = graph.get_nodes().keys().skip(seed % graph.get_nodes().len()).next().unwrap();
+ let dst = nodes.keys().skip(seed % nodes.len()).next().unwrap();
let amt = seed as u64 % 1_000_000;
if get_route(src, &graph, dst, Some(InvoiceFeatures::known()), None, &[], amt, 42, &DummyLogger{}).is_ok() {
path_endpoints.push((src, dst, amt));
// You may not use this file except in accordance with one or both of these
// licenses.
-use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction};
+use ln::chan_utils::{HTLCOutputInCommitment, ChannelPublicKeys, HolderCommitmentTransaction, CommitmentTransaction, ChannelTransactionParameters, TrustedCommitmentTransaction, ClosingTransaction};
use ln::{chan_utils, msgs};
use chain::keysinterface::{Sign, InMemorySigner, BaseSign};
-use io;
use prelude::*;
use core::cmp;
use sync::{Mutex, Arc};
+#[cfg(test)] use sync::MutexGuard;
use bitcoin::blockdata::transaction::{Transaction, SigHashType};
use bitcoin::util::bip143;
use bitcoin::secp256k1;
use bitcoin::secp256k1::key::{SecretKey, PublicKey};
use bitcoin::secp256k1::{Secp256k1, Signature};
-use util::ser::{Writeable, Writer, Readable};
+use util::ser::{Writeable, Writer};
use io::Error;
-use ln::msgs::DecodeError;
/// Initial value for revoked commitment downward counter
pub const INITIAL_REVOKED_COMMITMENT_NUMBER: u64 = 1 << 48;
/// - When signing, the holder transaction has not been revoked
/// - When revoking, the holder transaction has not been signed
/// - The holder commitment number is monotonic and without gaps
+/// - The revoked holder commitment number is monotonic and without gaps
+/// - There is at least one unrevoked holder transaction at all times
/// - The counterparty commitment number is monotonic and without gaps
/// - The pre-derived keys and pre-built transaction in CommitmentTransaction were correctly built
///
/// Eventually we will probably want to expose a variant of this which would essentially
/// be what you'd want to run on a hardware wallet.
///
+/// Note that counterparty signatures on the holder transaction are not checked, but it should
+/// be in a complete implementation.
+///
/// Note that before we do so we should ensure its serialization format has backwards- and
/// forwards-compatibility prefix/suffixes!
#[derive(Clone)]
pub struct EnforcingSigner {
pub inner: InMemorySigner,
- /// The last counterparty commitment number we signed, backwards counting
- pub last_commitment_number: Arc<Mutex<Option<u64>>>,
- /// The last holder commitment number we revoked, backwards counting
- pub revoked_commitment: Arc<Mutex<u64>>,
+ /// Channel state used for policy enforcement
+ pub state: Arc<Mutex<EnforcementState>>,
pub disable_revocation_policy_check: bool,
}
impl EnforcingSigner {
/// Construct an EnforcingSigner
pub fn new(inner: InMemorySigner) -> Self {
+ let state = Arc::new(Mutex::new(EnforcementState::new()));
Self {
inner,
- last_commitment_number: Arc::new(Mutex::new(None)),
- revoked_commitment: Arc::new(Mutex::new(INITIAL_REVOKED_COMMITMENT_NUMBER)),
+ state,
disable_revocation_policy_check: false
}
}
/// Construct an EnforcingSigner with externally managed storage
///
/// Since there are multiple copies of this struct for each channel, some coordination is needed
- /// so that all copies are aware of revocations. A pointer to this state is provided here, usually
- /// by an implementation of KeysInterface.
- pub fn new_with_revoked(inner: InMemorySigner, revoked_commitment: Arc<Mutex<u64>>, disable_revocation_policy_check: bool) -> Self {
+ /// so that all copies are aware of enforcement state. A pointer to this state is provided
+ /// here, usually by an implementation of KeysInterface.
+ pub fn new_with_revoked(inner: InMemorySigner, state: Arc<Mutex<EnforcementState>>, disable_revocation_policy_check: bool) -> Self {
Self {
inner,
- last_commitment_number: Arc::new(Mutex::new(None)),
- revoked_commitment,
+ state,
disable_revocation_policy_check
}
}
+
+ #[cfg(test)]
+ pub fn get_enforcement_state(&self) -> MutexGuard<EnforcementState> {
+ self.state.lock().unwrap()
+ }
}
impl BaseSign for EnforcingSigner {
fn release_commitment_secret(&self, idx: u64) -> [u8; 32] {
{
- let mut revoked = self.revoked_commitment.lock().unwrap();
- assert!(idx == *revoked || idx == *revoked - 1, "can only revoke the current or next unrevoked commitment - trying {}, revoked {}", idx, *revoked);
- *revoked = idx;
+ let mut state = self.state.lock().unwrap();
+ assert!(idx == state.last_holder_revoked_commitment || idx == state.last_holder_revoked_commitment - 1, "can only revoke the current or next unrevoked commitment - trying {}, last revoked {}", idx, state.last_holder_revoked_commitment);
+ assert!(idx > state.last_holder_commitment, "cannot revoke the last holder commitment - attempted to revoke {} last commitment {}", idx, state.last_holder_commitment);
+ state.last_holder_revoked_commitment = idx;
}
self.inner.release_commitment_secret(idx)
}
+ fn validate_holder_commitment(&self, holder_tx: &HolderCommitmentTransaction) -> Result<(), ()> {
+ let mut state = self.state.lock().unwrap();
+ let idx = holder_tx.commitment_number();
+ assert!(idx == state.last_holder_commitment || idx == state.last_holder_commitment - 1, "expecting to validate the current or next holder commitment - trying {}, current {}", idx, state.last_holder_commitment);
+ state.last_holder_commitment = idx;
+ Ok(())
+ }
+
fn pubkeys(&self) -> &ChannelPublicKeys { self.inner.pubkeys() }
fn channel_keys_id(&self) -> [u8; 32] { self.inner.channel_keys_id() }
self.verify_counterparty_commitment_tx(commitment_tx, secp_ctx);
{
- let mut last_commitment_number_guard = self.last_commitment_number.lock().unwrap();
+ let mut state = self.state.lock().unwrap();
let actual_commitment_number = commitment_tx.commitment_number();
- let last_commitment_number = last_commitment_number_guard.unwrap_or(actual_commitment_number);
+ let last_commitment_number = state.last_counterparty_commitment;
// These commitment numbers are backwards counting. We expect either the same as the previously encountered,
// or the next one.
assert!(last_commitment_number == actual_commitment_number || last_commitment_number - 1 == actual_commitment_number, "{} doesn't come after {}", actual_commitment_number, last_commitment_number);
- *last_commitment_number_guard = Some(cmp::min(last_commitment_number, actual_commitment_number))
+ // Ensure that the counterparty doesn't get more than two broadcastable commitments -
+ // the last and the one we are trying to sign
+ assert!(actual_commitment_number >= state.last_counterparty_revoked_commitment - 2, "cannot sign a commitment if second to last wasn't revoked - signing {} revoked {}", actual_commitment_number, state.last_counterparty_revoked_commitment);
+ state.last_counterparty_commitment = cmp::min(last_commitment_number, actual_commitment_number)
}
Ok(self.inner.sign_counterparty_commitment(commitment_tx, secp_ctx).unwrap())
}
+ fn validate_counterparty_revocation(&self, idx: u64, _secret: &SecretKey) -> Result<(), ()> {
+ let mut state = self.state.lock().unwrap();
+ assert!(idx == state.last_counterparty_revoked_commitment || idx == state.last_counterparty_revoked_commitment - 1, "expecting to validate the current or next counterparty revocation - trying {}, current {}", idx, state.last_counterparty_revoked_commitment);
+ state.last_counterparty_revoked_commitment = idx;
+ Ok(())
+ }
+
fn sign_holder_commitment_and_htlcs(&self, commitment_tx: &HolderCommitmentTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<(Signature, Vec<Signature>), ()> {
let trusted_tx = self.verify_holder_commitment_tx(commitment_tx, secp_ctx);
let commitment_txid = trusted_tx.txid();
let holder_csv = self.inner.counterparty_selected_contest_delay();
- let revoked = self.revoked_commitment.lock().unwrap();
+ let state = self.state.lock().unwrap();
let commitment_number = trusted_tx.commitment_number();
- if *revoked - 1 != commitment_number && *revoked - 2 != commitment_number {
+ if state.last_holder_revoked_commitment - 1 != commitment_number && state.last_holder_revoked_commitment - 2 != commitment_number {
if !self.disable_revocation_policy_check {
panic!("can only sign the next two unrevoked commitment numbers, revoked={} vs requested={} for {}",
- *revoked, commitment_number, self.inner.commitment_seed[0])
+ state.last_holder_revoked_commitment, commitment_number, self.inner.commitment_seed[0])
}
}
Ok(self.inner.sign_counterparty_htlc_transaction(htlc_tx, input, amount, per_commitment_point, htlc, secp_ctx).unwrap())
}
- fn sign_closing_transaction(&self, closing_tx: &Transaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+ fn sign_closing_transaction(&self, closing_tx: &ClosingTransaction, secp_ctx: &Secp256k1<secp256k1::All>) -> Result<Signature, ()> {
+ closing_tx.verify(self.inner.funding_outpoint().into_bitcoin_outpoint())
+ .expect("derived different closing transaction");
Ok(self.inner.sign_closing_transaction(closing_tx, secp_ctx).unwrap())
}
impl Writeable for EnforcingSigner {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> {
+ // EnforcingSigner has two fields - `inner` ([`InMemorySigner`]) and `state`
+ // ([`EnforcementState`]). `inner` is serialized here and deserialized by
+ // [`KeysInterface::read_chan_signer`]. `state` is managed by [`KeysInterface`]
+ // and will be serialized as needed by the implementation of that trait.
self.inner.write(writer)?;
- let last = *self.last_commitment_number.lock().unwrap();
- last.write(writer)?;
Ok(())
}
}
-impl Readable for EnforcingSigner {
- fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
- let inner = Readable::read(reader)?;
- let last_commitment_number = Readable::read(reader)?;
- Ok(EnforcingSigner {
- inner,
- last_commitment_number: Arc::new(Mutex::new(last_commitment_number)),
- revoked_commitment: Arc::new(Mutex::new(INITIAL_REVOKED_COMMITMENT_NUMBER)),
- disable_revocation_policy_check: false,
- })
- }
-}
-
impl EnforcingSigner {
fn verify_counterparty_commitment_tx<'a, T: secp256k1::Signing + secp256k1::Verification>(&self, commitment_tx: &'a CommitmentTransaction, secp_ctx: &Secp256k1<T>) -> TrustedCommitmentTransaction<'a> {
commitment_tx.verify(&self.inner.get_channel_parameters().as_counterparty_broadcastable(),
.expect("derived different per-tx keys or built transaction")
}
}
+
+/// The state used by [`EnforcingSigner`] in order to enforce policy checks
+///
+/// This structure is maintained by KeysInterface since we may have multiple copies of
+/// the signer and they must coordinate their state.
+#[derive(Clone)]
+pub struct EnforcementState {
+ /// The last counterparty commitment number we signed, backwards counting
+ pub last_counterparty_commitment: u64,
+ /// The last counterparty commitment they revoked, backwards counting
+ pub last_counterparty_revoked_commitment: u64,
+ /// The last holder commitment number we revoked, backwards counting
+ pub last_holder_revoked_commitment: u64,
+ /// The last validated holder commitment number, backwards counting
+ pub last_holder_commitment: u64,
+}
+
+impl EnforcementState {
+ /// Enforcement state for a new channel
+ pub fn new() -> Self {
+ EnforcementState {
+ last_counterparty_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
+ last_counterparty_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
+ last_holder_revoked_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
+ last_holder_commitment: INITIAL_REVOKED_COMMITMENT_NUMBER,
+ }
+ }
+}
//! future, as well as generate and broadcast funding transactions handle payment preimages and a
//! few other things.
+use chain::keysinterface::SpendableOutputDescriptor;
use ln::msgs;
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
-use chain::keysinterface::SpendableOutputDescriptor;
+use routing::network_graph::NetworkUpdate;
use util::ser::{Writeable, Writer, MaybeReadable, Readable, VecReadWrapper, VecWriteWrapper};
use bitcoin::blockdata::script::Script;
/// payment is to pay an invoice or to send a spontaneous payment.
purpose: PaymentPurpose,
},
- /// Indicates an outbound payment we made succeeded (ie it made it all the way to its target
+ /// Indicates an outbound payment we made succeeded (i.e. it made it all the way to its target
/// and we got back the payment preimage for it).
+ ///
+ /// Note for MPP payments: in rare cases, this event may be preceded by a `PaymentFailed` event.
+ /// In this situation, you SHOULD treat this payment as having succeeded.
PaymentSent {
/// The preimage to the hash given to ChannelManager::send_payment.
/// Note that this serves as a payment receipt, if you wish to have such a thing, you must
/// the payment has failed, not just the route in question. If this is not set, you may
/// retry the payment via a different route.
rejected_by_dest: bool,
+ /// Any failure information conveyed via the Onion return packet by a node along the failed
+ /// payment route.
+ ///
+ /// Should be applied to the [`NetworkGraph`] so that routing decisions can take into
+ /// account the update. [`NetGraphMsgHandler`] is capable of doing this.
+ ///
+ /// [`NetworkGraph`]: crate::routing::network_graph::NetworkGraph
+ /// [`NetGraphMsgHandler`]: crate::routing::network_graph::NetGraphMsgHandler
+ network_update: Option<NetworkUpdate>,
+ /// For both single-path and multi-path payments, this is set if all paths of the payment have
+ /// failed. This will be set to false if (1) this is an MPP payment and (2) other parts of the
+ /// larger MPP payment were still in flight when this event was generated.
+ all_paths_failed: bool,
#[cfg(test)]
error_code: Option<u16>,
#[cfg(test)]
(0, payment_preimage, required),
});
},
- &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest,
+ &Event::PaymentFailed { ref payment_hash, ref rejected_by_dest, ref network_update, ref all_paths_failed,
#[cfg(test)]
ref error_code,
#[cfg(test)]
error_data.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_hash, required),
+ (1, network_update, option),
(2, rejected_by_dest, required),
+ (3, all_paths_failed, required),
});
},
&Event::PendingHTLCsForwardable { time_forwardable: _ } => {
let error_data = Readable::read(reader)?;
let mut payment_hash = PaymentHash([0; 32]);
let mut rejected_by_dest = false;
+ let mut network_update = None;
+ let mut all_paths_failed = Some(true);
read_tlv_fields!(reader, {
(0, payment_hash, required),
+ (1, network_update, ignorable),
(2, rejected_by_dest, required),
+ (3, all_paths_failed, option),
});
Ok(Some(Event::PaymentFailed {
payment_hash,
rejected_by_dest,
+ network_update,
+ all_paths_failed: all_paths_failed.unwrap(),
#[cfg(test)]
error_code,
#[cfg(test)]
/// The action which should be taken.
action: msgs::ErrorAction
},
- /// When a payment fails we may receive updates back from the hop where it failed. In such
- /// cases this event is generated so that we can inform the network graph of this information.
- PaymentFailureNetworkUpdate {
- /// The channel/node update which should be sent to NetGraphMsgHandler
- update: msgs::HTLCFailChannelUpdate,
- },
/// Query a peer for channels with funding transaction UTXOs in a block range.
SendChannelRangeQuery {
/// The node_id of this message recipient
/// Handles the given [`Event`].
///
/// See [`EventsProvider`] for details that must be considered when implementing this method.
- fn handle_event(&self, event: Event);
+ fn handle_event(&self, event: &Event);
}
-impl<F> EventHandler for F where F: Fn(Event) {
- fn handle_event(&self, event: Event) {
+impl<F> EventHandler for F where F: Fn(&Event) {
+ fn handle_event(&self, event: &Event) {
self(event)
}
}
write!(f, "DelayedPaymentOutput {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
}
&SpendableOutputDescriptor::StaticPaymentOutput(ref descriptor) => {
- write!(f, "DynamicOutputP2WPKH {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
+ write!(f, "StaticPaymentOutput {}:{} marked for spending", descriptor.outpoint.txid, descriptor.outpoint.index)?;
}
}
Ok(())
/// serialization buffer size
pub const MAX_BUF_SIZE: usize = 64 * 1024;
-/// A trait that is similar to std::io::Write but has one extra function which can be used to size
-/// buffers being written into.
-/// An impl is provided for any type that also impls std::io::Write which simply ignores size
-/// hints.
+/// A simplified version of std::io::Write that exists largely for backwards compatibility.
+/// An impl is provided for any type that also impls std::io::Write.
///
/// (C-not exported) as we only export serialization to/from byte arrays instead
pub trait Writer {
/// Writes the given buf out. See std::io::Write::write_all for more
fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error>;
- /// Hints that data of the given size is about the be written. This may not always be called
- /// prior to data being written and may be safely ignored.
- fn size_hint(&mut self, size: usize);
}
impl<W: Write> Writer for W {
fn write_all(&mut self, buf: &[u8]) -> Result<(), io::Error> {
<Self as io::Write>::write_all(self, buf)
}
- #[inline]
- fn size_hint(&mut self, _size: usize) { }
}
pub(crate) struct WriterWriteAdaptor<'a, W: Writer + 'a>(pub &'a mut W);
self.0.extend_from_slice(buf);
Ok(())
}
- #[inline]
- fn size_hint(&mut self, size: usize) {
- self.0.reserve_exact(size);
- }
}
/// Writer that only tracks the amount of data written - useful if you need to calculate the length
self.0 += buf.len();
Ok(())
}
- #[inline]
- fn size_hint(&mut self, _size: usize) {}
}
/// Essentially std::io::Take but a bit simpler and with a method to walk the underlying stream
}
}
+// HashSet
+impl<T> Writeable for HashSet<T>
+where T: Writeable + Eq + Hash
+{
+ #[inline]
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ (self.len() as u16).write(w)?;
+ for item in self.iter() {
+ item.write(w)?;
+ }
+ Ok(())
+ }
+}
+
+impl<T> Readable for HashSet<T>
+where T: Readable + Eq + Hash
+{
+ #[inline]
+ fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+ let len: u16 = Readable::read(r)?;
+ let mut ret = HashSet::with_capacity(len as usize);
+ for _ in 0..len {
+ if !ret.insert(T::read(r)?) {
+ return Err(DecodeError::InvalidValue)
+ }
+ }
+ Ok(ret)
+ }
+}
+
// Vectors
impl Writeable for Vec<u8> {
#[inline]
self.2.write(w)
}
}
+
+impl Readable for () {
+ fn read<R: Read>(_r: &mut R) -> Result<Self, DecodeError> {
+ Ok(())
+ }
+}
$field = ser::Readable::read(&mut $reader)?;
}};
($reader: expr, $field: ident, vec_type) => {{
- $field = Some(ser::Readable::read(&mut $reader)?);
+ let f: ::util::ser::VecReadWrapper<_> = ser::Readable::read(&mut $reader)?;
+ $field = Some(f.0);
}};
($reader: expr, $field: ident, option) => {{
$field = Some(ser::Readable::read(&mut $reader)?);
} }
}
-macro_rules! impl_writeable {
- ($st:ident, $len: expr, {$($field:ident),*}) => {
+macro_rules! impl_writeable_msg {
+ ($st:ident, {$($field:ident),* $(,)*}, {$(($type: expr, $tlvfield: ident, $fieldty: tt)),* $(,)*}) => {
impl ::util::ser::Writeable for $st {
fn write<W: ::util::ser::Writer>(&self, w: &mut W) -> Result<(), $crate::io::Error> {
- if $len != 0 {
- w.size_hint($len);
- }
- #[cfg(any(test, feature = "fuzztarget"))]
- {
- // In tests, assert that the hard-coded length matches the actual one
- if $len != 0 {
- let mut len_calc = ::util::ser::LengthCalculatingWriter(0);
- $( self.$field.write(&mut len_calc).expect("No in-memory data may fail to serialize"); )*
- assert_eq!(len_calc.0, $len);
- assert_eq!(self.serialized_length(), $len);
- }
- }
$( self.$field.write(w)?; )*
+ encode_tlv_stream!(w, {$(($type, self.$tlvfield, $fieldty)),*});
Ok(())
}
-
- #[inline]
- fn serialized_length(&self) -> usize {
- if $len == 0 || cfg!(any(test, feature = "fuzztarget")) {
- let mut len_calc = 0;
- $( len_calc += self.$field.serialized_length(); )*
- if $len != 0 {
- // In tests, assert that the hard-coded length matches the actual one
- assert_eq!(len_calc, $len);
- } else {
- return len_calc;
- }
- }
- $len
- }
}
-
impl ::util::ser::Readable for $st {
fn read<R: $crate::io::Read>(r: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
+ $(let $field = ::util::ser::Readable::read(r)?;)*
+ $(init_tlv_field_var!($tlvfield, $fieldty);)*
+ decode_tlv_stream!(r, {$(($type, $tlvfield, $fieldty)),*});
Ok(Self {
- $($field: ::util::ser::Readable::read(r)?),*
+ $($field),*,
+ $($tlvfield),*
})
}
}
}
}
-macro_rules! impl_writeable_len_match {
- ($struct: ident, $cmp: tt, ($calc_len: expr), {$({$match: pat, $length: expr}),*}, {$($field:ident),*}) => {
- impl Writeable for $struct {
- fn write<W: Writer>(&self, w: &mut W) -> Result<(), $crate::io::Error> {
- let len = match *self {
- $($match => $length,)*
- };
- w.size_hint(len);
- #[cfg(any(test, feature = "fuzztarget"))]
- {
- // In tests, assert that the hard-coded length matches the actual one
- let mut len_calc = ::util::ser::LengthCalculatingWriter(0);
- $( self.$field.write(&mut len_calc).expect("No in-memory data may fail to serialize"); )*
- assert!(len_calc.0 $cmp len);
- assert_eq!(len_calc.0, self.serialized_length());
- }
+
+macro_rules! impl_writeable {
+ ($st:ident, {$($field:ident),*}) => {
+ impl ::util::ser::Writeable for $st {
+ fn write<W: ::util::ser::Writer>(&self, w: &mut W) -> Result<(), $crate::io::Error> {
$( self.$field.write(w)?; )*
Ok(())
}
#[inline]
fn serialized_length(&self) -> usize {
- if $calc_len || cfg!(any(test, feature = "fuzztarget")) {
- let mut len_calc = 0;
- $( len_calc += self.$field.serialized_length(); )*
- if !$calc_len {
- assert_eq!(len_calc, match *self {
- $($match => $length,)*
- });
- }
- return len_calc
- }
- match *self {
- $($match => $length,)*
- }
+ let mut len_calc = 0;
+ $( len_calc += self.$field.serialized_length(); )*
+ return len_calc;
}
}
- impl ::util::ser::Readable for $struct {
- fn read<R: $crate::io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+ impl ::util::ser::Readable for $st {
+ fn read<R: $crate::io::Read>(r: &mut R) -> Result<Self, ::ln::msgs::DecodeError> {
Ok(Self {
- $($field: Readable::read(r)?),*
+ $($field: ::util::ser::Readable::read(r)?),*
})
}
}
- };
- ($struct: ident, $cmp: tt, {$({$match: pat, $length: expr}),*}, {$($field:ident),*}) => {
- impl_writeable_len_match!($struct, $cmp, (true), { $({ $match, $length }),* }, { $($field),* });
- };
- ($struct: ident, {$({$match: pat, $length: expr}),*}, {$($field:ident),*}) => {
- impl_writeable_len_match!($struct, ==, (false), { $({ $match, $length }),* }, { $($field),* });
}
}
$field.0.unwrap()
};
($field: ident, vec_type) => {
- $field.unwrap().0
+ $field.unwrap()
};
}
let mut $field = ::util::ser::OptionDeserWrapper(None);
};
($field: ident, vec_type) => {
- let mut $field = Some(::util::ser::VecReadWrapper(Vec::new()));
+ let mut $field = Some(Vec::new());
};
($field: ident, option) => {
let mut $field = None;
use ln::msgs;
use ln::msgs::OptionalField;
use ln::script::ShutdownScript;
-use util::enforcing_trait_impls::{EnforcingSigner, INITIAL_REVOKED_COMMITMENT_NUMBER};
+use util::enforcing_trait_impls::{EnforcingSigner, EnforcementState};
use util::events;
use util::logger::{Logger, Level, Record};
use util::ser::{Readable, ReadableArgs, Writer, Writeable};
self.0.extend_from_slice(buf);
Ok(())
}
- fn size_hint(&mut self, size: usize) {
- self.0.reserve_exact(size);
- }
}
pub struct TestFeeEstimator {
fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> EnforcingSigner { unreachable!(); }
fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
- fn read_chan_signer(&self, reader: &[u8]) -> Result<Self::Signer, msgs::DecodeError> {
- EnforcingSigner::read(&mut io::Cursor::new(reader))
+ fn read_chan_signer(&self, mut reader: &[u8]) -> Result<Self::Signer, msgs::DecodeError> {
+ let inner: InMemorySigner = Readable::read(&mut reader)?;
+ let state = Arc::new(Mutex::new(EnforcementState::new()));
+
+ Ok(EnforcingSigner::new_with_revoked(
+ inner,
+ state,
+ false
+ ))
}
fn sign_invoice(&self, _invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> { unreachable!(); }
}
self.chan_upds_recvd.fetch_add(1, Ordering::AcqRel);
Err(msgs::LightningError { err: "".to_owned(), action: msgs::ErrorAction::IgnoreError })
}
- fn handle_htlc_fail_channel_update(&self, _update: &msgs::HTLCFailChannelUpdate) {}
fn get_next_channel_announcements(&self, starting_point: u64, batch_amount: u8) -> Vec<(msgs::ChannelAnnouncement, Option<msgs::ChannelUpdate>, Option<msgs::ChannelUpdate>)> {
let mut chan_anns = Vec::new();
const TOTAL_UPDS: u64 = 100;
pub override_session_priv: Mutex<Option<[u8; 32]>>,
pub override_channel_id_priv: Mutex<Option<[u8; 32]>>,
pub disable_revocation_policy_check: bool,
- revoked_commitments: Mutex<HashMap<[u8;32], Arc<Mutex<u64>>>>,
+ enforcement_states: Mutex<HashMap<[u8;32], Arc<Mutex<EnforcementState>>>>,
expectations: Mutex<Option<VecDeque<OnGetShutdownScriptpubkey>>>,
}
fn get_channel_signer(&self, inbound: bool, channel_value_satoshis: u64) -> EnforcingSigner {
let keys = self.backing.get_channel_signer(inbound, channel_value_satoshis);
- let revoked_commitment = self.make_revoked_commitment_cell(keys.commitment_seed);
- EnforcingSigner::new_with_revoked(keys, revoked_commitment, self.disable_revocation_policy_check)
+ let state = self.make_enforcement_state_cell(keys.commitment_seed);
+ EnforcingSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check)
}
fn get_secure_random_bytes(&self) -> [u8; 32] {
let mut reader = io::Cursor::new(buffer);
let inner: InMemorySigner = Readable::read(&mut reader)?;
- let revoked_commitment = self.make_revoked_commitment_cell(inner.commitment_seed);
-
- let last_commitment_number = Readable::read(&mut reader)?;
+ let state = self.make_enforcement_state_cell(inner.commitment_seed);
- Ok(EnforcingSigner {
+ Ok(EnforcingSigner::new_with_revoked(
inner,
- last_commitment_number: Arc::new(Mutex::new(last_commitment_number)),
- revoked_commitment,
- disable_revocation_policy_check: self.disable_revocation_policy_check,
- })
+ state,
+ self.disable_revocation_policy_check
+ ))
}
fn sign_invoice(&self, invoice_preimage: Vec<u8>) -> Result<RecoverableSignature, ()> {
override_session_priv: Mutex::new(None),
override_channel_id_priv: Mutex::new(None),
disable_revocation_policy_check: false,
- revoked_commitments: Mutex::new(HashMap::new()),
+ enforcement_states: Mutex::new(HashMap::new()),
expectations: Mutex::new(None),
}
}
pub fn derive_channel_keys(&self, channel_value_satoshis: u64, id: &[u8; 32]) -> EnforcingSigner {
let keys = self.backing.derive_channel_keys(channel_value_satoshis, id);
- let revoked_commitment = self.make_revoked_commitment_cell(keys.commitment_seed);
- EnforcingSigner::new_with_revoked(keys, revoked_commitment, self.disable_revocation_policy_check)
+ let state = self.make_enforcement_state_cell(keys.commitment_seed);
+ EnforcingSigner::new_with_revoked(keys, state, self.disable_revocation_policy_check)
}
- fn make_revoked_commitment_cell(&self, commitment_seed: [u8; 32]) -> Arc<Mutex<u64>> {
- let mut revoked_commitments = self.revoked_commitments.lock().unwrap();
- if !revoked_commitments.contains_key(&commitment_seed) {
- revoked_commitments.insert(commitment_seed, Arc::new(Mutex::new(INITIAL_REVOKED_COMMITMENT_NUMBER)));
+ fn make_enforcement_state_cell(&self, commitment_seed: [u8; 32]) -> Arc<Mutex<EnforcementState>> {
+ let mut states = self.enforcement_states.lock().unwrap();
+ if !states.contains_key(&commitment_seed) {
+ let state = EnforcementState::new();
+ states.insert(commitment_seed, Arc::new(Mutex::new(state)));
}
- let cell = revoked_commitments.get(&commitment_seed).unwrap();
+ let cell = states.get(&commitment_seed).unwrap();
Arc::clone(cell)
}
}