Skip to content

Commit

Permalink
Introduce LSPSDateTime wrapper
Browse files Browse the repository at this point in the history
This wrapper is more ergonomic to use in the local context and will be
used as a serialization wrapper in following commits.
  • Loading branch information
tnull committed Feb 2, 2025
1 parent 71aab53 commit 9db2575
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 50 deletions.
43 changes: 42 additions & 1 deletion lightning-liquidity/src/lsps0/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ use lightning::util::ser::WithoutLength;

use bitcoin::secp256k1::PublicKey;

use core::fmt;
use core::fmt::{self, Display};
use core::str::FromStr;

#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};

use serde::de::{self, MapAccess, Visitor};
use serde::ser::SerializeStruct;
use serde::{Deserialize, Deserializer, Serialize};
Expand Down Expand Up @@ -186,6 +189,44 @@ impl wire::Type for RawLSPSMessage {
#[serde(transparent)]
pub struct LSPSRequestId(pub String);

/// An object representing datetimes as described in bLIP-50 / LSPS0.
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct LSPSDateTime(chrono::DateTime<chrono::Utc>);

impl LSPSDateTime {
/// Returns the LSPSDateTime as RFC3339 formatted string.
pub fn to_rfc3339(&self) -> String {
self.0.to_rfc3339()
}

/// Returns if the given time is in the past.
#[cfg(feature = "std")]
pub fn is_past(&self) -> bool {
let now_seconds_since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock to be ahead of the unix epoch")
.as_secs();
let datetime_seconds_since_epoch =
self.0.timestamp().try_into().expect("expiration to be ahead of unix epoch");
now_seconds_since_epoch > datetime_seconds_since_epoch
}
}

impl FromStr for LSPSDateTime {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let datetime = chrono::DateTime::parse_from_rfc3339(s).map_err(|_| ())?;
Ok(Self(datetime.into()))
}
}

impl Display for LSPSDateTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_rfc3339())
}
}

/// An error returned in response to an JSON-RPC request.
///
/// Please refer to the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification#error_object) for
Expand Down
16 changes: 7 additions & 9 deletions lightning-liquidity/src/lsps1/msgs.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Message, request, and other primitive types used to implement bLIP-51 / LSPS1.
use crate::lsps0::ser::{
string_amount, u32_fee_rate, unchecked_address, unchecked_address_option, LSPSMessage,
LSPSRequestId, LSPSResponseError,
string_amount, u32_fee_rate, unchecked_address, unchecked_address_option, LSPSDateTime,
LSPSMessage, LSPSRequestId, LSPSResponseError,
};

use crate::prelude::String;
Expand All @@ -13,8 +13,6 @@ use lightning_invoice::Bolt11Invoice;

use serde::{Deserialize, Serialize};

use chrono::Utc;

use core::convert::TryFrom;

