-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(lightning): Add Lightning as transport #85
base: master
Are you sure you want to change the base?
Conversation
// Initialize the fields we will read from. An unused_assignment happens here. | ||
$(init_tlv_field_var!($field_name, $fieldty);)* | ||
$($field_name = $field;)* |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be replaced with
$(let $field_name = $field;)*
and remove #[allow(unused_assignments)]
.
let v = vec![1_u8, 2, 3, 6]; | ||
test_encode_decode_tlv!(1, v.len(), v, vec); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test other vector types (e.g. Vec<u16>
)
// Allow unreachable patterns which happen when we have some non-unique tlv types. | ||
#[allow(unreachable_patterns)] | ||
// Runs `test_encode_decode_tlv_stream` inside a closure so that we don't need to | ||
// return a `Result<(), DecodeError` from the test functions. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing >
writer.write_all(&self.to_vec())?; | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplify to
writer.write_all(&self.to_vec())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
fn test_msg<T: Debug + Readable + Writeable + PartialEq>(msg: T) { | ||
// Get a writer and write the message to it. | ||
let mut stream = TestVecWriter(Vec::new()); | ||
msg.write(&mut stream).ok().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop .ok()
.
msg.write(&mut stream).ok().unwrap(); | ||
// Create a reader out of the written buffer. | ||
let mut stream = Cursor::new(stream.0); | ||
let read_msg: T = Readable::read(&mut stream).ok().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop .ok()
.
*/ | ||
|
||
macro_rules! encode_tlv { | ||
($stream: expr, $type: expr, $field: expr, opt) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In std and other crates, they leave no space between the macro variable and its type: $stream:expr
.
/// Consume the remaining bytes. | ||
#[inline] | ||
pub fn eat_remaining(&mut self) -> Result<(), DecodeError> { | ||
copy(self, &mut sink()).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe tolerate this unwrap here and map to a decode error instead.
Sorry I haven't taken the time to review this yet. Will do as soon as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First pass. Not super familiar with Rust macros, so more like an Concept / Approach ACK than a thorough review.
use crate::cryptography::get_random_bytes; | ||
use bitcoin::hashes::Hash; | ||
use bitcoin::Txid; | ||
pub use lightning::util::test_utils::TestVecWriter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is imported here but not use here. I'm guessing this is because you're grouping it as part of the serialization tools and then re-importing it in messages.rs
and ser_macros.rs
.
Not against it but just wanted to verify that's the approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this is because you're grouping it as part of the serialization tools and then re-importing it in messages.rs and ser_macros.rs
Yup.
|
||
use crate::appointment::Locator; | ||
use crate::lightning::ser_macros::{impl_writeable_msg, set_msg_type}; | ||
use bitcoin::secp256k1::PublicKey; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: line break to separate crate
stuff from btc/ln stuff
/// The register message sent by the user to subscribe for the watching service. | ||
#[derive(Debug)] | ||
pub struct Register { | ||
pub pubkey: PublicKey, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be user_id
: UserId
pub struct SubscriptionDetails { | ||
pub appointment_max_size: u16, | ||
pub start_block: u32, | ||
pub amount_msat: u32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't be necessary. Payment methods can go in the TLV fields (as happens with the invoice), so we can assume that the service is altruistic if there is no amount_msat
. In case there is, then this value would be redundant.
#[derive(Debug)] | ||
pub struct AddUpdateAppointment { | ||
pub locator: Locator, | ||
// NOTE: LDK will prefix varying size fields (e.g. vectors and strings) with their length. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the length fields in BOLT13 are so it can be implemented in different langs properly. Don't thing this annotation is neeced.
/// The appointment data message sent by the tower after a get appointment message. | ||
#[derive(Debug)] | ||
pub struct AppointmentData { | ||
pub locator: Locator, | ||
pub encrypted_blob: Vec<u8>, | ||
} | ||
|
||
/// The tracker data message sent by the tower when the requested appointment has been acted upon. | ||
#[derive(Debug)] | ||
pub struct TrackerData { | ||
pub dispute_txid: Txid, | ||
pub penalty_txid: Txid, | ||
pub penalty_rawtx: Vec<u8>, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BOLT-wise this should be two different messages right? Have you put any thought on how this would look like?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BOLT-wise this should be two different messages right?
Indeed they are.
Have you put any thought on how this would look like?
Do you mean in terms of return types of methods and the typing stuff in Rust?
They are both TowerMessage
at the end.
writer.write_all(&self.to_vec())?; | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Second pass. This should be more thorough. Haven't checked test yet.
ser_macros.rs
is based on lightning::util::ser_macros.rs
but it significantly deviates from it. Is there any good reasoning behind it? Has lightning::util::ser_macros.rs
been updated since you wrote this?
As a general question, I saw you had some discussion regarding some of the serialization functionality being exposed in lightning
. Is that still on track? What parts of this would be exported and how would that affect this part of the codebase?
Ok(None) => {} | ||
// If we failed to read any bytes at all, we reached the end of our TLV | ||
// stream and have simply exhausted all entries. | ||
Err(e) if e == DecodeError::ShortRead && !track_read.have_read => break, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Err(e) if e == DecodeError::ShortRead && !track_read.have_read => break, | |
Err(ref e) if e == &DecodeError::ShortRead && !track_read.have_read => break, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this more ergonomic or it has performance impact?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It implies one less allocation AFAICT
impl<'a, T: Writeable> Writeable for LightningVecWriter<'a, T> { | ||
#[inline] | ||
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), Error> { | ||
for item in self.0.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for item in self.0.iter() { | |
for ref item in self.0.iter() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, iter
actually returns an iterator of references over the original data.
So no extra allocations on this one.
let locator = Self::from_slice(&buf).map_err(|_| DecodeError::InvalidValue)?; | ||
Ok(locator) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let locator = Self::from_slice(&buf).map_err(|_| DecodeError::InvalidValue)?; | |
Ok(locator) | |
Self::from_slice(&buf).map_err(|_| DecodeError::InvalidValue) |
} | ||
} | ||
|
||
#[cfg(test)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why this is test only? Also, why not derive this for the messages? Is it so you can test it generally within ser_macros.rs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't need to impl it at the beginning for usual functionality, and saw LDK
also doesn't implement it for their messages. But then I needed it for tests, that's why I made it test only.
ser_macros
wouldn't use it because it tests primitive types which already implement Eq. Generally, tests in ser_macros
don't test impl_writeable_msg
but test the macros used inside of it.
set_msg_type!(Register, 48848); | ||
set_msg_type!(SubscriptionDetails, 48850); | ||
set_msg_type!(AddUpdateAppointment, 48852); | ||
set_msg_type!(AppointmentAccepted, 48854); | ||
set_msg_type!(AppointmentRejected, 48856); | ||
set_msg_type!(GetAppointment, 48858); | ||
set_msg_type!(AppointmentData, 48860); | ||
set_msg_type!(TrackerData, 48862); | ||
set_msg_type!(AppointmentNotFound, 48864); | ||
// Let these messages get odd types since they are auxiliary messages. | ||
set_msg_type!(GetSubscriptionInfo, 48865); | ||
set_msg_type!(SubscriptionInfo, 48867); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reasoning behind the values taken for the message types? (apart from them being part of the custom message range and being even/odd depending on whether they are required or not)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not specifically, just random numbers as placeholders.
pub trait Type { | ||
/// The type identifying the message payload. | ||
const TYPE: u16; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mas to be a trait? How does LDK implement this for the standard messages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might work as something else, but I just followed LDK on this one. They have that trait Encode
which does the some thing. Encode is a little bit of a weird name, but I think they named it that way because they have another trait already called Type
.
I mainly wanted Not a big deal too. Exposing the utility macros would do fine. But after going on with BOLT13, some TLV datatypes there weren't covered by LDK's macro system (like vectors and strings) as they don't use it internally nor that it is used in other BOLTs, which led me to make some tweaks to my copy till it deviated that much. You can also find other parts trimmed out (like
Exposing them wouldn't help much now, because LDK's macros don't support strings and vector in TLVs, which we would like to have being supported. |
Right. I'd like to do one last pass on this before accepting it. |
Ok, I think this looks good. Not super used to reviewing macros but if something is not correct we will notice it when using the macros to implement the LN API. LGTM modulo making the pending changes. |
Yeah macros are tough but so cool and powerful. It's a pain that it ends up that you need to test macros with other macros 😆.
Will patch make them soon 👍. |
e34bb53
to
75d05e3
Compare
These next commits are implementing the server functionality/interface. |
b6889bd
to
ddfd755
Compare
Looks like this needs rebasing |
ddfd755
to
1a2840b
Compare
Add message (de)serilaization to teos-common This commit adds lightning messages and message (de)serialization using helper local modules (ser_utils, ser_macros) and LDK v0.0.108 instead of the attempted fork used before (https://github.com/meryacine/rust-lightning/tree/expose-tlv-macros) Most of the serialization macros and utils are copied from LDK's own serialization framework though. Also we might remove some of the locally implemented TLV stuff if they ever get exposed by LDK. But some of the macros have been edited and diverged from LDK's first implementation: for example, the `vec` arms for the macros were adjusted so that it works with `impl_writeable_msg` macro to send arrays as TLV (which is not the case for LDK ATM). Note that we could have had vectors(arrays) be inside `Option`s and put in a TLV, but this will waste extra two bytes for vector length annotation (we don't need the vector length annotated in the V(value) of the TLV since L(length) already embeds this information) We also now have another macro match arm `str_opt`, which indicates an optional string inside a TLV, this was created to avoid adding extra length field for strings just like `vec`.
This commit adds the lightning server and its port configuration option. The peer manager in charge of the lightning server doesn't know about routing and lightning channels (yet?). It can only responde to the user custom messages defined in `teos_common::lightning::messages`. A \`block_in_place\` is used in \`handle_tower_message\` to synchronously block on grpc calls.
These tests are analogous to tests in `http::tests_methods` and they test the tower message handler logic only (not the tower message handler when inside a peer manager)
These test utils include: - A test lightning client: this simulates a client's peer manager that is trying to connect to the tower and send/receive tower messages. For a real client, this should be part of a bigger application (say a lightning node) and the custom message handler adds the capabilities of speaking with the tower over Lightning. - Some utils to launch a Lightning server and connect one peer manager to another - A simple proof of concept test
This fixes a deadlock issue that appears with too many concurrent requtests. This solution was taken from devrandom's issue: lightningdevkit/rust-lightning #1367 Another solution that would probably work is that lightning net tokio wraps our sync calls with `tokio::task::spawn_blocking(|| sync_call()).await.unwarp()`, but not sure how would this affect performace of other users with no async code requirements. Note that peer_manager_tests now don't need to be anotated with `flavor = "multi_thread"`, that's because the runtime we block_on (inside lightning net tokio) is the artificial runtime created in `api::lightning::server` which is already "multi_thread"
1a2840b
to
5206d34
Compare
let tcp_stream = listener.accept().await.unwrap().0; | ||
if shutdown_signal.is_triggered() { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually waits until there is a new inbound connection then checks for if a shutdown signal was issued.
We need to listen for inbound and shutdown signal at the same time.
Any update regarding this ? |
1 similar comment
Any update regarding this ? |
Any update regarding this feature ? |
This PR introduces bolt13 message structs and their lightning (de)serialization, to be used by the tower (for the lightning server) and client apps.
LDK's (de)serialization traits are used to make the message structs sendable over wire (Readable and Writeable).
A small customized macro-based serialization framework similar to LDK's was added to help organize the code and reduce its size.
Will push the lightning server to this PR later on.
Addresses #31