Skip to content
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

Initial event handling implementation #10

Merged
merged 4 commits into from
Jan 17, 2023

Conversation

tnull
Copy link
Collaborator

@tnull tnull commented Sep 1, 2022

Based on #8. Dependent on #9.

In this PR, we provide the event handling for LDK events and implement the structures and logic for emitting LdkLiteEvents.

@tnull tnull force-pushed the 2022-09-initial-event-handling branch 3 times, most recently from fb52b66 to 562ae2a Compare September 8, 2022 10:38
@tnull tnull force-pushed the 2022-09-initial-event-handling branch 4 times, most recently from 4815eba to 3c4db1b Compare September 14, 2022 16:52
@tnull tnull force-pushed the 2022-09-initial-event-handling branch from 3c4db1b to 77940d9 Compare September 19, 2022 16:51
@tnull
Copy link
Collaborator Author

tnull commented Sep 19, 2022

Rebased on main.

Copy link

@wpaulino wpaulino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't review the event handling itself, will need to look at some of the dependent PRs for more context. The PR description should be updated to note what this currently depends on.

src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
@tnull
Copy link
Collaborator Author

tnull commented Sep 27, 2022

The PR description should be updated to note what this currently depends on.

Hum, this doesn't really depend on anything now that #8 has been merged. However, essentially everything after (as in "issue number is higher") is dependent on this PR.

@wpaulino
Copy link

Hum, this doesn't really depend on anything now that #8 has been merged. However, essentially everything after (as in "issue number is higher") is dependent on this PR.

Some of the components used within event handling don't exist in main yet, e.g., LdkLiteChainAccess.

@tnull
Copy link
Collaborator Author

tnull commented Sep 27, 2022

Some of the components used within event handling don't exist in main yet, e.g., LdkLiteChainAccess.

Ah, right, missed that one. Will update the description.

@tnull tnull force-pushed the 2022-09-initial-event-handling branch from 8312428 to fdef833 Compare September 30, 2022 08:25
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated

impl Writeable for PaymentSuccessfulEvent {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), lightning::io::Error> {
self.payment_hash.write(writer)?;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need some kind of upgradability story for all the events. IMO just do TLV-encoding for everything from day one, its simpler (but a bit slower...I really wish TLV encoding had a fixed-length type/length).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, IIUC, we'd follow the same approach as currently (as we can't yet make use of the macros), but just also write out the length prefix for each event?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, we could also expose the macro upstream? But, I mean, exact compat semantics is kinda up to you. #10 (comment)

src/event.rs Outdated Show resolved Hide resolved
locked_queue.0.front().unwrap().clone()
}

pub(crate) fn event_handled(&self) -> Result<(), Error> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if events are handled async? Dont we need some kind of EventId here?

Copy link
Collaborator Author

@tnull tnull Oct 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh, I though we had discussed that through this (blocking) interface we want to force users to handle events sequentially, i.e., allow them to only make progress once they handled an event? IIUC, async event handling would mean we'd keep them in a persisted hash map and allow to query for a list of all unhandled events?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, fair enough. Shouldn't that mean that next_event shouldn't return a new event unless all pending events have been handled?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It would currently always return the front of the queue until it is popped in event_handled.

Copy link

@TheBlueMatt TheBlueMatt Nov 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, hmm, that's a confusing API IMO. I guess it depends on how this is exposed publicly, but needs good docs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, def. need to document that behavior nicely. However, if the app crashes before the event is handled and we don't return the event again, we'd need yet another method to allow the user to look it up. I therefore think just returning the same event until handled is probably the most straightforward approach towards ensuring the user has to handle all events and can recover/resume after a failure.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are a nice way to document. 🙂 Could you add some either here or possibly as doc tests, if that makes sense.

Copy link
Collaborator Author