pub(crate) const LSPS1_GET_INFO_METHOD_NAME: &str = "lsps1.get_info";
Expand Down Expand Up @@ -127,7 +125,7 @@ pub struct LSPS1CreateOrderResponse {
#[serde(flatten)]
pub order: LSPS1OrderParams,
/// The datetime when the order was created
pub created_at: chrono::DateTime<Utc>,
pub created_at: LSPSDateTime,
/// The current status of the order.
pub order_status: LSPS1OrderStatus,
/// Contains details about how to pay for the order.
Expand Down Expand Up @@ -163,7 +161,7 @@ pub struct LSPS1Bolt11PaymentInfo {
/// Indicates the current state of the payment.
pub state: LSPS1PaymentState,
/// The datetime when the payment option expires.
pub expires_at: chrono::DateTime<Utc>,
pub expires_at: LSPSDateTime,
/// The total fee the LSP will charge to open this channel in satoshi.
#[serde(with = "string_amount")]
pub fee_total_sat: u64,
Expand All @@ -180,7 +178,7 @@ pub struct LSPS1OnchainPaymentInfo {
/// Indicates the current state of the payment.
pub state: LSPS1PaymentState,
/// The datetime when the payment option expires.
pub expires_at: chrono::DateTime<Utc>,
pub expires_at: LSPSDateTime,
/// The total fee the LSP will charge to open this channel in satoshi.
#[serde(with = "string_amount")]
pub fee_total_sat: u64,
Expand Down Expand Up @@ -237,11 +235,11 @@ pub struct LSPS1OnchainPayment {
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LSPS1ChannelInfo {
/// The datetime when the funding transaction has been published.
pub funded_at: chrono::DateTime<Utc>,
pub funded_at: LSPSDateTime,
/// The outpoint of the funding transaction.
pub funding_outpoint: OutPoint,
/// The earliest datetime when the channel may be closed by the LSP.
pub expires_at: chrono::DateTime<Utc>,
pub expires_at: LSPSDateTime,
}

/// A request made to an LSP to retrieve information about an previously made order.
Expand Down
12 changes: 7 additions & 5 deletions lightning-liquidity/src/lsps1/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ use super::msgs::{
use crate::message_queue::MessageQueue;

use crate::events::EventQueue;
use crate::lsps0::ser::{LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError};
use crate::lsps0::ser::{
LSPSDateTime, LSPSProtocolMessageHandler, LSPSRequestId, LSPSResponseError,
};
use crate::prelude::{new_hash_map, HashMap, String};
use crate::sync::{Arc, Mutex, RwLock};
use crate::utils;
Expand Down Expand Up @@ -73,7 +75,7 @@ impl OutboundRequestState {

struct OutboundLSPS1Config {
order: LSPS1OrderParams,
created_at: chrono::DateTime<Utc>,
created_at: LSPSDateTime,
payment: LSPS1PaymentInfo,
}

Expand All @@ -84,7 +86,7 @@ struct OutboundCRChannel {

impl OutboundCRChannel {
fn new(
order: LSPS1OrderParams, created_at: chrono::DateTime<Utc>, order_id: LSPS1OrderId,
order: LSPS1OrderParams, created_at: LSPSDateTime, order_id: LSPS1OrderId,
payment: LSPS1PaymentInfo,
) -> Self {
Self {
Expand Down Expand Up @@ -237,7 +239,7 @@ where
/// [`LSPS1ServiceEvent::RequestForPaymentDetails`]: crate::lsps1::event::LSPS1ServiceEvent::RequestForPaymentDetails
pub fn send_payment_details(
&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
payment: LSPS1PaymentInfo, created_at: chrono::DateTime<Utc>,
payment: LSPS1PaymentInfo, created_at: LSPSDateTime,
) -> Result<(), APIError> {
let (result, response) = {
let outer_state_lock = self.per_peer_state.read().unwrap();
Expand Down Expand Up @@ -380,7 +382,7 @@ where
order_id,
order: config.order.clone(),
order_status,
created_at: config.created_at,
created_at: config.created_at.clone(),
payment: config.payment.clone(),
channel,
});
Expand Down
30 changes: 16 additions & 14 deletions lightning-liquidity/src/lsps2/msgs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ use core::convert::TryFrom;
use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};
use chrono::Utc;
use serde::{Deserialize, Serialize};

use lightning::util::scid_utils;

use crate::lsps0::ser::{
string_amount, string_amount_option, LSPSMessage, LSPSRequestId, LSPSResponseError,
string_amount, string_amount_option, LSPSDateTime, LSPSMessage, LSPSRequestId,
LSPSResponseError,
};
use crate::prelude::{String, Vec};
use crate::utils;
Expand Down Expand Up @@ -42,7 +42,7 @@ pub struct LSPS2RawOpeningFeeParams {
/// A fee proportional to the size of the initial payment.
pub proportional: u32,
/// An [`ISO8601`](https://www.iso.org/iso-8601-date-and-time-format.html) formatted date for which these params are valid.
pub valid_until: chrono::DateTime<Utc>,
pub valid_until: LSPSDateTime,
/// The number of blocks after confirmation that the LSP promises it will keep the channel alive without closing.
pub min_lifetime: u32,
/// The maximum number of blocks that the client is allowed to set its `to_self_delay` parameter.
Expand Down Expand Up @@ -93,7 +93,7 @@ pub struct LSPS2OpeningFeeParams {
/// A fee proportional to the size of the initial payment.
pub proportional: u32,
/// An [`ISO8601`](https://www.iso.org/iso-8601-date-and-time-format.html) formatted date for which these params are valid.
pub valid_until: chrono::DateTime<Utc>,
pub valid_until: LSPSDateTime,
/// The number of blocks after confirmation that the LSP promises it will keep the channel alive without closing.
pub min_lifetime: u32,
/// The maximum number of blocks that the client is allowed to set its `to_self_delay` parameter.
Expand Down Expand Up @@ -214,15 +214,17 @@ impl From<LSPS2Message> for LSPSMessage {
#[cfg(test)]
mod tests {
use super::*;

use crate::alloc::string::ToString;
use crate::lsps2::utils::is_valid_opening_fee_params;

use core::str::FromStr;

#[test]
fn into_opening_fee_params_produces_valid_promise() {
let min_fee_msat = 100;
let proportional = 21;
let valid_until: chrono::DateTime<Utc> =
chrono::DateTime::parse_from_rfc3339("2035-05-20T08:30:45Z").unwrap().into();
let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
let min_lifetime = 144;
let max_client_to_self_delay = 128;
let min_payment_size_msat = 1;
Expand Down Expand Up @@ -257,7 +259,7 @@ mod tests {
fn changing_single_field_produced_invalid_params() {
let min_fee_msat = 100;
let proportional = 21;
let valid_until = chrono::DateTime::parse_from_rfc3339("2035-05-20T08:30:45Z").unwrap();
let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
let min_lifetime = 144;
let max_client_to_self_delay = 128;
let min_payment_size_msat = 1;
Expand All @@ -266,7 +268,7 @@ mod tests {
let raw = LSPS2RawOpeningFeeParams {
min_fee_msat,
proportional,
valid_until: valid_until.into(),
valid_until,
min_lifetime,
max_client_to_self_delay,
min_payment_size_msat,
Expand All @@ -284,7 +286,7 @@ mod tests {
fn wrong_secret_produced_invalid_params() {
let min_fee_msat = 100;
let proportional = 21;
let valid_until = chrono::DateTime::parse_from_rfc3339("2035-05-20T08:30:45Z").unwrap();
let valid_until = LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap();
let min_lifetime = 144;
let max_client_to_self_delay = 128;
let min_payment_size_msat = 1;
Expand All @@ -293,7 +295,7 @@ mod tests {
let raw = LSPS2RawOpeningFeeParams {
min_fee_msat,
proportional,
valid_until: valid_until.into(),
valid_until,
min_lifetime,
max_client_to_self_delay,
min_payment_size_msat,
Expand All @@ -313,7 +315,7 @@ mod tests {
fn expired_params_produces_invalid_params() {
let min_fee_msat = 100;
let proportional = 21;
let valid_until = chrono::DateTime::parse_from_rfc3339("2023-05-20T08:30:45Z").unwrap();
let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
let min_lifetime = 144;
let max_client_to_self_delay = 128;
let min_payment_size_msat = 1;
Expand All @@ -322,7 +324,7 @@ mod tests {
let raw = LSPS2RawOpeningFeeParams {
min_fee_msat,
proportional,
valid_until: valid_until.into(),
valid_until,
min_lifetime,
max_client_to_self_delay,
min_payment_size_msat,
Expand All @@ -339,7 +341,7 @@ mod tests {
fn buy_request_serialization() {
let min_fee_msat = 100;
let proportional = 21;
let valid_until = chrono::DateTime::parse_from_rfc3339("2023-05-20T08:30:45Z").unwrap();
let valid_until = LSPSDateTime::from_str("2023-05-20T08:30:45Z").unwrap();
let min_lifetime = 144;
let max_client_to_self_delay = 128;
let min_payment_size_msat = 1;
Expand All @@ -348,7 +350,7 @@ mod tests {
let raw = LSPS2RawOpeningFeeParams {
min_fee_msat,
proportional,
valid_until: valid_until.into(),
valid_until,
min_lifetime,
max_client_to_self_delay,
min_payment_size_msat,
Expand Down
12 changes: 7 additions & 5 deletions lightning-liquidity/src/lsps2/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1419,12 +1419,14 @@ fn calculate_amount_to_forward_per_htlc(

#[cfg(test)]
mod tests {

use super::*;
use chrono::TimeZone;
use chrono::Utc;

use crate::lsps0::ser::LSPSDateTime;

use proptest::prelude::*;

use core::str::FromStr;

const MAX_VALUE_MSAT: u64 = 21_000_000_0000_0000_000;

fn arb_forward_amounts() -> impl Strategy<Value = (u64, u64, u64, u64)> {
Expand Down Expand Up @@ -1518,7 +1520,7 @@ mod tests {
let opening_fee_params = LSPS2OpeningFeeParams {
min_fee_msat: 10_000_000,
proportional: 10_000,
valid_until: Utc.timestamp_opt(3000, 0).unwrap(),
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
min_lifetime: 4032,
max_client_to_self_delay: 2016,
min_payment_size_msat: 10_000_000,
Expand Down Expand Up @@ -1710,7 +1712,7 @@ mod tests {
let opening_fee_params = LSPS2OpeningFeeParams {
min_fee_msat: 10_000_000,
proportional: 10_000,
valid_until: Utc.timestamp_opt(3000, 0).unwrap(),
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
min_lifetime: 4032,
max_client_to_self_delay: 2016,
min_payment_size_msat: 10_000_000,
Expand Down
14 changes: 1 addition & 13 deletions lightning-liquidity/src/lsps2/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ use bitcoin::hashes::hmac::{Hmac, HmacEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::{Hash, HashEngine};

#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};

/// Determines if the given parameters are valid given the secret used to generate the promise.
pub fn is_valid_opening_fee_params(
fee_params: &LSPS2OpeningFeeParams, promise_secret: &[u8; 32],
Expand All @@ -35,16 +32,7 @@ pub fn is_valid_opening_fee_params(
pub fn is_expired_opening_fee_params(fee_params: &LSPS2OpeningFeeParams) -> bool {
#[cfg(feature = "std")]
{
let seconds_since_epoch = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock to be ahead of the unix epoch")
.as_secs();
let valid_until_seconds_since_epoch = fee_params
.valid_until
.timestamp()
.try_into()
.expect("expiration to be ahead of unix epoch");
seconds_since_epoch > valid_until_seconds_since_epoch
fee_params.valid_until.is_past()
}
#[cfg(not(feature = "std"))]
{
Expand Down
6 changes: 3 additions & 3 deletions lightning-liquidity/tests/lsps2_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod common;
use common::{create_service_and_client_nodes, get_lsps_message, Node};

use lightning_liquidity::events::LiquidityEvent;
use lightning_liquidity::lsps0::ser::LSPSDateTime;
use lightning_liquidity::lsps2::client::LSPS2ClientConfig;
use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
use lightning_liquidity::lsps2::msgs::LSPS2RawOpeningFeeParams;
Expand All @@ -24,8 +25,7 @@ use bitcoin::hashes::{sha256, Hash};
use bitcoin::secp256k1::{PublicKey, Secp256k1};
use bitcoin::Network;

use chrono::DateTime;

use std::str::FromStr;
use std::time::Duration;

fn create_jit_invoice(
Expand Down Expand Up @@ -128,7 +128,7 @@ fn invoice_generation_flow() {
let raw_opening_params = LSPS2RawOpeningFeeParams {
min_fee_msat: 100,
proportional: 21,
valid_until: DateTime::parse_from_rfc3339("2035-05-20T08:30:45Z").unwrap().into(),
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
min_lifetime: 144,
max_client_to_self_delay: 128,
min_payment_size_msat: 1,
Expand Down

0 comments on commit 9db2575

Please sign in to comment.