Merge pull request #651 from naumenkogs/2020-06-routing-data-improvements

Routing improvements
This commit is contained in:
Matt Corallo 2020-07-27 10:18:13 -07:00 committed by GitHub
commit 779ff6721b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 194 additions and 45 deletions

View file

@ -31,12 +31,12 @@ GEN_TEST msg_update_fee msg_targets::
GEN_TEST msg_update_fulfill_htlc msg_targets:: GEN_TEST msg_update_fulfill_htlc msg_targets::
GEN_TEST msg_channel_announcement msg_targets:: GEN_TEST msg_channel_announcement msg_targets::
GEN_TEST msg_channel_update msg_targets::
GEN_TEST msg_node_announcement msg_targets:: GEN_TEST msg_node_announcement msg_targets::
GEN_TEST msg_update_add_htlc msg_targets:: GEN_TEST msg_update_add_htlc msg_targets::
GEN_TEST msg_error_message msg_targets:: GEN_TEST msg_error_message msg_targets::
GEN_TEST msg_onion_hop_data msg_targets:: GEN_TEST msg_channel_update msg_targets::
GEN_TEST msg_onion_hop_data msg_targets::
GEN_TEST msg_ping msg_targets:: GEN_TEST msg_ping msg_targets::
GEN_TEST msg_pong msg_targets:: GEN_TEST msg_pong msg_targets::

View file

@ -29,11 +29,11 @@ GEN_TEST UpdateFee test_msg ""
GEN_TEST UpdateFulfillHTLC test_msg "" GEN_TEST UpdateFulfillHTLC test_msg ""
GEN_TEST ChannelAnnouncement test_msg_exact "" GEN_TEST ChannelAnnouncement test_msg_exact ""
GEN_TEST ChannelUpdate test_msg_exact ""
GEN_TEST NodeAnnouncement test_msg_exact "" GEN_TEST NodeAnnouncement test_msg_exact ""
GEN_TEST UpdateAddHTLC test_msg_hole ", 85, 33" GEN_TEST UpdateAddHTLC test_msg_hole ", 85, 33"
GEN_TEST ErrorMessage test_msg_hole ", 32, 2" GEN_TEST ErrorMessage test_msg_hole ", 32, 2"
GEN_TEST ChannelUpdate test_msg_hole ", 108, 1"
GEN_TEST Init test_msg_simple "" GEN_TEST Init test_msg_simple ""
GEN_TEST OnionHopData test_msg_simple "" GEN_TEST OnionHopData test_msg_simple ""

View file

@ -16,10 +16,10 @@ pub mod msg_update_fail_malformed_htlc;
pub mod msg_update_fee; pub mod msg_update_fee;
pub mod msg_update_fulfill_htlc; pub mod msg_update_fulfill_htlc;
pub mod msg_channel_announcement; pub mod msg_channel_announcement;
pub mod msg_channel_update;
pub mod msg_node_announcement; pub mod msg_node_announcement;
pub mod msg_update_add_htlc; pub mod msg_update_add_htlc;
pub mod msg_error_message; pub mod msg_error_message;
pub mod msg_channel_update;
pub mod msg_init; pub mod msg_init;
pub mod msg_onion_hop_data; pub mod msg_onion_hop_data;
pub mod msg_ping; pub mod msg_ping;

View file