@tnull tnull Nov 7, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now added a unit test here for the EventQueue with 1566dcb. (Also rebased #11 on top of this, so you can have a look at https://github.com/lightningdevkit/ldk-lite/actions/runs/3413412794/jobs/5680088292 to check the CI run)

As the documentation of the top-level API will be in lib.rs I took a TODO to possibly add a doctest/example there.

src/event.rs Outdated
let forwarding_channel_manager = self.channel_manager.clone();
let min = time_forwardable.as_millis() as u64;

// TODO: any way we still can use tokio here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please. Tokio has a nice simple timer interface to use here. I guess its not clear to me how these types are used so its less clear to me if its doable, but I assume we'll have a tokio runtime we can pass into the handler struct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, will address this in an upcoming "make everything async" PR in which I'll also switch the calls to BDK over to async. Should be noted thought that this is not entirely trivial since the tokio runtime won't be available when we initialize the event handler, so we'll have to pass it around after the fact, i.e., when start() gets called and the runtime is setup.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here's the upcoming PR: #26

@tnull tnull force-pushed the 2022-09-initial-event-handling branch 2 times, most recently from a9fd684 to a4922da Compare October 20, 2022 06:54
@tnull
Copy link
Collaborator Author

tnull commented Oct 20, 2022

So, turns out that UniFFI doesn't support tuple structs in enum variants in an easy manner. Rather than writing a lot of boilerplate code for bindings, I now reverted fdef833 with 0a0d8b3.

Let me know if history starts getting confusing here, happy to squash then.

This was referenced Oct 20, 2022
//6u8 => {
// TODO OnChainPaymentReceived
//}
_ => Err(lightning::ln::msgs::DecodeError::InvalidValue),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to do the thing LDK/lightning does and have the ability to ignore some types for forwards compatibility?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I'm currently reconsidering whether I want to re-use upstream serialization at all. I currently considering introducing a new abstraction in which every field/value would need to have explicit TYPE/SERIALIZATION_TYPE/VERSION: u8 given and to also enforce that converting from one VERSION to another has to be explicitly implemented. I hope this way I can escape the mess that is rust-lightning's serialization logic, where we have to remember any caveats for backwards/forwards compat at all times when introducing changes. Any thoughts on that?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dunno, all options are terrible. Some old rust-lightning structs still have a version:min_version two-byte prefix which goes "the version I am is X, the minimum version that can read this is Y", but that still requires the same level of awareness from devs. You could go the "no forwards-compatibility at all" route, which of course simplifies everything a ton, but then users can't run two copies of LDK on different devices and upgrade them out of lock-step, but maybe that's okay.

src/event.rs Show resolved Hide resolved
locked_queue.0.front().unwrap().clone()
}

pub(crate) fn event_handled(&self) -> Result<(), Error> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, fair enough. Shouldn't that mean that next_event shouldn't return a new event unless all pending events have been handled?

src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
@tnull tnull force-pushed the 2022-09-initial-event-handling branch 3 times, most recently from 3c705d4 to a834254 Compare November 1, 2022 14:21
@tnull tnull force-pushed the 2022-09-initial-event-handling branch from cc83be6 to 799df1e Compare November 7, 2022 11:51
@tnull
Copy link
Collaborator Author

tnull commented Nov 7, 2022

If you want to land this, feel free to squash (with some TODO: figure out serialization more concretely - #30 notes) and I'll give another once-over.

Alright, would be great to keep moving forward to escape rebase-hell. 😅

Squashed commits now:

$ git diff-tree -U2 cc83be6 799df1e
diff --git a/src/event.rs b/src/event.rs
index c8b807b..468af37 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -73,4 +73,5 @@ pub enum LdkLiteEvent {
 }

+// TODO: Figure out serialization more concretely - see issue #30
 impl Readable for LdkLiteEvent {
        fn read<R: lightning::io::Read>(

@tnull tnull force-pushed the 2022-09-initial-event-handling branch from aff85ae to 1566dcb Compare November 7, 2022 19:21
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
//}
}

// TODO: Figure out serialization more concretely - see issue #30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also consider using impl_writeable_tlv_based_enum for enums.

src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated
.notifier
.wait_while(self.queue.lock().unwrap(), |queue| queue.0.is_empty())
.unwrap();
locked_queue.0.front().unwrap().clone()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we store / return an Arc to avoid cloning?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with a45eb9e.

Copy link
Collaborator Author

@tnull tnull Dec 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now reverted this with 3b6ded0 for two reasons: a) I find using a plain Event preferable in the interface but more importantly b) turns out that UniFFI (see #25) doesn't support Arc'ed enum return values for the time being. However, it will itself wrap the Event in an Arc for the exposed interface, but sadly can't re-use the Arc in the case of an enum.

src/event.rs Outdated
}
}

