Skip to content

Commit 11ca6b6

Browse files
committed
Normalizes node features so they use LDK NodeFeatures
Currently we were using a pretty hacky way of parsing LND features that only parsed the keysend feature (given is the only we care about atm). This PR properly loads the feature vector returned by LND's GetNodeInfo and parses it so a complete NodeFeatures object can be constructed. Also sets NodeInfo::features to NodeFeatures for consistency
1 parent f57ff45 commit 11ca6b6

File tree

3 files changed

+83
-42
lines changed

3 files changed

+83
-42
lines changed

sim-lib/src/cln.rs

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,49 +21,60 @@ pub struct ClnNode {
2121

2222
impl ClnNode {
2323
pub async fn new(connection: ClnConnection) -> Result<Self, LightningError> {
24-
let ca_pem = reader(&connection.ca_cert).await.map_err(|_| {
25-
LightningError::ConnectionError("Cannot loads CA certificate".to_string())
26-
})?;
27-
let client_pem = reader(&connection.client_cert).await.map_err(|_| {
28-
LightningError::ConnectionError("Cannot loads client certificate".to_string())
29-
})?;
30-
let client_key = reader(&connection.client_key)
31-
.await
32-
.map_err(|_| LightningError::ConnectionError("Cannot loads client key".to_string()))?;
33-
34-
let ca = Certificate::from_pem(ca_pem);
35-
let ident = Identity::from_pem(client_pem, client_key);
36-
3724
let tls = ClientTlsConfig::new()
3825
.domain_name("cln")
39-
.identity(ident)
40-
.ca_certificate(ca);
41-
42-
let channel = Channel::from_shared(connection.address.to_string())
43-
.map_err(|err| LightningError::ConnectionError(err.to_string()))?
44-
.tls_config(tls)
45-
.map_err(|_| {
46-
LightningError::ConnectionError("Cannot establish tls connection".to_string())
47-
})?
48-
.connect()
49-
.await
50-
.map_err(|_| {
51-
LightningError::ConnectionError("Cannot connect to gRPC server".to_string())
52-
})?;
53-
let mut client = NodeClient::new(channel);
54-
55-
let GetinfoResponse { id, alias, .. } = client
26+
.identity(Identity::from_pem(
27+
reader(&connection.client_cert).await.map_err(|_| {
28+
LightningError::ConnectionError("Cannot loads client certificate".to_string())
29+
})?,
30+
reader(&connection.client_key).await.map_err(|_| {
31+
LightningError::ConnectionError("Cannot loads client key".to_string())
32+
})?,
33+
))
34+
.ca_certificate(Certificate::from_pem(
35+
reader(&connection.ca_cert).await.map_err(|_| {
36+
LightningError::ConnectionError("Cannot loads CA certificate".to_string())
37+
})?,
38+
));
39+
40+
let mut client = NodeClient::new(
41+
Channel::from_shared(connection.address.to_string())
42+
.map_err(|err| LightningError::ConnectionError(err.to_string()))?
43+
.tls_config(tls)
44+
.map_err(|_| {
45+
LightningError::ConnectionError("Cannot establish tls connection".to_string())
46+
})?
47+
.connect()
48+
.await
49+
.map_err(|_| {
50+
LightningError::ConnectionError("Cannot connect to gRPC server".to_string())
51+
})?,
52+
);
53+
54+
let GetinfoResponse {
55+
id,
56+
alias,
57+
our_features,
58+
..
59+
} = client
5660
.getinfo(GetinfoRequest {})
5761
.await
5862
.map_err(|err| LightningError::GetInfoError(err.to_string()))?
5963
.into_inner();
6064

65+
//FIXME: our_features is returning None, but it should not :S
66+
let features = if let Some(features) = our_features {
67+
NodeFeatures::from_le_bytes(features.node)
68+
} else {
69+
NodeFeatures::empty()
70+
};
71+
6172
Ok(Self {
6273
client,
6374
info: NodeInfo {
6475
pubkey: PublicKey::from_slice(&id)
6576
.map_err(|err| LightningError::GetInfoError(err.to_string()))?,
66-
features: vec![],
77+
features,
6778
alias,
6879
},
6980
})

sim-lib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub enum LightningError {
101101
pub struct NodeInfo {
102102
pub pubkey: PublicKey,
103103
pub alias: String,
104-
pub features: Vec<u32>,
104+
pub features: NodeFeatures,
105105
}
106106

107107
/// LightningNode represents the functionality that is required to execute events on a lightning node.

sim-lib/src/lnd.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashSet;
12
use std::{collections::HashMap, str::FromStr};
23

34
use crate::{
@@ -14,7 +15,6 @@ use tonic_lnd::routerrpc::TrackPaymentRequest;
1415
use tonic_lnd::{routerrpc::SendPaymentRequest, Client};
1516
use triggered::Listener;
1617

17-
const KEYSEND_OPTIONAL: u32 = 55;
1818
const KEYSEND_KEY: u64 = 5482373484;
1919
const SEND_PAYMENT_TIMEOUT_SECS: i32 = 300;
2020

@@ -24,6 +24,41 @@ pub struct LndNode {
2424
info: NodeInfo,
2525
}
2626

27+
// FIXME: Do better with this error
28+
// TODO: We could even generalize this to parse any type of Features
29+
/// Parses the node features from the format returned by LND gRPC to LDK NodeFeatures
30+
fn parse_node_features(features: HashSet<&u32>) -> Result<NodeFeatures, String> {
31+
let mut features_vec = vec![0; 256];
32+
let mut custom_features = Vec::new();
33+
34+
// Set well known features first
35+
for f in features.into_iter() {
36+
if *f > 255 {
37+
custom_features.push(*f as usize);
38+
}
39+
40+
let byte_index = (f / 8) as usize;
41+
let bit_index = (f % 8) as usize;
42+
features_vec[byte_index] |= 1 << bit_index
43+
}
44+
45+
// Create NodeFeatures with the regular features and set the custom based on whether they
46+
// are required or not
47+
let mut nf = NodeFeatures::from_le_bytes(features_vec);
48+
for f in custom_features {
49+
let r = if f % 2 == 0 {
50+
nf.set_required_custom_bit(f)
51+
} else {
52+
nf.set_optional_custom_bit(f)
53+
};
54+
if r.is_err() {
55+
return Err(format!("Couldn't set feature bit {}", f));
56+
}
57+
}
58+
59+
Ok(nf)
60+
}
61+
2762
impl LndNode {
2863
pub async fn new(conn_data: LndConnection) -> Result<Self, LightningError> {
2964
let mut client = tonic_lnd::connect(conn_data.address, conn_data.cert, conn_data.macaroon)
@@ -47,7 +82,8 @@ impl LndNode {
4782
info: NodeInfo {
4883
pubkey: PublicKey::from_str(&identity_pubkey)
4984
.map_err(|err| LightningError::GetInfoError(err.to_string()))?,
50-
features: features.keys().copied().collect(),
85+
features: parse_node_features(features.keys().collect())
86+
.map_err(|e| LightningError::GetInfoError(e))?,
5187
alias,
5288
},
5389
})
@@ -171,15 +207,9 @@ impl LightningNode for LndNode {
171207
.map_err(|err| LightningError::GetNodeInfoError(err.to_string()))?
172208
.into_inner();
173209

174-
let mut nf = NodeFeatures::empty();
175-
176210
if let Some(node_info) = node_info.node {
177-
// FIXME: We only care about the keysend feature now, but we should parse the whole feature vector
178-
// into LDK's feature bitvector and properly construct NodeFeatures.
179-
if node_info.features.contains_key(&KEYSEND_OPTIONAL) {
180-
nf.set_keysend_optional()
181-
}
182-
Ok(nf)
211+
parse_node_features(node_info.features.keys().collect())
212+
.map_err(|e| LightningError::GetNodeInfoError(e))
183213
} else {
184214
Err(LightningError::GetNodeInfoError(
185215
"Node not found".to_string(),

0 commit comments

Comments
 (0)