[dependencies]
bech32 = "0.7"
+lightning = { version = "0.0.13", path = "../lightning" }
secp256k1 = { version = "0.20", features = ["recovery"] }
num-traits = "0.2.8"
bitcoin_hashes = "0.9.4"
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256;
+use lightning::routing::network_graph::RoutingFees;
+use lightning::routing::router::RouteHint;
use num_traits::{CheckedAdd, CheckedMul};
}
}
-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,
U: Into<u8> + Copy
{
return Err(ParseError::UnexpectedEndOfTaggedFields);
}
- let mut route_hops = Vec::<RouteHop>::new();
+ let mut route_hops = Vec::<RouteHint>::new();
let mut bytes = bytes.as_slice();
while !bytes.is_empty() {
let mut channel_id: [u8; 8] = Default::default();
channel_id.copy_from_slice(&hop_bytes[33..41]);
- let hop = RouteHop {
- pubkey: PublicKey::from_slice(&hop_bytes[0..33])?,
- short_channel_id: channel_id,
- fee_base_msat: parse_int_be(&hop_bytes[41..45], 256).expect("slice too big?"),
- fee_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?")
+ let hop = RouteHint {
+ src_node_id: PublicKey::from_slice(&hop_bytes[0..33])?,
+ short_channel_id: parse_int_be(&channel_id, 256).expect("short chan ID slice too big?"),
+ fees: RoutingFees {
+ base_msat: parse_int_be(&hop_bytes[41..45], 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);
#[test]
fn test_parse_route() {
- use RouteHop;
+ use lightning::routing::network_graph::RoutingFees;
+ use lightning::routing::router::RouteHint;
use ::Route;
use bech32::FromBase32;
+ use de::parse_int_be;
let input = from_bech32(
"q20q82gphp2nflc7jtzrcazrra7wwgzxqc8u7754cdlpfrmccae92qgzqvzq2ps8pqqqqqqpqqqqq9qqqvpeuqa\
fqxu92d8lr6fvg0r5gv0heeeqgcrqlnm6jhphu9y00rrhy4grqszsvpcgpy9qqqqqqgqqqqq7qqzq".as_bytes()
);
- let mut expected = Vec::<RouteHop>::new();
- expected.push(RouteHop {
- pubkey: PublicKey::from_slice(
+ let mut expected = Vec::<RouteHint>::new();
+ expected.push(RouteHint {
+ src_node_id: PublicKey::from_slice(
&[
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,
0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
][..]
).unwrap(),
- short_channel_id: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
- fee_base_msat: 1,
- fee_proportional_millionths: 20,
- cltv_expiry_delta: 3
+ short_channel_id: parse_int_be(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], 256).expect("short chan ID slice too big?"),
+ fees: RoutingFees {
+ base_msat: 1,
+ proportional_millionths: 20,
+ },
+ cltv_expiry_delta: 3,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None
});
- expected.push(RouteHop {
- pubkey: PublicKey::from_slice(
+ expected.push(RouteHint {
+ src_node_id: PublicKey::from_slice(
&[
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,
0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
][..]
).unwrap(),
- short_channel_id: [0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a],
- fee_base_msat: 2,
- fee_proportional_millionths: 30,
- cltv_expiry_delta: 4
+ short_channel_id: parse_int_be(&[0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a], 256).expect("short chan ID slice too big?"),
+ fees: RoutingFees {
+ base_msat: 2,
+ proportional_millionths: 30,
+ },
+ cltv_expiry_delta: 4,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None
});
assert_eq!(Route::from_base32(&input), Ok(Route(expected)));
extern crate bech32;
extern crate bitcoin_hashes;
+extern crate lightning;
extern crate num_traits;
extern crate secp256k1;
use bech32::u5;
use bitcoin_hashes::Hash;
use bitcoin_hashes::sha256;
+#[cfg(any(doc, test))]
+use lightning::routing::network_graph::RoutingFees;
+use lightning::routing::router::RouteHint;
use secp256k1::key::PublicKey;
use secp256k1::{Message, Secp256k1};
/// The encoded route has to be <1024 5bit characters long (<=639 bytes or <=12 hops)
///
#[derive(Eq, PartialEq, Debug, Clone)]
-pub struct Route(Vec<RouteHop>);
-
-/// 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,
-}
+pub struct Route(Vec<RouteHint>);
/// Tag constants as specified in BOLT11
#[allow(missing_docs)]
}
/// Adds a private route.
- pub fn route(mut self, route: Vec<RouteHop>) -> Self {
+ pub fn route(mut self, route: Vec<RouteHint>) -> Self {
match Route::new(route) {
Ok(r) => self.tagged_fields.push(TaggedField::Route(r)),
Err(e) => self.error = Some(e),
impl Route {
/// Create a new (partial) route from a list of hops
- pub fn new(hops: Vec<RouteHop>) -> Result<Route, CreationError> {
+ pub fn new(hops: Vec<RouteHint>) -> Result<Route, CreationError> {
if hops.len() <= 12 {
Ok(Route(hops))
} else {
}
/// Returrn the underlying vector of hops
- pub fn into_inner(self) -> Vec<RouteHop> {
+ pub fn into_inner(self) -> Vec<RouteHint> {
self.0
}
}
-impl Into<Vec<RouteHop>> for Route {
- fn into(self) -> Vec<RouteHop> {
+impl Into<Vec<RouteHint>> for Route {
+ fn into(self) -> Vec<RouteHint> {
self.into_inner()
}
}
impl Deref for Route {
- type Target = Vec<RouteHop>;
+ type Target = Vec<RouteHint>;
- fn deref(&self) -> &Vec<RouteHop> {
+ fn deref(&self) -> &Vec<RouteHint> {
&self.0
}
}
.build_raw();
assert_eq!(long_desc_res, Err(CreationError::DescriptionTooLong));
- let route_hop = RouteHop {
- pubkey: PublicKey::from_slice(
+ let route_hop = RouteHint {
+ src_node_id: PublicKey::from_slice(
&[
0x03, 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, 0x7e, 0x14, 0x8f, 0x78, 0xc7, 0x72, 0x55
][..]
).unwrap(),
- short_channel_id: [0; 8],
- fee_base_msat: 0,
- fee_proportional_millionths: 0,
+ short_channel_id: 0,
+ fees: RoutingFees {
+ base_msat: 0,
+ proportional_millionths: 0,
+ },
cltv_expiry_delta: 0,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
};
let too_long_route = vec![route_hop; 13];
let long_route_res = builder.clone()
let public_key = PublicKey::from_secret_key(&secp_ctx, &private_key);
let route_1 = vec![
- RouteHop {
- pubkey: public_key.clone(),
- short_channel_id: [123; 8],
- fee_base_msat: 2,
- fee_proportional_millionths: 1,
+ RouteHint {
+ src_node_id: public_key.clone(),
+ short_channel_id: de::parse_int_be(&[123; 8], 256).expect("short chan ID slice too big?"),
+ fees: RoutingFees {
+ base_msat: 2,
+ proportional_millionths: 1,
+ },
cltv_expiry_delta: 145,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
},
- RouteHop {
- pubkey: public_key.clone(),
- short_channel_id: [42; 8],
- fee_base_msat: 3,
- fee_proportional_millionths: 2,
+ RouteHint {
+ src_node_id: public_key.clone(),
+ short_channel_id: de::parse_int_be(&[42; 8], 256).expect("short chan ID slice too big?"),
+ fees: RoutingFees {
+ base_msat: 3,
+ proportional_millionths: 2,
+ },
cltv_expiry_delta: 146,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
}
];
let route_2 = vec![
- RouteHop {
- pubkey: public_key.clone(),
- short_channel_id: [0; 8],
- fee_base_msat: 4,
- fee_proportional_millionths: 3,
+ RouteHint {
+ src_node_id: public_key.clone(),
+ short_channel_id: 0,
+ fees: RoutingFees {
+ base_msat: 4,
+ proportional_millionths: 3,
+ },
cltv_expiry_delta: 147,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
},
- RouteHop {
- pubkey: public_key.clone(),
- short_channel_id: [1; 8],
- fee_base_msat: 5,
- fee_proportional_millionths: 4,
+ RouteHint {
+ src_node_id: public_key.clone(),
+ short_channel_id: de::parse_int_be(&[1; 8], 256).expect("short chan ID slice too big?"),
+ fees: RoutingFees {
+ base_msat: 5,
+ proportional_millionths: 4,
+ },
cltv_expiry_delta: 148,
+ htlc_minimum_msat: None,
+ htlc_maximum_msat: None,
}
];
let mut converter = BytesToBase32::new(writer);
for hop in self.iter() {
- converter.append(&hop.pubkey.serialize()[..])?;
- converter.append(&hop.short_channel_id[..])?;
+ converter.append(&hop.src_node_id.serialize()[..])?;
+ 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(
- encode_int_be_base256(hop.fee_base_msat),
+ encode_int_be_base256(hop.fees.base_msat),
4
).expect("sizeof(u32) == 4");
converter.append(&fee_base_msat)?;
let fee_proportional_millionths = try_stretch(
- encode_int_be_base256(hop.fee_proportional_millionths),
+ encode_int_be_base256(hop.fees.proportional_millionths),
4
).expect("sizeof(u32) == 4");
converter.append(&fee_proportional_millionths)?;
}
/// A channel descriptor which provides a last-hop route to get_route
-#[derive(Clone)]
+#[derive(Eq, PartialEq, Debug, Clone)]
pub struct RouteHint {
/// The node_id of the non-target end of the route
pub src_node_id: PublicKey,