impl<K: KVStorePersister> ReadableArgs<Arc<K>> for LdkLiteEventQueue<K> {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want a Writeable implementation, too.

Copy link
Collaborator Author

@tnull tnull Nov 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need Writeable for EventQueue? Readable/Writeable are already implemented for EventQueueSerWrapper and the ReadableArgs implementation of EventQueue is merely a short form of reading the queue separately an passing it to something like EventQueue::new, which allows us to never expose EventQueueSerWrapper.

src/event.rs Show resolved Hide resolved
src/event.rs Outdated
Comment on lines 185 to 169
pub(crate) fn next_event(&self) -> Arc<LdkLiteEvent> {
let locked_queue = self
.notifier
.wait_while(self.queue.lock().unwrap(), |queue| queue.is_empty())
.unwrap();
Arc::clone(&locked_queue.front().unwrap())
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious how this would work with shutdown. If a user has a loop calling next_event, do they need to wait for an event comes through to exit the loop? Would using wait_timeout_while be preferable?

Copy link
Collaborator Author

@tnull tnull Nov 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yeah, that is a good question regarding how we imagine the overall API to be used (i.e., if we expect the user side to entirely run single-threaded or if there would be a background process blocking on this). I'm not the biggest fan of timeouts, but maybe we should expose multiple versions of this via the main API? One blocking, one blocking for a given timeout, and one returning a Future?

@tnull tnull force-pushed the 2022-09-initial-event-handling branch from c2ae588 to 9d6324b Compare November 30, 2022 12:34
@tnull
Copy link
Collaborator Author

tnull commented Nov 30, 2022

Alright, after I had to make #11 async-based already, this now also includes the necessary changes to event.rs. Had to squash fixups in the process.

Copy link

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like the commits are a little mixed up. Might as well squash them.

src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
@tnull tnull force-pushed the 2022-09-initial-event-handling branch from 1ff2855 to 417eb55 Compare December 12, 2022 13:35
@tnull
Copy link
Collaborator Author

tnull commented Dec 12, 2022

Looks like the commits are a little mixed up. Might as well squash them.

Squashed and addressed latest feedback.

Copy link

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty much LGTM if you want to squash

src/event.rs Outdated
Comment on lines 463 to 479
if locked_runtime.as_ref().is_none() {
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, likely. Tagging @TheBlueMatt to confirm.

src/event.rs Outdated Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
src/event.rs Show resolved Hide resolved
@tnull tnull force-pushed the 2022-09-initial-event-handling branch from 418dc62 to e0bfb64 Compare January 6, 2023 09:12
@tnull
Copy link
Collaborator Author

tnull commented Jan 6, 2023

Pretty much LGTM if you want to squash

Squashed fixups without further changes.

@tnull tnull force-pushed the 2022-09-initial-event-handling branch from b2f3872 to e775a6b Compare January 9, 2023 09:50
src/event.rs Show resolved Hide resolved
src/event.rs Outdated Show resolved Hide resolved
@tnull tnull requested a review from jkczyz January 16, 2023 14:01
@tnull tnull merged commit 053a5ed into lightningdevkit:main Jan 17, 2023
@tnull tnull mentioned this pull request May 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants