mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-24 15:02:20 +01:00
Merge pull request #887 from valentinewallace/invoice-use-RL-routehint
invoice: swap RouteHop for RouteHint
This commit is contained in:
commit
f40e47c1ef
7 changed files with 137 additions and 111 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -47,7 +47,7 @@ jobs:
|
||||||
run: RUSTFLAGS="-C link-dead-code" cargo build --verbose --color always
|
run: RUSTFLAGS="-C link-dead-code" cargo build --verbose --color always
|
||||||
- name: Build on Rust ${{ matrix.toolchain }}
|
- name: Build on Rust ${{ matrix.toolchain }}
|
||||||
if: "! matrix.build-net-tokio"
|
if: "! matrix.build-net-tokio"
|
||||||
run: cargo build --verbose --color always -p lightning
|
run: cargo build --verbose --color always -p lightning && cargo build --verbose --color always -p lightning-invoice
|
||||||
- name: Build Block Sync Clients on Rust ${{ matrix.toolchain }} with features
|
- name: Build Block Sync Clients on Rust ${{ matrix.toolchain }} with features
|
||||||
if: "matrix.build-net-tokio && !matrix.coverage"
|
if: "matrix.build-net-tokio && !matrix.coverage"
|
||||||
run: |
|
run: |
|
||||||
|
@ -74,7 +74,7 @@ jobs:
|
||||||
run: RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always
|
run: RUSTFLAGS="-C link-dead-code" cargo test --verbose --color always
|
||||||
- name: Test on Rust ${{ matrix.toolchain }}
|
- name: Test on Rust ${{ matrix.toolchain }}
|
||||||
if: "! matrix.build-net-tokio"
|
if: "! matrix.build-net-tokio"
|
||||||
run: cargo test --verbose --color always -p lightning
|
run: cargo test --verbose --color always -p lightning && cargo test --verbose --color always -p lightning-invoice
|
||||||
- name: Test Block Sync Clients on Rust ${{ matrix.toolchain }} with features
|
- name: Test Block Sync Clients on Rust ${{ matrix.toolchain }} with features
|
||||||
if: "matrix.build-net-tokio && !matrix.coverage"
|
if: "matrix.build-net-tokio && !matrix.coverage"
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -15,7 +15,7 @@ use lightning::chain;
|
||||||
use lightning::ln::channelmanager::ChannelDetails;
|
use lightning::ln::channelmanager::ChannelDetails;
|
||||||
use lightning::ln::features::InitFeatures;
|
use lightning::ln::features::InitFeatures;
|
||||||
use lightning::ln::msgs;
|
use lightning::ln::msgs;
|
||||||
use lightning::routing::router::{get_route, RouteHint};
|
use lightning::routing::router::{get_route, RouteHintHop};
|
||||||
use lightning::util::logger::Logger;
|
use lightning::util::logger::Logger;
|
||||||
use lightning::util::ser::Readable;
|
use lightning::util::ser::Readable;
|
||||||
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
|
use lightning::routing::network_graph::{NetworkGraph, RoutingFees};
|
||||||
|
@ -224,7 +224,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
scid += 1;
|
scid += 1;
|
||||||
let rnid = node_pks.iter().skip(slice_to_be16(get_slice!(2))as usize % node_pks.len()).next().unwrap();
|
let rnid = node_pks.iter().skip(slice_to_be16(get_slice!(2))as usize % node_pks.len()).next().unwrap();
|
||||||
last_hops_vec.push(RouteHint {
|
last_hops_vec.push(RouteHintHop {
|
||||||
src_node_id: *rnid,
|
src_node_id: *rnid,
|
||||||
short_channel_id: scid,
|
short_channel_id: scid,
|
||||||
fees: RoutingFees {
|
fees: RoutingFees {
|
||||||
|
|
|
@ -10,6 +10,7 @@ readme = "README.md"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bech32 = "0.7"
|
bech32 = "0.7"
|
||||||
|
lightning = { version = "0.0.13", path = "../lightning" }
|
||||||
secp256k1 = { version = "0.20", features = ["recovery"] }
|
secp256k1 = { version = "0.20", features = ["recovery"] }
|
||||||
num-traits = "0.2.8"
|
num-traits = "0.2.8"
|
||||||
bitcoin_hashes = "0.9.4"
|
bitcoin_hashes = "0.9.4"
|
||||||
|
|
|
@ -10,6 +10,8 @@ use bech32::{u5, FromBase32};
|
||||||
|
|
||||||
use bitcoin_hashes::Hash;
|
use bitcoin_hashes::Hash;
|
||||||
use bitcoin_hashes::sha256;
|
use bitcoin_hashes::sha256;
|
||||||
|
use lightning::routing::network_graph::RoutingFees;
|
||||||
|
use lightning::routing::router::RouteHintHop;
|
||||||
|
|
||||||
use num_traits::{CheckedAdd, CheckedMul};
|
use num_traits::{CheckedAdd, CheckedMul};
|
||||||
|
|
||||||
|
@ -353,7 +355,7 @@ impl FromBase32 for Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
|
pub(crate) fn parse_int_be<T, U>(digits: &[U], base: T) -> Option<T>
|
||||||
where T: CheckedAdd + CheckedMul + From<u8> + Default,
|
where T: CheckedAdd + CheckedMul + From<u8> + Default,
|
||||||
U: Into<u8> + Copy
|
U: Into<u8> + Copy
|
||||||
{
|
{
|
||||||
|
@ -428,7 +430,7 @@ impl FromBase32 for TaggedField {
|
||||||
constants::TAG_FALLBACK =>
|
constants::TAG_FALLBACK =>
|
||||||
Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
|
Ok(TaggedField::Fallback(Fallback::from_base32(field_data)?)),
|
||||||
constants::TAG_ROUTE =>
|
constants::TAG_ROUTE =>
|
||||||
Ok(TaggedField::Route(Route::from_base32(field_data)?)),
|
Ok(TaggedField::Route(RouteHint::from_base32(field_data)?)),
|
||||||
constants::TAG_PAYMENT_SECRET =>
|
constants::TAG_PAYMENT_SECRET =>
|
||||||
Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
|
Ok(TaggedField::PaymentSecret(PaymentSecret::from_base32(field_data)?)),
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -565,17 +567,17 @@ impl FromBase32 for Fallback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromBase32 for Route {
|
impl FromBase32 for RouteHint {
|
||||||
type Err = ParseError;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_base32(field_data: &[u5]) -> Result<Route, ParseError> {
|
fn from_base32(field_data: &[u5]) -> Result<RouteHint, ParseError> {
|
||||||
let bytes = Vec::<u8>::from_base32(field_data)?;
|
let bytes = Vec::<u8>::from_base32(field_data)?;
|
||||||
|
|
||||||
if bytes.len() % 51 != 0 {
|
if bytes.len() % 51 != 0 {
|
||||||
return Err(ParseError::UnexpectedEndOfTaggedFields);
|
return Err(ParseError::UnexpectedEndOfTaggedFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut route_hops = Vec::<RouteHop>::new();
|
let mut route_hops = Vec::<RouteHintHop>::new();
|
||||||
|
|
||||||
let mut bytes = bytes.as_slice();
|
let mut bytes = bytes.as_slice();
|
||||||
while !bytes.is_empty() {
|
while !bytes.is_empty() {
|
||||||
|
@ -585,18 +587,22 @@ impl FromBase32 for Route {
|
||||||
let mut channel_id: [u8; 8] = Default::default();
|
let mut channel_id: [u8; 8] = Default::default();
|
||||||
channel_id.copy_from_slice(&hop_bytes[33..41]);
|
channel_id.copy_from_slice(&hop_bytes[33..41]);
|
||||||
|
|
||||||
let hop = RouteHop {
|
let hop = RouteHintHop {
|
||||||
pubkey: PublicKey::from_slice(&hop_bytes[0..33])?,
|
src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
|
||||||
short_channel_id: channel_id,
|
short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
|
||||||
fee_base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
|
base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
|
||||||
cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?")
|
proportional_millionths: parse_int_be(&hop_bytes[45..49], 256).expect("slice too big?"),
|
||||||
|
},
|
||||||
|
cltv_expiry_delta: parse_int_be(&hop_bytes[49..51], 256).expect("slice too big?"),
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
route_hops.push(hop);
|
route_hops.push(hop);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Route(route_hops))
|
Ok(RouteHint(route_hops))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -931,47 +937,57 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_route() {
|
fn test_parse_route() {
|
||||||
use RouteHop;
|
use lightning::routing::network_graph::RoutingFees;
|
||||||
use ::Route;
|
use lightning::routing::router::RouteHintHop;
|
||||||
|
use ::RouteHint;
|
||||||
use bech32::FromBase32;
|
use bech32::FromBase32;
|
||||||
|
use de::parse_int_be;
|
||||||
|
|
||||||
let input = from_bech32(
|
let input = from_bech32(
|
||||||
"q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
|
"q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
|
||||||
fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
|
fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut expected = Vec::<RouteHop>::new();
|
let mut expected = Vec::<RouteHintHop>::new();
|
||||||
expected.push(RouteHop {
|
expected.push(RouteHintHop {
|
||||||
pubkey: PublicKey::from_slice(
|
src_node_id: PublicKey::from_slice(
|
||||||
&[
|
&[
|
||||||
0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
|
0x02u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
|
||||||
0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
|
0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
|
||||||
0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
|
0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
|
||||||
][..]
|
][..]
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
short_channel_id: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
|
short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
|
||||||
fee_base_msat: 1,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 20,
|
base_msat: 1,
|
||||||
cltv_expiry_delta: 3
|
proportional_millionths: 20,
|
||||||
|
},
|
||||||
|
cltv_expiry_delta: 3,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None
|
||||||
});
|
});
|
||||||
expected.push(RouteHop {
|
expected.push(RouteHintHop {
|
||||||
pubkey: PublicKey::from_slice(
|
src_node_id: PublicKey::from_slice(
|
||||||
&[
|
&[
|
||||||
0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
|
0x03u8, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4, 0x3c,
|
||||||
0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
|
0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a, 0x95, 0xc3,
|
||||||
0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
|
0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
|
||||||
][..]
|
][..]
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
short_channel_id: [0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a],
|
short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
|
||||||
fee_base_msat: 2,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 30,
|
base_msat: 2,
|
||||||
cltv_expiry_delta: 4
|
proportional_millionths: 30,
|
||||||
|
},
|
||||||
|
cltv_expiry_delta: 4,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(Route::from_base32(&input), Ok(Route(expected)));
|
assert_eq!(RouteHint::from_base32(&input), Ok(RouteHint(expected)));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Route::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
|
RouteHint::from_base32(&[u5::try_from_u8(0).unwrap(); 40][..]),
|
||||||
Err(ParseError::UnexpectedEndOfTaggedFields)
|
Err(ParseError::UnexpectedEndOfTaggedFields)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,12 +17,16 @@
|
||||||
|
|
||||||
extern crate bech32;
|
extern crate bech32;
|
||||||
extern crate bitcoin_hashes;
|
extern crate bitcoin_hashes;
|
||||||
|
extern crate lightning;
|
||||||
extern crate num_traits;
|
extern crate num_traits;
|
||||||
extern crate secp256k1;
|
extern crate secp256k1;
|
||||||
|
|
||||||
use bech32::u5;
|
use bech32::u5;
|
||||||
use bitcoin_hashes::Hash;
|
use bitcoin_hashes::Hash;
|
||||||
use bitcoin_hashes::sha256;
|
use bitcoin_hashes::sha256;
|
||||||
|
#[cfg(any(doc, test))]
|
||||||
|
use lightning::routing::network_graph::RoutingFees;
|
||||||
|
use lightning::routing::router::RouteHintHop;
|
||||||
|
|
||||||
use secp256k1::key::PublicKey;
|
use secp256k1::key::PublicKey;
|
||||||
use secp256k1::{Message, Secp256k1};
|
use secp256k1::{Message, Secp256k1};
|
||||||
|
@ -323,7 +327,7 @@ pub enum TaggedField {
|
||||||
ExpiryTime(ExpiryTime),
|
ExpiryTime(ExpiryTime),
|
||||||
MinFinalCltvExpiry(MinFinalCltvExpiry),
|
MinFinalCltvExpiry(MinFinalCltvExpiry),
|
||||||
Fallback(Fallback),
|
Fallback(Fallback),
|
||||||
Route(Route),
|
Route(RouteHint),
|
||||||
PaymentSecret(PaymentSecret),
|
PaymentSecret(PaymentSecret),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,26 +387,7 @@ pub struct Signature(pub RecoverableSignature);
|
||||||
/// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
|
/// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
|
||||||
///
|
///
|
||||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||||
pub struct Route(Vec<RouteHop>);
|
pub struct RouteHint(Vec<RouteHintHop>);
|
||||||
|
|
||||||
/// Node on a private route
|
|
||||||
#[derive(Eq, PartialEq, Debug, Clone)]
|
|
||||||
pub struct RouteHop {
|
|
||||||
/// Node's public key
|
|
||||||
pub pubkey: PublicKey,
|
|
||||||
|
|
||||||
/// Which channel of this node we would be using
|
|
||||||
pub short_channel_id: [u8; 8],
|
|
||||||
|
|
||||||
/// Fee charged by this node per transaction
|
|
||||||
pub fee_base_msat: u32,
|
|
||||||
|
|
||||||
/// Fee charged by this node proportional to the amount routed
|
|
||||||
pub fee_proportional_millionths: u32,
|
|
||||||
|
|
||||||
/// Delta substracted by this node from incoming cltv_expiry value
|
|
||||||
pub cltv_expiry_delta: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tag constants as specified in BOLT11
|
/// Tag constants as specified in BOLT11
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -499,8 +484,8 @@ impl<D: tb::Bool, H: tb::Bool, T: tb::Bool> InvoiceBuilder<D, H, T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a private route.
|
/// Adds a private route.
|
||||||
pub fn route(mut self, route: Vec<RouteHop>) -> Self {
|
pub fn route(mut self, route: Vec<RouteHintHop>) -> Self {
|
||||||
match Route::new(route) {
|
match RouteHint::new(route) {
|
||||||
Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
|
Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
|
||||||
Err(e) => self.error = Some(e),
|
Err(e) => self.error = Some(e),
|
||||||
}
|
}
|
||||||
|
@ -832,11 +817,11 @@ impl RawInvoice {
|
||||||
}).collect::<Vec<&Fallback>>()
|
}).collect::<Vec<&Fallback>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn routes(&self) -> Vec<&Route> {
|
pub fn routes(&self) -> Vec<&RouteHint> {
|
||||||
self.known_tagged_fields().filter_map(|tf| match tf {
|
self.known_tagged_fields().filter_map(|tf| match tf {
|
||||||
&TaggedField::Route(ref r) => Some(r),
|
&TaggedField::Route(ref r) => Some(r),
|
||||||
_ => None,
|
_ => None,
|
||||||
}).collect::<Vec<&Route>>()
|
}).collect::<Vec<&RouteHint>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn amount_pico_btc(&self) -> Option<u64> {
|
pub fn amount_pico_btc(&self) -> Option<u64> {
|
||||||
|
@ -1035,7 +1020,7 @@ impl Invoice {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a list of all routes included in the invoice
|
/// Returns a list of all routes included in the invoice
|
||||||
pub fn routes(&self) -> Vec<&Route> {
|
pub fn routes(&self) -> Vec<&RouteHint> {
|
||||||
self.signed_invoice.routes()
|
self.signed_invoice.routes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,32 +1142,32 @@ impl ExpiryTime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Route {
|
impl RouteHint {
|
||||||
/// Create a new (partial) route from a list of hops
|
/// Create a new (partial) route from a list of hops
|
||||||
pub fn new(hops: Vec<RouteHop>) -> Result<Route, CreationError> {
|
pub fn new(hops: Vec<RouteHintHop>) -> Result<RouteHint, CreationError> {
|
||||||
if hops.len() <= 12 {
|
if hops.len() <= 12 {
|
||||||
Ok(Route(hops))
|
Ok(RouteHint(hops))
|
||||||
} else {
|
} else {
|
||||||
Err(CreationError::RouteTooLong)
|
Err(CreationError::RouteTooLong)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returrn the underlying vector of hops
|
/// Returrn the underlying vector of hops
|
||||||
pub fn into_inner(self) -> Vec<RouteHop> {
|
pub fn into_inner(self) -> Vec<RouteHintHop> {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Vec<RouteHop>> for Route {
|
impl Into<Vec<RouteHintHop>> for RouteHint {
|
||||||
fn into(self) -> Vec<RouteHop> {
|
fn into(self) -> Vec<RouteHintHop> {
|
||||||
self.into_inner()
|
self.into_inner()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for Route {
|
impl Deref for RouteHint {
|
||||||
type Target = Vec<RouteHop>;
|
type Target = Vec<RouteHintHop>;
|
||||||
|
|
||||||
fn deref(&self) -> &Vec<RouteHop> {
|
fn deref(&self) -> &Vec<RouteHintHop> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1458,18 +1443,22 @@ mod test {
|
||||||
.build_raw();
|
.build_raw();
|
||||||
assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
|
assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
|
||||||
|
|
||||||
let route_hop = RouteHop {
|
let route_hop = RouteHintHop {
|
||||||
pubkey: PublicKey::from_slice(
|
src_node_id: PublicKey::from_slice(
|
||||||
&[
|
&[
|
||||||
0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
|
0x03, 0x9e, 0x03, 0xa9, 0x01, 0xb8, 0x55, 0x34, 0xff, 0x1e, 0x92, 0xc4,
|
||||||
0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
|
0x3c, 0x74, 0x43, 0x1f, 0x7c, 0xe7, 0x20, 0x46, 0x06, 0x0f, 0xcf, 0x7a,
|
||||||
0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
|
0x95, 0xc3, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
|
||||||
][..]
|
][..]
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
short_channel_id: [0; 8],
|
short_channel_id: 0,
|
||||||
fee_base_msat: 0,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 0,
|
base_msat: 0,
|
||||||
|
proportional_millionths: 0,
|
||||||
|
},
|
||||||
cltv_expiry_delta: 0,
|
cltv_expiry_delta: 0,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None,
|
||||||
};
|
};
|
||||||
let too_long_route = vec![route_hop; 13];
|
let too_long_route = vec![route_hop; 13];
|
||||||
let long_route_res = builder.clone()
|
let long_route_res = builder.clone()
|
||||||
|
@ -1505,36 +1494,52 @@ mod test {
|
||||||
let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
|
let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
|
||||||
|
|
||||||
let route_1 = vec![
|
let route_1 = vec![
|
||||||
RouteHop {
|
RouteHintHop {
|
||||||
pubkey: public_key.clone(),
|
src_node_id: public_key.clone(),
|
||||||
short_channel_id: [123; 8],
|
short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
|
||||||
fee_base_msat: 2,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 1,
|
base_msat: 2,
|
||||||
|
proportional_millionths: 1,
|
||||||
|
},
|
||||||
cltv_expiry_delta: 145,
|
cltv_expiry_delta: 145,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None,
|
||||||
},
|
},
|
||||||
RouteHop {
|
RouteHintHop {
|
||||||
pubkey: public_key.clone(),
|
src_node_id: public_key.clone(),
|
||||||
short_channel_id: [42; 8],
|
short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
|
||||||
fee_base_msat: 3,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 2,
|
base_msat: 3,
|
||||||
|
proportional_millionths: 2,
|
||||||
|
},
|
||||||
cltv_expiry_delta: 146,
|
cltv_expiry_delta: 146,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
let route_2 = vec![
|
let route_2 = vec![
|
||||||
RouteHop {
|
RouteHintHop {
|
||||||
pubkey: public_key.clone(),
|
src_node_id: public_key.clone(),
|
||||||
short_channel_id: [0; 8],
|
short_channel_id: 0,
|
||||||
fee_base_msat: 4,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 3,
|
base_msat: 4,
|
||||||
|
proportional_millionths: 3,
|
||||||
|
},
|
||||||
cltv_expiry_delta: 147,
|
cltv_expiry_delta: 147,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None,
|
||||||
},
|
},
|
||||||
RouteHop {
|
RouteHintHop {
|
||||||
pubkey: public_key.clone(),
|
src_node_id: public_key.clone(),
|
||||||
short_channel_id: [1; 8],
|
short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
|
||||||
fee_base_msat: 5,
|
fees: RoutingFees {
|
||||||
fee_proportional_millionths: 4,
|
base_msat: 5,
|
||||||
|
proportional_millionths: 4,
|
||||||
|
},
|
||||||
cltv_expiry_delta: 148,
|
cltv_expiry_delta: 148,
|
||||||
|
htlc_minimum_msat: None,
|
||||||
|
htlc_maximum_msat: None,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1568,7 +1573,7 @@ mod test {
|
||||||
assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
|
assert_eq!(invoice.expiry_time(), Duration::from_secs(54321));
|
||||||
assert_eq!(invoice.min_final_cltv_expiry(), Some(&144));
|
assert_eq!(invoice.min_final_cltv_expiry(), Some(&144));
|
||||||
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
|
assert_eq!(invoice.fallbacks(), vec![&Fallback::PubKeyHash([0;20])]);
|
||||||
assert_eq!(invoice.routes(), vec![&Route(route_1), &Route(route_2)]);
|
assert_eq!(invoice.routes(), vec![&RouteHint(route_1), &RouteHint(route_2)]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
invoice.description(),
|
invoice.description(),
|
||||||
InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
|
InvoiceDescription::Hash(&Sha256(sha256::Hash::from_slice(&[3;32][..]).unwrap()))
|
||||||
|
|
|
@ -365,22 +365,26 @@ impl Base32Len for Fallback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToBase32 for Route {
|
impl ToBase32 for RouteHint {
|
||||||
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
|
fn write_base32<W: WriteBase32>(&self, writer: &mut W) -> Result<(), <W as WriteBase32>::Err> {
|
||||||
let mut converter = BytesToBase32::new(writer);
|
let mut converter = BytesToBase32::new(writer);
|
||||||
|
|
||||||
for hop in self.iter() {
|
for hop in self.iter() {
|
||||||
converter.append(&hop.pubkey.serialize()[..])?;
|
converter.append(&hop.src_node_id.serialize()[..])?;
|
||||||
converter.append(&hop.short_channel_id[..])?;
|
let short_channel_id = try_stretch(
|
||||||
|
encode_int_be_base256(hop.short_channel_id),
|
||||||
|
8
|
||||||
|
).expect("sizeof(u64) == 8");
|
||||||
|
converter.append(&short_channel_id)?;
|
||||||
|
|
||||||
let fee_base_msat = try_stretch(
|
let fee_base_msat = try_stretch(
|
||||||
encode_int_be_base256(hop.fee_base_msat),
|
encode_int_be_base256(hop.fees.base_msat),
|
||||||
4
|
4
|
||||||
).expect("sizeof(u32) == 4");
|
).expect("sizeof(u32) == 4");
|
||||||
converter.append(&fee_base_msat)?;
|
converter.append(&fee_base_msat)?;
|
||||||
|
|
||||||
let fee_proportional_millionths = try_stretch(
|
let fee_proportional_millionths = try_stretch(
|
||||||
encode_int_be_base256(hop.fee_proportional_millionths),
|
encode_int_be_base256(hop.fees.proportional_millionths),
|
||||||
4
|
4
|
||||||
).expect("sizeof(u32) == 4");
|
).expect("sizeof(u32) == 4");
|
||||||
converter.append(&fee_proportional_millionths)?;
|
converter.append(&fee_proportional_millionths)?;
|
||||||
|
@ -397,7 +401,7 @@ impl ToBase32 for Route {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Base32Len for Route {
|
impl Base32Len for RouteHint {
|
||||||
fn base32_len(&self) -> usize {
|
fn base32_len(&self) -> usize {
|
||||||
bytes_size_to_base32_size(self.0.len() * 51)
|
bytes_size_to_base32_size(self.0.len() * 51)
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,8 +117,8 @@ impl Readable for Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A channel descriptor which provides a last-hop route to get_route
|
/// A channel descriptor which provides a last-hop route to get_route
|
||||||
#[derive(Clone)]
|
#[derive(Eq, PartialEq, Debug, Clone)]
|
||||||
pub struct RouteHint {
|
pub struct RouteHintHop {
|
||||||
/// The node_id of the non-target end of the route
|
/// The node_id of the non-target end of the route
|
||||||
pub src_node_id: PublicKey,
|
pub src_node_id: PublicKey,
|
||||||
/// The short_channel_id of this channel
|
/// The short_channel_id of this channel
|
||||||
|
@ -176,7 +176,7 @@ struct DummyDirectionalChannelInfo {
|
||||||
/// These fee values are useful to choose hops as we traverse the graph "payee-to-payer".
|
/// These fee values are useful to choose hops as we traverse the graph "payee-to-payer".
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct PathBuildingHop<'a> {
|
struct PathBuildingHop<'a> {
|
||||||
// The RouteHint fields which will eventually be used if this hop is used in a final Route.
|
// The RouteHintHop fields which will eventually be used if this hop is used in a final Route.
|
||||||
// Note that node_features is calculated separately after our initial graph walk.
|
// Note that node_features is calculated separately after our initial graph walk.
|
||||||
pubkey: PublicKey,
|
pubkey: PublicKey,
|
||||||
short_channel_id: u64,
|
short_channel_id: u64,
|
||||||
|
@ -353,7 +353,7 @@ fn compute_fees(amount_msat: u64, channel_fees: RoutingFees) -> Option<u64> {
|
||||||
/// equal), however the enabled/disabled bit on such channels as well as the
|
/// equal), however the enabled/disabled bit on such channels as well as the
|
||||||
/// htlc_minimum_msat/htlc_maximum_msat *are* checked as they may change based on the receiving node.
|
/// htlc_minimum_msat/htlc_maximum_msat *are* checked as they may change based on the receiving node.
|
||||||
pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, payee: &PublicKey, payee_features: Option<InvoiceFeatures>, first_hops: Option<&[&ChannelDetails]>,
|
pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, payee: &PublicKey, payee_features: Option<InvoiceFeatures>, first_hops: Option<&[&ChannelDetails]>,
|
||||||
last_hops: &[&RouteHint], final_value_msat: u64, final_cltv: u32, logger: L) -> Result<Route, LightningError> where L::Target: Logger {
|
last_hops: &[&RouteHintHop], final_value_msat: u64, final_cltv: u32, logger: L) -> Result<Route, LightningError> where L::Target: Logger {
|
||||||
// TODO: Obviously *only* using total fee cost sucks. We should consider weighting by
|
// TODO: Obviously *only* using total fee cost sucks. We should consider weighting by
|
||||||
// uptime/success in using a node in the past.
|
// uptime/success in using a node in the past.
|
||||||
if *payee == *our_node_id {
|
if *payee == *our_node_id {
|
||||||
|
@ -1163,7 +1163,7 @@ pub fn get_route<L: Deref>(our_node_id: &PublicKey, network: &NetworkGraph, paye
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use routing::router::{get_route, RouteHint, RoutingFees};
|
use routing::router::{get_route, RouteHintHop, RoutingFees};
|
||||||
use routing::network_graph::{NetworkGraph, NetGraphMsgHandler};
|
use routing::network_graph::{NetworkGraph, NetGraphMsgHandler};
|
||||||
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
|
use ln::features::{ChannelFeatures, InitFeatures, InvoiceFeatures, NodeFeatures};
|
||||||
use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
|
use ln::msgs::{ErrorAction, LightningError, OptionalField, UnsignedChannelAnnouncement, ChannelAnnouncement, RoutingMessageHandler,
|
||||||
|
@ -2084,19 +2084,19 @@ mod tests {
|
||||||
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13));
|
assert_eq!(route.paths[0][1].channel_features.le_flags(), &id_to_feature_flags(13));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn last_hops(nodes: &Vec<PublicKey>) -> Vec<RouteHint> {
|
fn last_hops(nodes: &Vec<PublicKey>) -> Vec<RouteHintHop> {
|
||||||
let zero_fees = RoutingFees {
|
let zero_fees = RoutingFees {
|
||||||
base_msat: 0,
|
base_msat: 0,
|
||||||
proportional_millionths: 0,
|
proportional_millionths: 0,
|
||||||
};
|
};
|
||||||
vec!(RouteHint {
|
vec!(RouteHintHop {
|
||||||
src_node_id: nodes[3].clone(),
|
src_node_id: nodes[3].clone(),
|
||||||
short_channel_id: 8,
|
short_channel_id: 8,
|
||||||
fees: zero_fees,
|
fees: zero_fees,
|
||||||
cltv_expiry_delta: (8 << 8) | 1,
|
cltv_expiry_delta: (8 << 8) | 1,
|
||||||
htlc_minimum_msat: None,
|
htlc_minimum_msat: None,
|
||||||
htlc_maximum_msat: None,
|
htlc_maximum_msat: None,
|
||||||
}, RouteHint {
|
}, RouteHintHop {
|
||||||
src_node_id: nodes[4].clone(),
|
src_node_id: nodes[4].clone(),
|
||||||
short_channel_id: 9,
|
short_channel_id: 9,
|
||||||
fees: RoutingFees {
|
fees: RoutingFees {
|
||||||
|
@ -2106,7 +2106,7 @@ mod tests {
|
||||||
cltv_expiry_delta: (9 << 8) | 1,
|
cltv_expiry_delta: (9 << 8) | 1,
|
||||||
htlc_minimum_msat: None,
|
htlc_minimum_msat: None,
|
||||||
htlc_maximum_msat: None,
|
htlc_maximum_msat: None,
|
||||||
}, RouteHint {
|
}, RouteHintHop {
|
||||||
src_node_id: nodes[5].clone(),
|
src_node_id: nodes[5].clone(),
|
||||||
short_channel_id: 10,
|
short_channel_id: 10,
|
||||||
fees: zero_fees,
|
fees: zero_fees,
|
||||||
|
@ -2124,7 +2124,7 @@ mod tests {
|
||||||
// Simple test across 2, 3, 5, and 4 via a last_hop channel
|
// Simple test across 2, 3, 5, and 4 via a last_hop channel
|
||||||
|
|
||||||
// First check that lst hop can't have its source as the payee.
|
// First check that lst hop can't have its source as the payee.
|
||||||
let invalid_last_hop = RouteHint {
|
let invalid_last_hop = RouteHintHop {
|
||||||
src_node_id: nodes[6],
|
src_node_id: nodes[6],
|
||||||
short_channel_id: 8,
|
short_channel_id: 8,
|
||||||
fees: RoutingFees {
|
fees: RoutingFees {
|
||||||
|
@ -2309,7 +2309,7 @@ mod tests {
|
||||||
let target_node_id = PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&hex::decode(format!("{:02}", 43).repeat(32)).unwrap()[..]).unwrap());
|
let target_node_id = PublicKey::from_secret_key(&Secp256k1::new(), &SecretKey::from_slice(&hex::decode(format!("{:02}", 43).repeat(32)).unwrap()[..]).unwrap());
|
||||||
|
|
||||||
// If we specify a channel to a middle hop, that overrides our local channel view and that gets used
|
// If we specify a channel to a middle hop, that overrides our local channel view and that gets used
|
||||||
let last_hops = vec![RouteHint {
|
let last_hops = vec![RouteHintHop {
|
||||||
src_node_id: middle_node_id,
|
src_node_id: middle_node_id,
|
||||||
short_channel_id: 8,
|
short_channel_id: 8,
|
||||||
fees: RoutingFees {
|
fees: RoutingFees {
|
||||||
|
|
Loading…
Add table
Reference in a new issue