-
Notifications
You must be signed in to change notification settings - Fork 402
LSPS2: Add TimeProvider trait for expiry checks in no-std and std builds #3746
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
base: main
Are you sure you want to change the base?
LSPS2: Add TimeProvider trait for expiry checks in no-std and std builds #3746
Conversation
- Add 'time' feature flag to allow disabling time-dependent functionality. - Include 'time' in default features - It will allow users to disable SystemTime::now without disabling all std features
- Provides a single method duration_since_epoch() -> Duration - Also a DefaultTimeProvider implementation is provided that uses std::time
👋 Thanks for assigning @tnull as a reviewer! |
- is_past() on LSPSDateTime is changed so it receives a time_provider to get current time. - is_expired_opening_fee_params() now receives a time_provider to pass to is_past() - is_expired_opening_fee_params now does not depend on std being enabled. - LSPS2 service now passes around the time_provider to be able to use wherever necessary. - LSPS2 service now provides two new() methods: 1. a default new() that uses DefaultTimeProvider 2. a new_with_custom_time_provider() that expects a custom time_provider Devices with no access to std will need to use the latter
49c8b02
to
0f17d59
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3746 +/- ##
==========================================
- Coverage 89.15% 89.08% -0.08%
==========================================
Files 156 156
Lines 123837 123875 +38
Branches 123837 123875 +38
==========================================
- Hits 110408 110348 -60
- Misses 10754 10835 +81
- Partials 2675 2692 +17 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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 probably still miss some context, but kept thinking why not just inject the dependency always and let the user instantiate either the std time provider or their own.
/// This trait is used to provide the current time service operations and to convert between timestamps and durations. | ||
pub trait TimeProvider { | ||
/// Get the current time as a duration since the Unix epoch. | ||
fn duration_since_epoch(&self) -> Duration; |
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 know this name is sort of standard in LDK, but to me something like unix_time
would have been better. Duration is already the type, and epoch doesn't specify which epoch.
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 disagree, exactly because you'd expect unix_time
to be a SystemTime
or u64
, but not a Duration
. Plus, homogeneity is an important API feature, so keeping this in-line with the rest of internal APIs is a big plus, IMO.
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.
No need to change it now. As you say, keeping it consistent is a good API feature. For me as a developer though, this isn't the most descriptive name.
#[cfg(feature = "time")] | ||
{ | ||
// Use default new if time feature is enabled and no custom provider | ||
LSPS2ServiceHandler::new( |
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.
Can't TimeProvider
be simply required here (no option), and then instantiate or panic higher up in the call stack? Also no need for two new
variants then.
|
||
/// Default time provider using the system clock. | ||
#[derive(Clone, Debug)] | ||
#[cfg(feature = "time")] |
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 re-read the PR description, but was still thinking why isn't this just std gated?
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.
Sorry for not mentioning this in the PR description (I’ll update it shortly). This change comes from a discussion in this PR, specifically this comment from tnull:
Actually, now that we went this way, let's not feature gate this behind std, but rather a new time feature. Context is the following: on WASM, you generally have access to std, but not entirely. For example, time-dependent behaviors like SystemTime::now would just panic at runtime. However, when previously feature-gating on std, we good feedback from some WASM users that would like to keep using the non-time std features.
TLDR: would be nice to allow users to disable the SystemTime::now default without disabling the entirety of std. So let's introduce a time feature, as we did in other crates (cf. lightning-transaction-sync).
@tnull tagging Elias for review and input.
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.
But if those users run with std
and just don't inject the panic'ing default std time provider and instead supply their own, aren't they good with the time
flag?
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.
But if those users run with
std
and just don't inject the panic'ing default std time provider and instead supply their own, aren't they good with thetime
flag?
Yes, but we still shouldn't expose a 'default' API that would panic if used? That said, given that it requires std
, we should probably feature-gate this on all(feature = "std", feature = "time")
, or have time
enable std
.
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.
Yes, but we still shouldn't expose a 'default' API that would panic if used?
If some platforms have an incomplete implementation of the std feature, is it the responsibility of this library to protect against hitting the missing part via a feature flag?
I also wouldn't make it a 'default' API, but just require a time provider to be supplied. Overall it seems to me that a time
flag may be unnecessary.
@@ -14,8 +14,9 @@ categories = ["cryptography::cryptocurrencies"] | |||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | |||
|
|||
[features] | |||
default = ["std"] | |||
default = ["std", "time"] |
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.
Disabling SystemTIme::now
without disabling all of std: couldn't this be realized by just passing in another time provider than the std one, without requiring this flag?
👋 The first review has been submitted! Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer. |
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 trait is used to provide the current time service operations and to convert between timestamps and durations. | ||
pub trait TimeProvider { | ||
/// Get the current time as a duration since the Unix epoch. | ||
fn duration_since_epoch(&self) -> Duration; |
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 disagree, exactly because you'd expect unix_time
to be a SystemTime
or u64
, but not a Duration
. Plus, homogeneity is an important API feature, so keeping this in-line with the rest of internal APIs is a big plus, IMO.
|
||
/// Default time provider using the system clock. | ||
#[derive(Clone, Debug)] | ||
#[cfg(feature = "time")] |
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.
But if those users run with
std
and just don't inject the panic'ing default std time provider and instead supply their own, aren't they good with thetime
flag?
Yes, but we still shouldn't expose a 'default' API that would panic if used? That said, given that it requires std
, we should probably feature-gate this on all(feature = "std", feature = "time")
, or have time
enable std
.
/// Trait defining a time provider | ||
/// | ||
/// This trait is used to provide the current time service operations and to convert between timestamps and durations. | ||
pub trait TimeProvider { |
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.
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 PR is extracting some of the code from LSPS5 PR. My idea was to get this PR merged and then rebase LSPS5 on top of it, that way we would lose some commits from the LSPS5 PR and make it a bit smaller (same idea with the solution to this issue #3459 (PR coming soon), where I'm extracting some of the code written in the LSPS5 PR and putting it in a new PR, that way making the LSPS5 PR smaller)
I do kinda wonder if this is worth it? What is the practical user impact of not checking for expiry? Presumably the LSP will simply fail the attempt to use the past offers and the client will simply request new offers, assuming it has past offers, but currently we don't persist offers (and probably shouldn't?) so in practice we'll basically always be requesting fresh offers anyway. |
Note that the |
Closes #3471
As discussed here #3662 (comment)
This PR introduces a TimeProvider trait to enable expiry checks for OpeningFeeParams on LSPS2 in both std and no-std environments. Previously, expiry validation was skipped in no-std builds due to the lack of a system clock.
With this change:
This abstraction will be even more useful for LSPS5, which relies on time-based logic much more extensively than LSPS2.