Skip to content

WIP: Initial LdkLite architecture and project layout #1

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

Closed
wants to merge 20 commits into from

Conversation

tnull
Copy link
Collaborator

@tnull tnull commented Aug 26, 2022

This PR is an initial WIP implementation of the simplified LDK Lite API and also provides the general project layout. I referenced / adapted code from the sample node where possible and also started to add documentation.

I'm really looking forward to any feedback, but in particular comments on the taken design choices would be highly appreciated, since they probably should be adapted ASAP, if suboptimal.

As in the current state it's hard to draw clear lines, I intend to add basic initial implementations of the following in this PR:

  • Basic API methods.
  • Handling of LDK events
  • Generation LDK Lite events.

However, as this will make it probably pretty unwieldy any further features/modification will be done in follow-ups (also see TODOs in the code and Issues).

@tnull tnull self-assigned this Aug 26, 2022
@tnull tnull marked this pull request as draft August 26, 2022 13:26
@tnull tnull force-pushed the 2022-08-start-of-ldk-lite branch from 6a54722 to 31a43f6 Compare August 26, 2022 13:54
Copy link

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

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

Just did a quick scroll through.

let _outbound_payments = Arc::new(Mutex::new(HashMap::new()));

// Step 14: Handle LDK Events
let event_queue = mpsc::sync_channel(CHANNEL_BUF_SIZE);

Choose a reason for hiding this comment

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

Sadly we can't queue events. They must be fully handled in-line (and any resulting data persisted to disk) before the handle-event method returns. This ensures we never lose events, as we do not remove them from the channel manager until that point.

Copy link
Collaborator Author

@tnull tnull Aug 27, 2022

Choose a reason for hiding this comment

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

Ah, wait, but that requirement is for LDK events, right? The event_queue here is meant for LdkLiteEvents.

The idea would be: LDK events get handled by event::LdkLiteEventHandler, which processes them and emits more user-friendly LdkLiteEvents (notifications really, possibly wrapping the original events) to said event queue. Yes, these events should probably not get lost either, but it's maybe not as critical if we already handled technicalities for, e.g, FundingGenerationReady or SpendableOutputs in the event handler?

Choose a reason for hiding this comment

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

Ah okay, I see your point. I assume eg payment history is persisted directly in LdkLite before we hand events to users (this would imply LdkLite is required to track history)? Are there no cases where users could lose data from missing an event?

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, I payment history will be persisted directly by the event handler I assume.

But, I'm honestly not sure how we could ensure that no data is lost ever when the user doesn't process the events given. If event queue gets dropped, the user will probably not notified again about the queued events. However, I'm not sure if "we hand you individual events and it's on you if you don't act on them before dropping" is that different from "we hand you a list/queue of events, and it's on you if you don't act on all of them before dropping" at that point?

Copy link

@TheBlueMatt TheBlueMatt Aug 29, 2022

Choose a reason for hiding this comment

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

Well, the first is implementable, the second is impossible if the program crashes (eg is running on mobile and the user goes back to the home screen).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hm, but I'm not sure exposing this kind of interface is in line with the usability we're trying to achieve with LdkLite. I agree we should handle all security-critical things before we even notify the user, but forcing him to handle events inline is sometimes just not practical? Also, it would likely force us to block the event handler and thereby the entire BP thread? Also not sure the given example matches exactly, because IIUC at least under iOS the app would be suspended and its state saved until it is brought to the foreground again? (Which opens a whole other topic on whether we would cleanly recover and continue operation from different states, but that's 'future work').

Choose a reason for hiding this comment

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

Well the other way to go about it is ensure the user never does their own event responses. Its kinda an annoying API, but you can remove all details from events and just generate a singular "you should poll various LDK structs now, cause things have changed" event flag. This forces users into being able to handle arbitrary changes which they may have missed on restart.

Indeed, we want the API to be usable, but we absolutely cannot trade off safety for that, and an event stream abstraction which is impossible to implement correctly is not a fix.

Also, it would likely force us to block the event handler and thereby the entire BP thread?

It wouldn't necessarily have to, LdkLite could persist the event itself into its own DB, return control to LDK, then let the user handle the event as they wish.

Copy link
Contributor

@G8XSU G8XSU Aug 31, 2022

Choose a reason for hiding this comment

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

regarding event reliability, as long as these events represent non-critical notifications type of things, it would have been fine and maybe we can lose those events.
But the problem is do we expect clients to poll through this ? I don't think that is ideal.

Hence handling individual events using event handler is definitely better UX.
Also, it might be helpful to have some distinction b/w core/critical events and some notification/alarm kind of things.

@jkczyz jkczyz self-requested a review August 26, 2022 19:04
@tnull tnull force-pushed the 2022-08-start-of-ldk-lite branch from 1e67afc to 1512e9d Compare August 29, 2022 13:28
@tnull tnull force-pushed the 2022-08-start-of-ldk-lite branch from 1512e9d to 5b9e4c4 Compare August 29, 2022 13:31
Copy link
Contributor

@G8XSU G8XSU left a comment

Choose a reason for hiding this comment

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

Major thing is split PR into multiple PR's
I tried to focus on parts that belonged to this PR.

src/access.rs Outdated
Comment on lines 34 to 35
let blockchain = Mutex::new(blockchain);
let wallet = Mutex::new(wallet);
Copy link
Contributor

Choose a reason for hiding this comment

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

please separate this whole chain-access logic implementation into another PR.
keep the interfaces/empty/dummy functions in this PR.
We should review it carefully separately.

PR Splits:

  1. This PR (with mostly interfaces/empty/dummy functions, license, rustfmt, base setup, logger, ldk-lite builder )
  2. event handling
  3. LDK-lite Error/Exception standards
  4. Chain Access Impl
  5. create_funding_transaction
  6. setup runtime

I focused on reviewing parts that belonged to this PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hm, to be honest I'd rather not because in this initial step the individual components are pretty interdependent and due to change. I imagine it to be more annoying to constantly having to rebase the individual PRs on each other.

So, if you not really insist, I'd like to keep it in this one PR for now and handle forthcoming changes in individual PRs.


# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
Copy link
Contributor

Choose a reason for hiding this comment

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

is this a no-std build?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No, currently std is used all over?

@tnull tnull force-pushed the 2022-08-start-of-ldk-lite branch from f025939 to 5fcb938 Compare August 31, 2022 10:51
@tnull tnull force-pushed the 2022-08-start-of-ldk-lite branch from 0925bb3 to d6f09ad Compare August 31, 2022 18:55
@tnull
Copy link
Collaborator Author

tnull commented Sep 1, 2022

As requested by @G8XSU, I'm closing this in favor of individual PRs.

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.

3 participants