@ -8,11 +8,11 @@ use utils::test_logger;
#[inline] #[inline]
pub fn msg_channel_update_test<Out: test_logger::Output>(data: &[u8], _out: Out) { pub fn msg_channel_update_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
test_msg_exact!(msgs::ChannelUpdate, data); test_msg_hole!(msgs::ChannelUpdate, data, 108, 1);
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn msg_channel_update_run(data: *const u8, datalen: usize) { pub extern "C" fn msg_channel_update_run(data: *const u8, datalen: usize) {
let data = unsafe { std::slice::from_raw_parts(data, datalen) }; let data = unsafe { std::slice::from_raw_parts(data, datalen) };
test_msg_exact!(msgs::ChannelUpdate, data); test_msg_hole!(msgs::ChannelUpdate, data, 108, 1);
} }

View file

@ -78,8 +78,8 @@ macro_rules! test_msg_exact {
} }
} }
// Tests a message that must survive roundtrip exactly, modulo one "hole" which may be set to 0s on // Tests a message that must survive roundtrip exactly, modulo one "hole" which may be set to
// re-serialization. // any value on re-serialization.
#[macro_export] #[macro_export]
macro_rules! test_msg_hole { macro_rules! test_msg_hole {
($MsgType: path, $data: ident, $hole: expr, $hole_len: expr) => { ($MsgType: path, $data: ident, $hole: expr, $hole_len: expr) => {

View file

@ -172,12 +172,12 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
let _ = net_graph_msg_handler.handle_channel_announcement(&decode_msg_with_len16!(msgs::ChannelAnnouncement, 64*4, 32+8+33*4)); let _ = net_graph_msg_handler.handle_channel_announcement(&decode_msg_with_len16!(msgs::ChannelAnnouncement, 64*4, 32+8+33*4));
}, },
2 => { 2 => {
let _ = net_graph_msg_handler.handle_channel_update(&decode_msg!(msgs::ChannelUpdate, 128)); let _ = net_graph_msg_handler.handle_channel_update(&decode_msg!(msgs::ChannelUpdate, 136));
}, },
3 => { 3 => {
match get_slice!(1)[0] { match get_slice!(1)[0] {
0 => { 0 => {
net_graph_msg_handler.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {msg: decode_msg!(msgs::ChannelUpdate, 128)}); net_graph_msg_handler.handle_htlc_fail_channel_update(&msgs::HTLCFailChannelUpdate::ChannelUpdateMessage {msg: decode_msg!(msgs::ChannelUpdate, 136)});
}, },
1 => { 1 => {
let short_channel_id = slice_to_be64(get_slice!(8)); let short_channel_id = slice_to_be64(get_slice!(8));

View file

@ -22,10 +22,10 @@ void msg_update_fail_malformed_htlc_run(const unsigned char* data, size_t data_l
void msg_update_fee_run(const unsigned char* data, size_t data_len); void msg_update_fee_run(const unsigned char* data, size_t data_len);
void msg_update_fulfill_htlc_run(const unsigned char* data, size_t data_len); void msg_update_fulfill_htlc_run(const unsigned char* data, size_t data_len);
void msg_channel_announcement_run(const unsigned char* data, size_t data_len); void msg_channel_announcement_run(const unsigned char* data, size_t data_len);
void msg_channel_update_run(const unsigned char* data, size_t data_len);
void msg_node_announcement_run(const unsigned char* data, size_t data_len); void msg_node_announcement_run(const unsigned char* data, size_t data_len);
void msg_update_add_htlc_run(const unsigned char* data, size_t data_len); void msg_update_add_htlc_run(const unsigned char* data, size_t data_len);
void msg_error_message_run(const unsigned char* data, size_t data_len); void msg_error_message_run(const unsigned char* data, size_t data_len);
void msg_channel_update_run(const unsigned char* data, size_t data_len);
void msg_onion_hop_data_run(const unsigned char* data, size_t data_len); void msg_onion_hop_data_run(const unsigned char* data, size_t data_len);
void msg_ping_run(const unsigned char* data, size_t data_len); void msg_ping_run(const unsigned char* data, size_t data_len);
void msg_pong_run(const unsigned char* data, size_t data_len); void msg_pong_run(const unsigned char* data, size_t data_len);

View file

@ -3128,6 +3128,18 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
self.our_htlc_minimum_msat self.our_htlc_minimum_msat
} }
/// Allowed in any state (including after shutdown)
pub fn get_announced_htlc_max_msat(&self) -> u64 {
return cmp::min(
// Upper bound by capacity. We make it a bit less than full capacity to prevent attempts
// to use full capacity. This is an effort to reduce routing failures, because in many cases
// channel might have been used to route very small values (either by honest users or as DoS).
self.channel_value_satoshis * 9 / 10,
Channel::<ChanSigner>::get_our_max_htlc_value_in_flight_msat(self.channel_value_satoshis)
);
}
/// Allowed in any state (including after shutdown) /// Allowed in any state (including after shutdown)
pub fn get_their_htlc_minimum_msat(&self) -> u64 { pub fn get_their_htlc_minimum_msat(&self) -> u64 {
self.our_htlc_minimum_msat self.our_htlc_minimum_msat

View file

@ -34,7 +34,7 @@ use ln::features::{InitFeatures, NodeFeatures};
use routing::router::{Route, RouteHop}; use routing::router::{Route, RouteHop};
use ln::msgs; use ln::msgs;
use ln::onion_utils; use ln::onion_utils;
use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; use ln::msgs::{ChannelMessageHandler, DecodeError, LightningError, OptionalField};
use chain::keysinterface::{ChannelKeys, KeysInterface, KeysManager, InMemoryChannelKeys}; use chain::keysinterface::{ChannelKeys, KeysInterface, KeysManager, InMemoryChannelKeys};
use util::config::UserConfig; use util::config::UserConfig;
use util::{byte_utils, events}; use util::{byte_utils, events};
@ -1186,7 +1186,8 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry)); res.extend_from_slice(&byte_utils::be32_to_array(msg.cltv_expiry));
} }
else if code == 0x1000 | 20 { else if code == 0x1000 | 20 {
res.extend_from_slice(&byte_utils::be16_to_array(chan_update.contents.flags)); // 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(&chan_update.encode_with_len()[..]); res.extend_from_slice(&chan_update.encode_with_len()[..]);
} }
@ -1212,9 +1213,10 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
chain_hash: self.genesis_hash, chain_hash: self.genesis_hash,
short_channel_id: short_channel_id, short_channel_id: short_channel_id,
timestamp: chan.get_update_time_counter(), timestamp: chan.get_update_time_counter(),
flags: (!were_node_one) as u16 | ((!chan.is_live() as u16) << 1), flags: (!were_node_one) as u8 | ((!chan.is_live() as u8) << 1),
cltv_expiry_delta: CLTV_EXPIRY_DELTA, cltv_expiry_delta: CLTV_EXPIRY_DELTA,
htlc_minimum_msat: chan.get_our_htlc_minimum_msat(), htlc_minimum_msat: chan.get_our_htlc_minimum_msat(),
htlc_maximum_msat: OptionalField::Present(chan.get_announced_htlc_max_msat()),
fee_base_msat: chan.get_our_fee_base_msat(&self.fee_estimator), fee_base_msat: chan.get_our_fee_base_msat(&self.fee_estimator),
fee_proportional_millionths: chan.get_fee_proportional_millionths(), fee_proportional_millionths: chan.get_fee_proportional_millionths(),
excess_data: Vec::new(), excess_data: Vec::new(),
@ -2494,7 +2496,8 @@ impl<ChanSigner: ChannelKeys, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
let reason = if let Ok(upd) = self.get_channel_update(chan) { let reason = if let Ok(upd) = self.get_channel_update(chan) {
onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{ onion_utils::build_first_hop_failure_packet(incoming_shared_secret, error_code, &{
let mut res = Vec::with_capacity(8 + 128); let mut res = Vec::with_capacity(8 + 128);
res.extend_from_slice(&byte_utils::be16_to_array(upd.contents.flags)); // 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.extend_from_slice(&upd.encode_with_len()[..]);
res res
}[..]) }[..])

View file

@ -15,7 +15,7 @@ use ln::{chan_utils, onion_utils};
use routing::router::{Route, RouteHop, get_route}; use routing::router::{Route, RouteHop, get_route};
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use ln::msgs; use ln::msgs;
use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction}; use ln::msgs::{ChannelMessageHandler,RoutingMessageHandler,HTLCFailChannelUpdate, ErrorAction, OptionalField};
use util::enforcing_trait_impls::EnforcingChannelKeys; use util::enforcing_trait_impls::EnforcingChannelKeys;
use util::{byte_utils, test_utils}; use util::{byte_utils, test_utils};
use util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider}; use util::events::{Event, EventsProvider, MessageSendEvent, MessageSendEventsProvider};
@ -6058,6 +6058,7 @@ impl msgs::ChannelUpdate {
flags: 0, flags: 0,
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: vec![], excess_data: vec![],

View file

@ -427,9 +427,10 @@ pub(crate) struct UnsignedChannelUpdate {
pub(crate) chain_hash: BlockHash, pub(crate) chain_hash: BlockHash,
pub(crate) short_channel_id: u64, pub(crate) short_channel_id: u64,
pub(crate) timestamp: u32, pub(crate) timestamp: u32,
pub(crate) flags: u16, pub(crate) flags: u8,
pub(crate) cltv_expiry_delta: u16, pub(crate) cltv_expiry_delta: u16,
pub(crate) htlc_minimum_msat: u64, pub(crate) htlc_minimum_msat: u64,
pub(crate) htlc_maximum_msat: OptionalField<u64>,
pub(crate) fee_base_msat: u32, pub(crate) fee_base_msat: u32,
pub(crate) fee_proportional_millionths: u32, pub(crate) fee_proportional_millionths: u32,
pub(crate) excess_data: Vec<u8>, pub(crate) excess_data: Vec<u8>,
@ -517,7 +518,7 @@ pub enum HTLCFailChannelUpdate {
/// As we wish to serialize these differently from Option<T>s (Options get a tag byte, but /// 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 /// OptionalFeild simply gets Present if there are enough bytes to read into it), we have a
/// separate enum type for them. /// separate enum type for them.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Debug)]
pub enum OptionalField<T> { pub enum OptionalField<T> {
/// Optional field is included in message /// Optional field is included in message
Present(T), Present(T),
@ -742,6 +743,26 @@ impl Readable for OptionalField<Script> {
} }
} }
impl Writeable for OptionalField<u64> {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
match *self {
OptionalField::Present(ref value) => {
value.write(w)?;
},
OptionalField::Absent => {}
}
Ok(())
}
}
impl Readable for OptionalField<u64> {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let value: u64 = Readable::read(r)?;
Ok(OptionalField::Present(value))
}
}
impl_writeable_len_match!(AcceptChannel, { impl_writeable_len_match!(AcceptChannel, {
{AcceptChannel{ shutdown_scriptpubkey: OptionalField::Present(ref script), .. }, 270 + 2 + script.len()}, {AcceptChannel{ shutdown_scriptpubkey: OptionalField::Present(ref script), .. }, 270 + 2 + script.len()},
{_, 270} {_, 270}
@ -1180,15 +1201,23 @@ impl_writeable_len_match!(ChannelAnnouncement, {
impl Writeable for UnsignedChannelUpdate { impl Writeable for UnsignedChannelUpdate {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> { fn write<W: Writer>(&self, w: &mut W) -> Result<(), ::std::io::Error> {
w.size_hint(64 + self.excess_data.len()); 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.chain_hash.write(w)?;
self.short_channel_id.write(w)?; self.short_channel_id.write(w)?;
self.timestamp.write(w)?; self.timestamp.write(w)?;
self.flags.write(w)?; let all_flags = self.flags as u16 | ((message_flags as u16) << 8);
all_flags.write(w)?;
self.cltv_expiry_delta.write(w)?; self.cltv_expiry_delta.write(w)?;
self.htlc_minimum_msat.write(w)?; self.htlc_minimum_msat.write(w)?;
self.fee_base_msat.write(w)?; self.fee_base_msat.write(w)?;
self.fee_proportional_millionths.write(w)?; self.fee_proportional_millionths.write(w)?;
self.htlc_maximum_msat.write(w)?;
w.write_all(&self.excess_data[..])?; w.write_all(&self.excess_data[..])?;
Ok(()) Ok(())
} }
@ -1196,15 +1225,22 @@ impl Writeable for UnsignedChannelUpdate {
impl Readable for UnsignedChannelUpdate { impl Readable for UnsignedChannelUpdate {
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> { fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
let has_htlc_maximum_msat;
Ok(Self { Ok(Self {
chain_hash: Readable::read(r)?, chain_hash: Readable::read(r)?,
short_channel_id: Readable::read(r)?, short_channel_id: Readable::read(r)?,
timestamp: Readable::read(r)?, timestamp: Readable::read(r)?,
flags: Readable::read(r)?, flags: {
let flags: u16 = Readable::read(r)?;
let message_flags = flags >> 8;
has_htlc_maximum_msat = (message_flags as i32 & 1) == 1;
flags as u8
},
cltv_expiry_delta: Readable::read(r)?, cltv_expiry_delta: Readable::read(r)?,
htlc_minimum_msat: Readable::read(r)?, htlc_minimum_msat: Readable::read(r)?,
fee_base_msat: Readable::read(r)?, fee_base_msat: Readable::read(r)?,
fee_proportional_millionths: Readable::read(r)?, fee_proportional_millionths: Readable::read(r)?,
htlc_maximum_msat: if has_htlc_maximum_msat { Readable::read(r)? } else { OptionalField::Absent },
excess_data: { excess_data: {
let mut excess_data = vec![]; let mut excess_data = vec![];
r.read_to_end(&mut excess_data)?; r.read_to_end(&mut excess_data)?;
@ -1597,7 +1633,7 @@ mod tests {
do_encoding_node_announcement(false, false, true, false, true, false, false); do_encoding_node_announcement(false, false, true, false, true, false, false);
} }
fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool) { fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool, excess_data: bool) {
let secp_ctx = Secp256k1::new(); let secp_ctx = Secp256k1::new();
let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx); let (privkey_1, _) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101")); let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
@ -1605,12 +1641,13 @@ mod tests {
chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(), chain_hash: BlockHash::from_hex("6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000").unwrap(),
short_channel_id: 2316138423780173, short_channel_id: 2316138423780173,
timestamp: 20190119, timestamp: 20190119,
flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 } | if htlc_maximum_msat { 1 << 8 } else { 0 }, flags: if direction { 1 } else { 0 } | if disable { 1 << 1 } else { 0 },
cltv_expiry_delta: 144, cltv_expiry_delta: 144,
htlc_minimum_msat: 1000000, htlc_minimum_msat: 1000000,
htlc_maximum_msat: if htlc_maximum_msat { OptionalField::Present(131355275467161) } else { OptionalField::Absent },
fee_base_msat: 10000, fee_base_msat: 10000,
fee_proportional_millionths: 20, fee_proportional_millionths: 20,
excess_data: if htlc_maximum_msat { vec![0, 0, 0, 0, 59, 154, 202, 0] } else { Vec::new() } excess_data: if excess_data { vec![0, 0, 0, 0, 59, 154, 202, 0] } else { Vec::new() }
}; };
let channel_update = msgs::ChannelUpdate { let channel_update = msgs::ChannelUpdate {
signature: sig_1, signature: sig_1,
@ -1636,6 +1673,9 @@ mod tests {
} }
target_value.append(&mut hex::decode("009000000000000f42400000271000000014").unwrap()); target_value.append(&mut hex::decode("009000000000000f42400000271000000014").unwrap());
if htlc_maximum_msat { if htlc_maximum_msat {
target_value.append(&mut hex::decode("0000777788889999").unwrap());
}
if excess_data {
target_value.append(&mut hex::decode("000000003b9aca00").unwrap()); target_value.append(&mut hex::decode("000000003b9aca00").unwrap());
} }
assert_eq!(encoded_value, target_value); assert_eq!(encoded_value, target_value);
@ -1643,11 +1683,16 @@ mod tests {
#[test] #[test]
fn encoding_channel_update() { fn encoding_channel_update() {
do_encoding_channel_update(false, false, false); do_encoding_channel_update(false, false, false, false);
do_encoding_channel_update(true, false, false); do_encoding_channel_update(false, false, false, true);
do_encoding_channel_update(false, true, false); do_encoding_channel_update(true, false, false, false);
do_encoding_channel_update(false, false, true); do_encoding_channel_update(true, false, false, true);
do_encoding_channel_update(true, true, true); do_encoding_channel_update(false, true, false, false);
do_encoding_channel_update(false, true, false, true);
do_encoding_channel_update(false, false, true, false);
do_encoding_channel_update(false, false, true, true);
do_encoding_channel_update(true, true, true, false);
do_encoding_channel_update(true, true, true, true);
} }
fn do_encoding_open_channel(random_bit: bool, shutdown: bool) { fn do_encoding_open_channel(random_bit: bool, shutdown: bool) {

View file

@ -11,7 +11,7 @@ use bitcoin::blockdata::opcodes;
use chain::chaininterface::{ChainError, ChainWatchInterface}; use chain::chaininterface::{ChainError, ChainWatchInterface};
use ln::features::{ChannelFeatures, NodeFeatures}; use ln::features::{ChannelFeatures, NodeFeatures};
use ln::msgs::{DecodeError,ErrorAction,LightningError,RoutingMessageHandler,NetAddress}; use ln::msgs::{DecodeError, ErrorAction, LightningError, RoutingMessageHandler, NetAddress, OptionalField, MAX_VALUE_MSAT};
use ln::msgs; use ln::msgs;
use util::ser::{Writeable, Readable, Writer}; use util::ser::{Writeable, Readable, Writer};
use util::logger::Logger; use util::logger::Logger;
@ -90,8 +90,8 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError}); return Err(LightningError{err: "Channel announcement node had a channel with itself".to_owned(), action: ErrorAction::IgnoreError});
} }
let checked_utxo = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) { let utxo_value = match self.chain_monitor.get_chain_utxo(msg.contents.chain_hash, msg.contents.short_channel_id) {
Ok((script_pubkey, _value)) => { Ok((script_pubkey, value)) => {
let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2) let expected_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
.push_slice(&msg.contents.bitcoin_key_1.serialize()) .push_slice(&msg.contents.bitcoin_key_1.serialize())
.push_slice(&msg.contents.bitcoin_key_2.serialize()) .push_slice(&msg.contents.bitcoin_key_2.serialize())
@ -102,11 +102,11 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
} }
//TODO: Check if value is worth storing, use it to inform routing, and compare it //TODO: Check if value is worth storing, use it to inform routing, and compare it
//to the new HTLC max field in channel_update //to the new HTLC max field in channel_update
true Some(value)
}, },
Err(ChainError::NotSupported) => { Err(ChainError::NotSupported) => {
// Tentatively accept, potentially exposing us to DoS attacks // Tentatively accept, potentially exposing us to DoS attacks
false None
}, },
Err(ChainError::NotWatched) => { Err(ChainError::NotWatched) => {
return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError}); return Err(LightningError{err: format!("Channel announced on an unknown chain ({})", msg.contents.chain_hash.encode().to_hex()), action: ErrorAction::IgnoreError});
@ -115,7 +115,7 @@ impl<C: Deref + Sync + Send, L: Deref + Sync + Send> RoutingMessageHandler for N
return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError}); return Err(LightningError{err: "Channel announced without corresponding UTXO entry".to_owned(), action: ErrorAction::IgnoreError});
}, },
}; };
let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, checked_utxo, Some(&self.secp_ctx)); let result = self.network_graph.write().unwrap().update_channel_from_announcement(msg, utxo_value, Some(&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 { "" }); 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 { "" });
result result
} }
@ -215,6 +215,8 @@ pub struct DirectionalChannelInfo {
pub cltv_expiry_delta: u16, pub cltv_expiry_delta: u16,
/// The minimum value, which must be relayed to the next hop via the channel /// The minimum value, which must be relayed to the next hop via the channel
pub htlc_minimum_msat: u64, pub htlc_minimum_msat: u64,
/// The maximum value which may be relayed to the next hop via the channel.
pub htlc_maximum_msat: Option<u64>,
/// Fees charged when the channel is used for routing /// Fees charged when the channel is used for routing
pub fees: RoutingFees, pub fees: RoutingFees,
/// Most recent update for the channel received from the network /// Most recent update for the channel received from the network
@ -236,6 +238,7 @@ impl_writeable!(DirectionalChannelInfo, 0, {
enabled, enabled,
cltv_expiry_delta, cltv_expiry_delta,
htlc_minimum_msat, htlc_minimum_msat,
htlc_maximum_msat,
fees, fees,
last_update_message last_update_message
}); });
@ -254,6 +257,8 @@ pub struct ChannelInfo {
pub node_two: PublicKey, pub node_two: PublicKey,
/// Details about the second direction of a channel /// Details about the second direction of a channel
pub two_to_one: Option<DirectionalChannelInfo>, pub two_to_one: Option<DirectionalChannelInfo>,
/// The channel capacity as seen on-chain, if chain lookup is available.
pub capacity_sats: Option<u64>,
/// An initial announcement of the channel /// An initial announcement of the channel
/// Mostly redundant with the data we store in fields explicitly. /// Mostly redundant with the data we store in fields explicitly.
/// Everything else is useful only for sending out for initial routing sync. /// Everything else is useful only for sending out for initial routing sync.
@ -275,6 +280,7 @@ impl_writeable!(ChannelInfo, 0, {
one_to_two, one_to_two,
node_two, node_two,
two_to_one, two_to_one,
capacity_sats,
announcement_message announcement_message
}); });
@ -552,7 +558,7 @@ impl NetworkGraph {
/// which is probably result of a reorg. In that case, we update channel info only if the /// which is probably result of a reorg. In that case, we update channel info only if the
/// utxo was checked, otherwise stick to the existing update, to prevent DoS risks. /// utxo was checked, otherwise stick to the existing update, to prevent DoS risks.
/// Announcement signatures are checked here only if Secp256k1 object is provided. /// Announcement signatures are checked here only if Secp256k1 object is provided.
fn update_channel_from_announcement(&mut self, msg: &msgs::ChannelAnnouncement, checked_utxo: bool, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> { fn update_channel_from_announcement(&mut self, msg: &msgs::ChannelAnnouncement, utxo_value: Option<u64>, secp_ctx: Option<&Secp256k1<secp256k1::VerifyOnly>>) -> Result<bool, LightningError> {
if let Some(sig_verifier) = secp_ctx { if let Some(sig_verifier) = secp_ctx {
let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]); let msg_hash = hash_to_message!(&Sha256dHash::hash(&msg.contents.encode()[..])[..]);
secp_verify_sig!(sig_verifier, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1); secp_verify_sig!(sig_verifier, &msg_hash, &msg.node_signature_1, &msg.contents.node_id_1);
@ -569,6 +575,7 @@ impl NetworkGraph {
one_to_two: None, one_to_two: None,
node_two: msg.contents.node_id_2.clone(), node_two: msg.contents.node_id_2.clone(),
two_to_one: None, two_to_one: None,
capacity_sats: utxo_value,
announcement_message: if should_relay { Some(msg.clone()) } else { None }, announcement_message: if should_relay { Some(msg.clone()) } else { None },
}; };
@ -577,7 +584,7 @@ impl NetworkGraph {
//TODO: because asking the blockchain if short_channel_id is valid is only optional //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 //in the blockchain API, we need to handle it smartly here, though it's unclear
//exactly how... //exactly how...
if checked_utxo { if utxo_value.is_some() {
// Either our UTXO provider is busted, there was a reorg, or the UTXO provider // Either our UTXO provider is busted, there was a reorg, or the UTXO provider
// only sometimes returns results. In any case remove the previous entry. Note // only sometimes returns results. In any case remove the previous entry. Note
// that the spec expects us to "blacklist" the node_ids involved, but we can't // that the spec expects us to "blacklist" the node_ids involved, but we can't
@ -659,6 +666,19 @@ impl NetworkGraph {
match self.channels.get_mut(&msg.contents.short_channel_id) { match self.channels.get_mut(&msg.contents.short_channel_id) {
None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}), None => return Err(LightningError{err: "Couldn't find channel for update".to_owned(), action: ErrorAction::IgnoreError}),
Some(channel) => { Some(channel) => {
if let OptionalField::Present(htlc_maximum_msat) = msg.contents.htlc_maximum_msat {
if htlc_maximum_msat > MAX_VALUE_MSAT {
return Err(LightningError{err: "htlc_maximum_msat is larger than maximum possible msats".to_owned(), action: ErrorAction::IgnoreError});
}
if let Some(capacity_sats) = channel.capacity_sats {
// It's possible channel capacity is available now, although it wasn't available at announcement (so the field is None).
// Don't query UTXO set here to reduce DoS risks.
if htlc_maximum_msat > capacity_sats * 1000 {
return Err(LightningError{err: "htlc_maximum_msat is larger than channel capacity".to_owned(), action: ErrorAction::IgnoreError});
}
}
}
macro_rules! maybe_update_channel_info { macro_rules! maybe_update_channel_info {
( $target: expr, $src_node: expr) => { ( $target: expr, $src_node: expr) => {
if let Some(existing_chan_info) = $target.as_ref() { if let Some(existing_chan_info) = $target.as_ref() {
@ -681,6 +701,7 @@ impl NetworkGraph {
last_update: msg.contents.timestamp, last_update: msg.contents.timestamp,
cltv_expiry_delta: msg.contents.cltv_expiry_delta, cltv_expiry_delta: msg.contents.cltv_expiry_delta,
htlc_minimum_msat: msg.contents.htlc_minimum_msat, htlc_minimum_msat: msg.contents.htlc_minimum_msat,
htlc_maximum_msat: if let OptionalField::Present(max_value) = msg.contents.htlc_maximum_msat { Some(max_value) } else { None },
fees: RoutingFees { fees: RoutingFees {
base_msat: msg.contents.fee_base_msat, base_msat: msg.contents.fee_base_msat,
proportional_millionths: msg.contents.fee_proportional_millionths, proportional_millionths: msg.contents.fee_proportional_millionths,
@ -774,8 +795,9 @@ mod tests {
use chain::chaininterface; use chain::chaininterface;
use ln::features::{ChannelFeatures, NodeFeatures}; use ln::features::{ChannelFeatures, NodeFeatures};
use routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; use routing::network_graph::{NetGraphMsgHandler, NetworkGraph};
use ln::msgs::{RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement, use ln::msgs::{OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, HTLCFailChannelUpdate}; UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate, HTLCFailChannelUpdate,
MAX_VALUE_MSAT};
use util::test_utils; use util::test_utils;
use util::logger::Logger; use util::logger::Logger;
use util::ser::{Readable, Writeable}; use util::ser::{Readable, Writeable};
@ -1110,7 +1132,11 @@ mod tests {
#[test] #[test]
fn handling_channel_update() { fn handling_channel_update() {
let (secp_ctx, net_graph_msg_handler) = create_net_graph_msg_handler(); let secp_ctx = Secp256k1::new();
let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
let chain_monitor = Arc::new(test_utils::TestChainWatcher::new());
let net_graph_msg_handler = NetGraphMsgHandler::new(chain_monitor.clone(), Arc::clone(&logger));
let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap(); let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
let node_2_privkey = &SecretKey::from_slice(&[41; 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 node_id_1 = PublicKey::from_secret_key(&secp_ctx, node_1_privkey);
@ -1121,8 +1147,16 @@ mod tests {
let zero_hash = Sha256dHash::hash(&[0; 32]); let zero_hash = Sha256dHash::hash(&[0; 32]);
let short_channel_id = 0; let short_channel_id = 0;
let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash(); let chain_hash = genesis_block(Network::Testnet).header.bitcoin_hash();
let amount_sats = 1000_000;
{ {
// Announce a channel we will update // Announce a channel we will update
let good_script = Builder::new().push_opcode(opcodes::all::OP_PUSHNUM_2)
.push_slice(&PublicKey::from_secret_key(&secp_ctx, node_1_btckey).serialize())
.push_slice(&PublicKey::from_secret_key(&secp_ctx, node_2_btckey).serialize())
.push_opcode(opcodes::all::OP_PUSHNUM_2)
.push_opcode(opcodes::all::OP_CHECKMULTISIG).into_script().to_v0_p2wsh();
*chain_monitor.utxo_ret.lock().unwrap() = Ok((good_script.clone(), amount_sats));
let unsigned_announcement = UnsignedChannelAnnouncement { let unsigned_announcement = UnsignedChannelAnnouncement {
features: ChannelFeatures::empty(), features: ChannelFeatures::empty(),
chain_hash, chain_hash,
@ -1156,6 +1190,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: 144, cltv_expiry_delta: 144,
htlc_minimum_msat: 1000000, htlc_minimum_msat: 1000000,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 10000, fee_base_msat: 10000,
fee_proportional_millionths: 20, fee_proportional_millionths: 20,
excess_data: Vec::new() excess_data: Vec::new()
@ -1194,6 +1229,7 @@ mod tests {
Ok(res) => assert!(!res), Ok(res) => assert!(!res),
_ => panic!() _ => panic!()
}; };
unsigned_channel_update.timestamp += 10;
unsigned_channel_update.short_channel_id += 1; unsigned_channel_update.short_channel_id += 1;
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]); let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
@ -1208,6 +1244,31 @@ mod tests {
}; };
unsigned_channel_update.short_channel_id = short_channel_id; unsigned_channel_update.short_channel_id = short_channel_id;
unsigned_channel_update.htlc_maximum_msat = OptionalField::Present(MAX_VALUE_MSAT + 1);
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
let valid_channel_update = ChannelUpdate {
signature: secp_ctx.sign(&msghash, node_1_privkey),
contents: unsigned_channel_update.clone()
};
match net_graph_msg_handler.handle_channel_update(&valid_channel_update) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.err, "htlc_maximum_msat is larger than maximum possible msats")
};
unsigned_channel_update.htlc_maximum_msat = OptionalField::Absent;
unsigned_channel_update.htlc_maximum_msat = OptionalField::Present(amount_sats * 1000 + 1);
let msghash = hash_to_message!(&Sha256dHash::hash(&unsigned_channel_update.encode()[..])[..]);
let valid_channel_update = ChannelUpdate {
signature: secp_ctx.sign(&msghash, node_1_privkey),
contents: unsigned_channel_update.clone()
};
match net_graph_msg_handler.handle_channel_update(&valid_channel_update) {
Ok(_) => panic!(),
Err(e) => assert_eq!(e.err, "htlc_maximum_msat is larger than channel capacity")
};
unsigned_channel_update.htlc_maximum_msat = OptionalField::Absent;
// Even though previous update was not relayed further, we still accepted it, // Even though previous update was not relayed further, we still accepted it,
// so we now won't accept update before the previous one. // so we now won't accept update before the previous one.
@ -1289,6 +1350,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: 144, cltv_expiry_delta: 144,
htlc_minimum_msat: 1000000, htlc_minimum_msat: 1000000,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 10000, fee_base_msat: 10000,
fee_proportional_millionths: 20, fee_proportional_millionths: 20,
excess_data: Vec::new() excess_data: Vec::new()
@ -1416,6 +1478,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: 144, cltv_expiry_delta: 144,
htlc_minimum_msat: 1000000, htlc_minimum_msat: 1000000,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 10000, fee_base_msat: 10000,
fee_proportional_millionths: 20, fee_proportional_millionths: 20,
excess_data: Vec::new() excess_data: Vec::new()
@ -1452,6 +1515,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: 144, cltv_expiry_delta: 144,
htlc_minimum_msat: 1000000, htlc_minimum_msat: 1000000,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 10000, fee_base_msat: 10000,
fee_proportional_millionths: 20, fee_proportional_millionths: 20,
excess_data: [1; 3].to_vec() excess_data: [1; 3].to_vec()

View file

@ -7,7 +7,7 @@ use bitcoin::secp256k1::key::PublicKey;
use ln::channelmanager; use ln::channelmanager;
use ln::features::{ChannelFeatures, NodeFeatures}; use ln::features::{ChannelFeatures, NodeFeatures};
use ln::msgs::{DecodeError,ErrorAction,LightningError}; use ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
use routing::network_graph::{NetworkGraph, RoutingFees}; use routing::network_graph::{NetworkGraph, RoutingFees};
use util::ser::{Writeable, Readable}; use util::ser::{Writeable, Readable};
use util::logger::Logger; use util::logger::Logger;
@ -144,7 +144,7 @@ struct DummyDirectionalChannelInfo {
} }
/// Gets a route from us (as specified in the provided NetworkGraph) to the given target node. /// Gets a route from us to the given target node.
/// ///
/// Extra routing hops between known nodes and the target will be used if they are included in /// Extra routing hops between known nodes and the target will be used if they are included in
/// last_hops. /// last_hops.
@ -168,7 +168,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError}); return Err(LightningError{err: "Cannot generate a route to ourselves".to_owned(), action: ErrorAction::IgnoreError});
} }
if final_value_msat > 21_000_000 * 1_0000_0000 * 1000 { if final_value_msat > MAX_VALUE_MSAT {
return Err(LightningError{err: "Cannot generate a route of more value than all existing satoshis".to_owned(), action: ErrorAction::IgnoreError}); return Err(LightningError{err: "Cannot generate a route of more value than all existing satoshis".to_owned(), action: ErrorAction::IgnoreError});
} }
@ -244,7 +244,8 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, targ
channel_features: $chan_features.clone(), channel_features: $chan_features.clone(),
fee_msat: 0, fee_msat: 0,
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
}) },
)
}); });
if $src_node_id != *our_node_id { if $src_node_id != *our_node_id {
// Ignore new_fee for channel-from-us as we assume all channels-from-us // Ignore new_fee for channel-from-us as we assume all channels-from-us
@ -404,7 +405,7 @@ mod tests {
use routing::router::{get_route, RouteHint, RoutingFees}; use routing::router::{get_route, RouteHint, RoutingFees};
use routing::network_graph::NetGraphMsgHandler; use routing::network_graph::NetGraphMsgHandler;
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures}; use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
use ln::msgs::{ErrorAction, LightningError, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler, use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate}; NodeAnnouncement, UnsignedNodeAnnouncement, ChannelUpdate, UnsignedChannelUpdate};
use ln::channelmanager; use ln::channelmanager;
use util::test_utils; use util::test_utils;
@ -603,6 +604,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -617,6 +619,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: u16::max_value(), cltv_expiry_delta: u16::max_value(),
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: u32::max_value(), fee_base_msat: u32::max_value(),
fee_proportional_millionths: u32::max_value(), fee_proportional_millionths: u32::max_value(),
excess_data: Vec::new() excess_data: Vec::new()
@ -628,6 +631,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -643,6 +647,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: u16::max_value(), cltv_expiry_delta: u16::max_value(),
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: u32::max_value(), fee_base_msat: u32::max_value(),
fee_proportional_millionths: u32::max_value(), fee_proportional_millionths: u32::max_value(),
excess_data: Vec::new() excess_data: Vec::new()
@ -654,6 +659,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -670,6 +676,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: (3 << 8) | 1, cltv_expiry_delta: (3 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -681,6 +688,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: (3 << 8) | 2, cltv_expiry_delta: (3 << 8) | 2,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 100, fee_base_msat: 100,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -695,6 +703,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: (4 << 8) | 1, cltv_expiry_delta: (4 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 1000000, fee_proportional_millionths: 1000000,
excess_data: Vec::new() excess_data: Vec::new()
@ -706,6 +715,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: (4 << 8) | 2, cltv_expiry_delta: (4 << 8) | 2,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -719,6 +729,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: (13 << 8) | 1, cltv_expiry_delta: (13 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 2000000, fee_proportional_millionths: 2000000,
excess_data: Vec::new() excess_data: Vec::new()
@ -730,6 +741,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: (13 << 8) | 2, cltv_expiry_delta: (13 << 8) | 2,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -744,6 +756,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: (6 << 8) | 1, cltv_expiry_delta: (6 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -755,6 +768,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: (6 << 8) | 2, cltv_expiry_delta: (6 << 8) | 2,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -768,6 +782,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: (11 << 8) | 1, cltv_expiry_delta: (11 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -779,6 +794,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: (11 << 8) | 2, cltv_expiry_delta: (11 << 8) | 2,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -795,6 +811,7 @@ mod tests {
flags: 0, flags: 0,
cltv_expiry_delta: (7 << 8) | 1, cltv_expiry_delta: (7 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 1000000, fee_proportional_millionths: 1000000,
excess_data: Vec::new() excess_data: Vec::new()
@ -806,6 +823,7 @@ mod tests {
flags: 1, flags: 1,
cltv_expiry_delta: (7 << 8) | 2, cltv_expiry_delta: (7 << 8) | 2,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -840,6 +858,7 @@ mod tests {
flags: 2, // to disable flags: 2, // to disable
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -851,6 +870,7 @@ mod tests {
flags: 2, // to disable flags: 2, // to disable
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: Vec::new() excess_data: Vec::new()
@ -898,6 +918,7 @@ mod tests {
flags: 0, // to enable flags: 0, // to enable
cltv_expiry_delta: (4 << 8) | 1, cltv_expiry_delta: (4 << 8) | 1,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 1000000, fee_proportional_millionths: 1000000,
excess_data: Vec::new() excess_data: Vec::new()
@ -909,6 +930,7 @@ mod tests {
flags: 0, // to enable flags: 0, // to enable
cltv_expiry_delta: u16::max_value(), cltv_expiry_delta: u16::max_value(),
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: u32::max_value(), fee_base_msat: u32::max_value(),
fee_proportional_millionths: u32::max_value(), fee_proportional_millionths: u32::max_value(),
excess_data: Vec::new() excess_data: Vec::new()

View file

@ -5,6 +5,7 @@ use chain::keysinterface;
use ln::channelmonitor; use ln::channelmonitor;
use ln::features::{ChannelFeatures, InitFeatures}; use ln::features::{ChannelFeatures, InitFeatures};
use ln::msgs; use ln::msgs;
use ln::msgs::OptionalField;
use ln::channelmonitor::HTLCUpdate; use ln::channelmonitor::HTLCUpdate;
use util::enforcing_trait_impls::EnforcingChannelKeys; use util::enforcing_trait_impls::EnforcingChannelKeys;
use util::events; use util::events;
@ -216,6 +217,7 @@ fn get_dummy_channel_update(short_chan_id: u64) -> msgs::ChannelUpdate {
flags: 0, flags: 0,
cltv_expiry_delta: 0, cltv_expiry_delta: 0,
htlc_minimum_msat: 0, htlc_minimum_msat: 0,
htlc_maximum_msat: OptionalField::Absent,
fee_base_msat: 0, fee_base_msat: 0,
fee_proportional_millionths: 0, fee_proportional_millionths: 0,
excess_data: vec![], excess_data: vec![],