lexe-common
Advanced tools
| { | ||
| "git": { | ||
| "sha1": "64d6f049576247f7e48a0ef09d191999b14272b9" | ||
| "sha1": "553d66ebde3e8ba9e951f94c11a4075f4351c912" | ||
| }, | ||
| "path_in_vcs": "public/lexe-common" | ||
| } |
+15
-15
@@ -505,5 +505,5 @@ # This file is automatically @generated by Cargo. | ||
| name = "lexe-byte-array" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "dc728990c344e970e15ef71db3c7ae165b1cb6168d3e772849622e4b9a70f648" | ||
| checksum = "fee3d67908f9aa7ac9f07dd3114b51a4bfd09759eb2b4c6bb6df83faab0bb21c" | ||
| dependencies = [ | ||
@@ -515,3 +515,3 @@ "lexe-hex", | ||
| name = "lexe-common" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| dependencies = [ | ||
@@ -564,5 +564,5 @@ "anyhow", | ||
| name = "lexe-crypto" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "9318ef44606022ff7ca7798a747ad126805ad9054a073e3378aaa5a1d5bcd5ec" | ||
| checksum = "4488af261b1481a2fdb7f01fd0e1972990c9739b1012afce6974db977b344114" | ||
| dependencies = [ | ||
@@ -585,5 +585,5 @@ "bcs", | ||
| name = "lexe-enclave" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "ff757e0c4d9f0ba7f5c635f2ce53c25cf2aee8cd3dab63acb5cc2eecb0bdb46e" | ||
| checksum = "a9692fc4a8b45b4811b8404dd59357c8ae442923a96ce660f2a1b30d4b226d05" | ||
| dependencies = [ | ||
@@ -605,11 +605,11 @@ "cfg-if", | ||
| name = "lexe-hex" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "1c08f90b258615b4ada83e4ed35d596cbe6af69cb35512e78460ac8a11b5d753" | ||
| checksum = "4f7211357af2a696cefba7b0860471824eabe310e08eedd361e65b4612feed13" | ||
| [[package]] | ||
| name = "lexe-serde" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "c5841cf7e9d34aa855ba6a077886980d3a9971a885afd527dfef6a525de009d8" | ||
| checksum = "6e222e3f705327b7aa55c4ed0791c18b42c315d2216ea714ef4a7f56adbb36a7" | ||
| dependencies = [ | ||
@@ -623,5 +623,5 @@ "base64", | ||
| name = "lexe-sha256" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "5fb14bba432279681424729b4ed9ab153dad35f760bc744a634e7e31287db461" | ||
| checksum = "1b8f46023da11d8241a1b6f942f1f3623614cf428e581de6a6d02d24e91c5ea5" | ||
| dependencies = [ | ||
@@ -636,5 +636,5 @@ "lexe-byte-array", | ||
| name = "lexe-std" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||
| checksum = "77db2d58961be8209cacea384e9c62c56bb447e6c52e36fb79ba017dd3b82763" | ||
| checksum = "4f10fc9fbd043dd6170b19ee0a48ff210800c212e458f8d24fe3262c928568cd" | ||
| dependencies = [ | ||
@@ -641,0 +641,0 @@ "ref-cast", |
+10
-10
@@ -16,3 +16,3 @@ # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO | ||
| name = "lexe-common" | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| authors = [ | ||
@@ -105,21 +105,21 @@ "Max Fang <max@lexe.app>", | ||
| [dependencies.lexe-byte-array] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| [dependencies.lexe-crypto] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| [dependencies.lexe-enclave] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| [dependencies.lexe-hex] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| [dependencies.lexe-serde] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| [dependencies.lexe-sha256] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| [dependencies.lexe-std] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| features = ["ref-cast"] | ||
@@ -213,7 +213,7 @@ | ||
| [dev-dependencies.lexe-crypto] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| features = ["test-utils"] | ||
| [dev-dependencies.lexe-enclave] | ||
| version = "0.1.16" | ||
| version = "0.1.17" | ||
| features = ["test-utils"] | ||
@@ -220,0 +220,0 @@ |
+1
-1
@@ -14,3 +14,3 @@ // TODO(max): All of these modules should be moved to `lexe_api[_core]`. | ||
| pub mod provision; | ||
| /// Revocable clients. | ||
| /// The revocable clients interface for handshake-time revocation checks. | ||
| pub mod revocable_clients; | ||
@@ -17,0 +17,0 @@ /// `TestEvent`. |
+1
-1
@@ -196,3 +196,3 @@ // bearer auth v1 | ||
| /// - a CONNECT tunnel to the user's own node, or | ||
| /// - a request the gateway makes to the backend (`AppBackendApi`) on the | ||
| /// - a request the gateway makes to the backend (`UserBackendApi`) on the | ||
| /// user's behalf, using its own backend credentials. | ||
@@ -199,0 +199,0 @@ /// |
+26
-308
@@ -1,320 +0,38 @@ | ||
| //! Information about a client | ||
| //! The `GetRevocableClientStatus` interface for handshake-time revocation | ||
| //! checks. | ||
| use std::collections::HashMap; | ||
| use std::fmt; | ||
| use anyhow::anyhow; | ||
| use lexe_crypto::ed25519; | ||
| use lexe_serde::{ | ||
| base64_or_bytes, | ||
| optopt::{self, none}, | ||
| }; | ||
| #[cfg(any(test, feature = "test-utils"))] | ||
| use proptest_derive::Arbitrary; | ||
| use serde::{Deserialize, Serialize}; | ||
| use crate::{ | ||
| api::{ | ||
| auth::{BearerAuthToken, LexeScope}, | ||
| user::UserPk, | ||
| }, | ||
| time::TimestampMs, | ||
| }; | ||
| use crate::time::TimestampMs; | ||
| /// All revocable clients which have ever been created. | ||
| /// A handshake-time validity check for a revocable client cert. | ||
| /// | ||
| /// This struct must be persisted in a rollback-resistant data store. | ||
| // We don't *really* need to persist revoked clients but might as well keep them | ||
| // around for historical reference. We can prune them later if needed. | ||
| #[derive(Clone, Debug, Default, Serialize, Deserialize)] | ||
| pub struct RevocableClients { | ||
| pub clients: HashMap<ed25519::PublicKey, RevocableClient>, | ||
| } | ||
| impl RevocableClients { | ||
| /// A user shouldn't need more than 100 clients. | ||
| pub const MAX_LEN: usize = 100; | ||
| /// An iterator over all clients which are valid right now. | ||
| pub fn iter_valid( | ||
| /// This trait exists mainly so `RevocableClients` can live in `lexe-api-core` | ||
| /// rather than `lexe-common`. | ||
| /// | ||
| /// Implemented by `lexe_api_core::revocable_clients::RevocableClientsHandle`. | ||
| pub trait GetRevocableClientStatus: fmt::Debug + Send + Sync { | ||
| /// The status of the revocable client identified by `client_pk` at `now`, | ||
| /// or [`None`] if no client with this pubkey exists. | ||
| fn get_client_status( | ||
| &self, | ||
| ) -> impl Iterator<Item = (&ed25519::PublicKey, &RevocableClient)> { | ||
| self.iter_valid_at(TimestampMs::now()) | ||
| } | ||
| /// An iterator over all clients which are valid at the given time. | ||
| pub fn iter_valid_at( | ||
| &self, | ||
| client_pk: &ed25519::PublicKey, | ||
| now: TimestampMs, | ||
| ) -> impl Iterator<Item = (&ed25519::PublicKey, &RevocableClient)> { | ||
| self.clients | ||
| .iter() | ||
| .filter(|(_k, v)| !v.is_revoked) | ||
| .filter(move |(_k, v)| !v.is_expired_at(now)) | ||
| } | ||
| ) -> Option<RevocableClientStatus>; | ||
| } | ||
| /// Information about a revocable client. | ||
| /// Each client is issued a `RevocableClientCert` whose pubkey is saved here. | ||
| #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | ||
| #[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))] | ||
| pub struct RevocableClient { | ||
| /// The client's cert pubkey. | ||
| // TODO(max): In the future, bearer auth tokens could be issued to this pk. | ||
| pub pubkey: ed25519::PublicKey, | ||
| /// When we first issued the client cert and created this client. | ||
| pub created_at: TimestampMs, | ||
| /// The time after which the server will no longer accept this client. | ||
| /// [`None`] indicates that the client will never expire (use carefully!). | ||
| /// This expiration time can be extended at any time. | ||
| pub expires_at: Option<TimestampMs>, | ||
| /// Optional user-provided label for this client. | ||
| #[cfg_attr( | ||
| any(test, feature = "test-utils"), | ||
| proptest(strategy = "arb::any_label()") | ||
| )] | ||
| pub label: Option<String>, | ||
| /// The authorization scopes allowed for this client. | ||
| // TODO(max): This scope is currently ineffective. | ||
| pub scope: LexeScope, | ||
| /// Whether this client has been revoked. Revocation is permanent. | ||
| pub is_revoked: bool, | ||
| // TODO(phlip9): add "pausing" a client's access temporarily? | ||
| } | ||
| impl RevocableClient { | ||
| /// Limit label length to 64 bytes | ||
| pub const MAX_LABEL_LEN: usize = 64; | ||
| /// Whether the client is valid at a given time (not revoked, not expired). | ||
| #[must_use] | ||
| pub fn is_valid_at(&self, now: TimestampMs) -> bool { | ||
| !self.is_revoked && !self.is_expired_at(now) | ||
| } | ||
| /// Whether the client is expired at the given time. | ||
| #[must_use] | ||
| pub fn is_expired_at(&self, now: TimestampMs) -> bool { | ||
| if let Some(expiration) = self.expires_at | ||
| && now > expiration | ||
| { | ||
| return true; | ||
| } | ||
| false | ||
| } | ||
| } | ||
| /// A request to list all revocable clients. | ||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| #[cfg_attr(any(test, feature = "test-utils"), derive(Eq, PartialEq, Arbitrary))] | ||
| pub struct GetRevocableClients { | ||
| /// Whether to return only clients which are currently valid. | ||
| pub valid_only: bool, | ||
| } | ||
| /// A request to create a new revocable client. | ||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| pub struct CreateRevocableClientRequest { | ||
| /// The expiration after which the node should reject this client. | ||
| /// [`None`] indicates that the client will never expire (use carefully!). | ||
| pub expires_at: Option<TimestampMs>, | ||
| /// Optional user-provided label for this client. | ||
| pub label: Option<String>, | ||
| /// The authorization scopes allowed for this client. | ||
| pub scope: LexeScope, | ||
| } | ||
| /// The response to [`CreateRevocableClientRequest`]. | ||
| #[derive(Clone, Debug, Serialize, Deserialize)] | ||
| pub struct CreateRevocableClientResponse { | ||
| /// The user public key associated with these credentials. | ||
| /// Always `Some` since `node-v0.8.11`. | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub user_pk: Option<UserPk>, | ||
| /// The client cert pubkey. | ||
| pub pubkey: ed25519::PublicKey, | ||
| /// When this client was created. | ||
| pub created_at: TimestampMs, | ||
| /// The DER-encoded ephemeral issuing CA cert that the client should trust. | ||
| /// The handshake-time status of a revocable client cert. | ||
| pub enum RevocableClientStatus { | ||
| /// Not revoked and not expired — accept the cert. | ||
| /// | ||
| /// This is just packaged alongside the rest for convenience. | ||
| // NOTE: This client cert goes *last* in the cert chain given to rustls. | ||
| #[serde(with = "base64_or_bytes")] | ||
| pub eph_ca_cert_der: Vec<u8>, | ||
| /// The DER-encoded client cert to present when connecting to the node. | ||
| // NOTE: This client cert goes *first* in the cert chain given to rustls. | ||
| #[serde(with = "base64_or_bytes")] | ||
| pub rev_client_cert_der: Vec<u8>, | ||
| /// The DER-encoded client cert key. | ||
| #[serde(with = "base64_or_bytes")] | ||
| pub rev_client_cert_key_der: Vec<u8>, | ||
| /// A long-lived [`LexeScope::GatewayProxy`] token for connecting to the | ||
| /// user's node via the gateway proxy. Always `Some` for user nodes. | ||
| #[serde(skip_serializing_if = "Option::is_none")] | ||
| pub gateway_proxy_token: Option<BearerAuthToken>, | ||
| /// NOTE: This only means that the client is allowed to connect; it does NOT | ||
| /// mean that the client is *authorized* to do whatever it is asking to do. | ||
| /// Client scopes and budgets must still be enforced at a higher level. | ||
| Valid, | ||
| /// The client was revoked. Revocation is permanent. | ||
| Revoked, | ||
| /// The client is expired as of the queried time. | ||
| Expired, | ||
| } | ||
| /// A request to update a single [`RevocableClient`]. | ||
| /// | ||
| /// All fields except `pubkey` are optional. If a field is `None`, it will not | ||
| /// be updated. For example: | ||
| /// | ||
| /// * `expires_at: None` -> don't change | ||
| /// * `expires_at: Some(None)` -> set to never expire | ||
| /// * `expires_at: Some(TimestampMs(..))` -> set to expire at that time | ||
| #[derive(Serialize, Deserialize)] | ||
| #[cfg_attr(test, derive(Debug, Eq, PartialEq, Arbitrary))] | ||
| pub struct UpdateClientRequest { | ||
| /// The pubkey of the client to update. | ||
| pub pubkey: ed25519::PublicKey, | ||
| /// Set this client's expiration (`Some(None)` means never expire). | ||
| #[serde(default, skip_serializing_if = "none", with = "optopt")] | ||
| pub expires_at: Option<Option<TimestampMs>>, | ||
| /// Set this client's label. | ||
| #[serde(default, skip_serializing_if = "none", with = "optopt")] | ||
| #[cfg_attr(test, proptest(strategy = "arb::any_label_update()"))] | ||
| pub label: Option<Option<String>>, | ||
| /// Set the authorization scopes allowed for this client. | ||
| #[serde(skip_serializing_if = "none")] | ||
| pub scope: Option<LexeScope>, | ||
| /// Set this to revoke or unrevoke the client. Revocation is permanent, so | ||
| /// you cannot unrevoke a client once it is revoked. | ||
| #[serde(skip_serializing_if = "none")] | ||
| pub is_revoked: Option<bool>, | ||
| } | ||
| /// The updated [`RevocableClient`] after a successful update. | ||
| #[derive(Serialize, Deserialize)] | ||
| pub struct UpdateClientResponse { | ||
| pub client: RevocableClient, | ||
| } | ||
| impl RevocableClient { | ||
| /// Apply an update to this client, returning a copy with updates applied. | ||
| pub fn update(&self, req: UpdateClientRequest) -> anyhow::Result<Self> { | ||
| let UpdateClientRequest { | ||
| pubkey: req_pubkey, | ||
| expires_at: req_expires_at, | ||
| label: req_label, | ||
| scope: req_scope, | ||
| is_revoked: req_is_revoked, | ||
| } = req; | ||
| let mut out = self.clone(); | ||
| if self.pubkey != req_pubkey { | ||
| debug_assert!(false); | ||
| return Err(anyhow!("Cannot update a different client")); | ||
| } | ||
| if let Some(expires_at) = req_expires_at { | ||
| // TODO(max): Maybe need some validation here | ||
| out.expires_at = expires_at; | ||
| } | ||
| if let Some(maybe_label) = req_label { | ||
| if let Some(label) = &maybe_label | ||
| && label.len() > Self::MAX_LABEL_LEN | ||
| { | ||
| return Err(anyhow!( | ||
| "Label must not be longer than {} bytes", | ||
| Self::MAX_LABEL_LEN, | ||
| )); | ||
| } | ||
| out.label = maybe_label; | ||
| } | ||
| if let Some(scope) = req_scope { | ||
| // TODO(max): Need some validation here; can't request broader | ||
| // scope, only some clients can call, etc. | ||
| out.scope = scope; | ||
| } | ||
| if let Some(revoke) = req_is_revoked { | ||
| if self.is_revoked && !revoke { | ||
| return Err(anyhow!("Cannot unrevoke a client")); | ||
| } | ||
| out.is_revoked = revoke; | ||
| } | ||
| Ok(out) | ||
| } | ||
| } | ||
| #[cfg(any(test, feature = "test-utils"))] | ||
| mod arb { | ||
| use std::ops::RangeInclusive; | ||
| use proptest::{collection::vec, option, strategy::Strategy}; | ||
| use super::*; | ||
| use crate::test_utils::arbitrary; | ||
| pub fn any_label() -> impl Strategy<Value = Option<String>> { | ||
| static RANGES: &[RangeInclusive<char>] = | ||
| &['0'..='9', 'A'..='Z', 'a'..='z']; | ||
| let any_alphanum_char = proptest::char::ranges(RANGES.into()); | ||
| option::of( | ||
| vec(any_alphanum_char, 0..=RevocableClient::MAX_LABEL_LEN) | ||
| .prop_map(String::from_iter), | ||
| ) | ||
| } | ||
| #[allow(dead_code)] | ||
| pub fn any_label_update() -> impl Strategy<Value = Option<Option<String>>> { | ||
| option::of(arbitrary::any_option_simple_string()) | ||
| } | ||
| } | ||
| #[cfg(test)] | ||
| mod test { | ||
| use super::*; | ||
| use crate::{root_seed::RootSeed, test_utils::roundtrip}; | ||
| #[test] | ||
| fn rev_client_ser_basic() { | ||
| let client1 = RevocableClient { | ||
| pubkey: *RootSeed::from_u64(1).derive_user_key_pair().public_key(), | ||
| created_at: TimestampMs::from_secs_u32(69), | ||
| expires_at: Some(TimestampMs::from_secs_u32(420)), | ||
| label: Some("deez".to_string()), | ||
| scope: LexeScope::All, | ||
| is_revoked: false, | ||
| }; | ||
| let client_json = serde_json::to_string_pretty(&client1).unwrap(); | ||
| // println!("{client_json}"); | ||
| let client_json_snapshot = r#"{ | ||
| "pubkey": "aa8e3e1a9bffdb073507f23474100619fdd4e392ef0ff1e89348252f287a06fc", | ||
| "created_at": 69000, | ||
| "expires_at": 420000, | ||
| "label": "deez", | ||
| "scope": "All", | ||
| "is_revoked": false | ||
| }"#; | ||
| assert_eq!(client_json, client_json_snapshot); | ||
| let client2 = | ||
| serde_json::from_str::<RevocableClient>(&client_json).unwrap(); | ||
| assert_eq!(client1, client2); | ||
| } | ||
| #[test] | ||
| fn test_update_request_serde() { | ||
| roundtrip::json_string_roundtrip_proptest::<UpdateClientRequest>(); | ||
| } | ||
| #[test] | ||
| fn test_get_revocable_clients_serde() { | ||
| roundtrip::query_string_roundtrip_proptest::<GetRevocableClients>(); | ||
| } | ||
| } |
+60
-53
@@ -9,6 +9,3 @@ use std::{fmt, str::FromStr}; | ||
| use lexe_std::Apply; | ||
| use lightning::{ | ||
| chain::transaction::OutPoint, | ||
| ln::{channel_state::ChannelDetails, types::ChannelId}, | ||
| }; | ||
| use lightning::ln::channel_state::ChannelDetails; | ||
| #[cfg(any(test, feature = "test-utils"))] | ||
@@ -28,2 +25,4 @@ use proptest_derive::Arbitrary; | ||
| /// A newtype for [`lightning::ln::types::ChannelId`]. | ||
| // | ||
| // NOTE: This is exposed in the Rust SDK. | ||
| #[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))] | ||
@@ -33,15 +32,15 @@ #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] | ||
| #[repr(transparent)] | ||
| pub struct LxChannelId(#[serde(with = "hexstr_or_bytes")] pub [u8; 32]); | ||
| pub struct ChannelId(#[serde(with = "hexstr_or_bytes")] pub [u8; 32]); | ||
| lexe_byte_array::impl_byte_array!(LxChannelId, 32); | ||
| lexe_byte_array::impl_fromstr_fromhex!(LxChannelId, 32); | ||
| lexe_byte_array::impl_debug_display_as_hex!(LxChannelId); | ||
| lexe_byte_array::impl_byte_array!(ChannelId, 32); | ||
| lexe_byte_array::impl_fromstr_fromhex!(ChannelId, 32); | ||
| lexe_byte_array::impl_debug_display_as_hex!(ChannelId); | ||
| impl From<ChannelId> for LxChannelId { | ||
| fn from(cid: ChannelId) -> Self { | ||
| impl From<lightning::ln::types::ChannelId> for ChannelId { | ||
| fn from(cid: lightning::ln::types::ChannelId) -> Self { | ||
| Self(cid.0) | ||
| } | ||
| } | ||
| impl From<LxChannelId> for ChannelId { | ||
| fn from(cid: LxChannelId) -> Self { | ||
| impl From<ChannelId> for lightning::ln::types::ChannelId { | ||
| fn from(cid: ChannelId) -> Self { | ||
| Self(cid.0) | ||
@@ -51,17 +50,21 @@ } | ||
| /// See: [`lightning::ln::channel_state::ChannelDetails::user_channel_id`] | ||
| /// | ||
| /// The user channel id lets us consistently identify a channel through its | ||
| /// whole lifecycle. | ||
| /// | ||
| /// The main issue is that we don't know the [`LxChannelId`] until we've | ||
| /// actually talked to the remote node and agreed to open a channel. The second | ||
| /// issue is that we can't easily observe and correlate any errors from channel | ||
| /// negotiation beyond some basic checks before we send any messages. | ||
| /// A client-generated id that identifies a channel throughout its entire | ||
| /// lifecycle, including before the channel is confirmed on-chain and assigned | ||
| /// its [`ChannelId`]. | ||
| // | ||
| // See: `lightning::ln::channel_state::ChannelDetails::user_channel_id` | ||
| // | ||
| // The user channel id lets us consistently identify a channel through its whole | ||
| // lifecycle. The main issue is that we don't know the `ChannelId` until we've | ||
| // actually talked to the remote node and agreed to open a channel. The second | ||
| // issue is that we can't easily observe and correlate any errors from channel | ||
| // negotiation beyond some basic checks before we send any messages. | ||
| // | ||
| // NOTE: This is exposed in the Rust SDK. | ||
| #[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))] | ||
| #[derive(Copy, Clone, Eq, PartialEq, Hash, RefCast, Serialize, Deserialize)] | ||
| #[repr(transparent)] | ||
| pub struct LxUserChannelId(#[serde(with = "hexstr_or_bytes")] pub [u8; 16]); | ||
| pub struct UserChannelId(#[serde(with = "hexstr_or_bytes")] pub [u8; 16]); | ||
| impl LxUserChannelId { | ||
| impl UserChannelId { | ||
| #[inline] | ||
@@ -76,12 +79,12 @@ pub fn to_u128(self) -> u128 { | ||
| pub fn derive_temporary_channel_id(&self) -> LxChannelId { | ||
| LxChannelId(sha256::digest(&self.0).to_array()) | ||
| pub fn derive_temporary_channel_id(&self) -> ChannelId { | ||
| ChannelId(sha256::digest(&self.0).to_array()) | ||
| } | ||
| } | ||
| lexe_byte_array::impl_byte_array!(LxUserChannelId, 16); | ||
| lexe_byte_array::impl_fromstr_fromhex!(LxUserChannelId, 16); | ||
| lexe_byte_array::impl_debug_display_as_hex!(LxUserChannelId); | ||
| lexe_byte_array::impl_byte_array!(UserChannelId, 16); | ||
| lexe_byte_array::impl_fromstr_fromhex!(UserChannelId, 16); | ||
| lexe_byte_array::impl_debug_display_as_hex!(UserChannelId); | ||
| impl From<u128> for LxUserChannelId { | ||
| impl From<u128> for UserChannelId { | ||
| fn from(value: u128) -> Self { | ||
@@ -92,4 +95,4 @@ Self(value.to_le_bytes()) | ||
| impl From<LxUserChannelId> for u128 { | ||
| fn from(value: LxUserChannelId) -> Self { | ||
| impl From<UserChannelId> for u128 { | ||
| fn from(value: UserChannelId) -> Self { | ||
| value.to_u128() | ||
@@ -104,4 +107,4 @@ } | ||
| // --- Basic info --- // | ||
| pub channel_id: LxChannelId, | ||
| pub user_channel_id: LxUserChannelId, | ||
| pub channel_id: ChannelId, | ||
| pub user_channel_id: UserChannelId, | ||
| /// The position of the funding transaction in the chain. | ||
@@ -125,3 +128,3 @@ /// - Used as a short identifier in many places. | ||
| pub outbound_payment_scid: Option<Scid>, | ||
| pub funding_txo: Option<LxOutPoint>, | ||
| pub funding_txo: Option<OutPoint>, | ||
| // Introduced in node-v0.6.16, lsp-v0.6.32 | ||
@@ -272,6 +275,6 @@ pub counterparty_alias: Option<String>, | ||
| let channel_id = LxChannelId::from(channel_id); | ||
| let user_channel_id = LxUserChannelId::from(user_channel_id); | ||
| let channel_id = ChannelId::from(channel_id); | ||
| let user_channel_id = UserChannelId::from(user_channel_id); | ||
| let scid = short_channel_id.map(Scid); | ||
| let funding_txo = funding_txo.map(LxOutPoint::from); | ||
| let funding_txo = funding_txo.map(OutPoint::from); | ||
| let counterparty_node_id = NodePk(counterparty.node_id); | ||
@@ -390,12 +393,16 @@ let channel_value = Amount::try_from_sats_u64(channel_value_satoshis) | ||
| /// A newtype for [`OutPoint`] that provides [`FromStr`] / [`fmt::Display`] | ||
| /// impls. | ||
| /// | ||
| /// Since the persister relies on the string representation to identify | ||
| /// channels, having a newtype (instead of upstreaming these impls to LDK) | ||
| /// ensures that the serialization scheme does not change from beneath us. | ||
| /// A reference to a specific transaction output: the `txid` of the transaction | ||
| /// together with the `index` of the output within that transaction. | ||
| // | ||
| // A newtype for `lightning::chain::transaction::OutPoint` that provides | ||
| // `FromStr` / `fmt::Display` impls. Since the persister relies on the string | ||
| // representation to identify channels, having a newtype (instead of upstreaming | ||
| // these impls to LDK) ensures that the serialization scheme does not change | ||
| // from beneath us. | ||
| // | ||
| // NOTE: This is exposed in the Rust SDK. | ||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] | ||
| #[derive(SerializeDisplay, DeserializeFromStr)] | ||
| #[cfg_attr(any(test, feature = "test-utils"), derive(Arbitrary))] | ||
| pub struct LxOutPoint { | ||
| pub struct OutPoint { | ||
| pub txid: Txid, | ||
@@ -405,4 +412,4 @@ pub index: u16, | ||
| impl From<OutPoint> for LxOutPoint { | ||
| fn from(op: OutPoint) -> Self { | ||
| impl From<lightning::chain::transaction::OutPoint> for OutPoint { | ||
| fn from(op: lightning::chain::transaction::OutPoint) -> Self { | ||
| Self { | ||
@@ -415,4 +422,4 @@ txid: Txid(op.txid), | ||
| impl From<LxOutPoint> for OutPoint { | ||
| fn from(op: LxOutPoint) -> Self { | ||
| impl From<OutPoint> for lightning::chain::transaction::OutPoint { | ||
| fn from(op: OutPoint) -> Self { | ||
| Self { | ||
@@ -425,4 +432,4 @@ txid: op.txid.0, | ||
| impl From<LxOutPoint> for bitcoin::OutPoint { | ||
| fn from(op: LxOutPoint) -> Self { | ||
| impl From<OutPoint> for bitcoin::OutPoint { | ||
| fn from(op: OutPoint) -> Self { | ||
| bitcoin::OutPoint { | ||
@@ -436,3 +443,3 @@ txid: op.txid.0, | ||
| /// Deserializes from `<txid>_<index>` | ||
| impl FromStr for LxOutPoint { | ||
| impl FromStr for OutPoint { | ||
| type Err = anyhow::Error; | ||
@@ -458,3 +465,3 @@ fn from_str(outpoint_str: &str) -> anyhow::Result<Self> { | ||
| /// Serializes to `<txid>_<index>` | ||
| impl fmt::Display for LxOutPoint { | ||
| impl fmt::Display for OutPoint { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
@@ -472,4 +479,4 @@ write!(f, "{}_{}", self.txid, self.index) | ||
| fn outpoint_fromstr_display_roundtrip() { | ||
| roundtrip::fromstr_display_roundtrip_proptest::<LxOutPoint>(); | ||
| roundtrip::fromstr_display_roundtrip_proptest::<OutPoint>(); | ||
| } | ||
| } |
Sorry, the diff of this file is not supported yet