mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 07:17:40 +01:00
Merge pull request #651 from naumenkogs/2020-06-routing-data-improvements
Routing improvements
This commit is contained in:
commit
779ff6721b
14 changed files with 194 additions and 45 deletions
|
@ -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::
|
||||||
|
|
|
@ -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 ""
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}[..])
|
}[..])
|
||||||
|
|
|
@ -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![],
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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![],
|
||||||
|
|
Loading…
Add table
Reference in a new issue