From 2646629f76c6fbe5791e6e26b6f48c0b44f75c72 Mon Sep 17 00:00:00 2001 From: G8XSU <3442979+G8XSU@users.noreply.github.com> Date: Thu, 10 Oct 2024 02:17:57 -0700 Subject: [PATCH 1/3] Enable using VssStore with VssHeaderProvider. --- src/builder.rs | 73 ++++++++++++++++++++++++++-------- src/io/vss_store.rs | 15 +++++-- src/lib.rs | 2 + tests/integration_tests_vss.rs | 18 +++++++-- 4 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index 43171db1f..1a6410241 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -65,7 +65,9 @@ use bitcoin::secp256k1::PublicKey; use bitcoin::{BlockHash, Network}; #[cfg(any(vss, vss_test))] -use bitcoin::bip32::ChildNumber; +use bitcoin::bip32::{ChildNumber, Xpriv}; +#[cfg(any(vss, vss_test))] +use std::collections::HashMap; use std::convert::TryInto; use std::default::Default; use std::fmt; @@ -74,6 +76,8 @@ use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex, RwLock}; use std::time::SystemTime; +#[cfg(any(vss, vss_test))] +use vss_client::headers::{FixedHeaders, VssHeaderProvider}; #[derive(Debug, Clone)] enum ChainDataSourceConfig { @@ -357,12 +361,41 @@ impl NodeBuilder { self.build_with_store(kv_store) } - /// Builds a [`Node`] instance with a [`VssStore`] backend and according to the options + /// Builds a [`Node`] instance with a [VSS] backend and according to the options /// previously configured. + /// + /// Uses [`FixedHeaders`] as default method for authentication/authorization. + /// Given `fixed_headers` are included as it is in all the requests made to VSS. + /// + /// **Caution**: VSS support is in **alpha** and is considered experimental. + /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are + /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. + /// + /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md #[cfg(any(vss, vss_test))] - pub fn build_with_vss_store(&self, url: String, store_id: String) -> Result { - use bitcoin::key::Secp256k1; + pub fn build_with_vss_store_and_fixed_headers( + &self, vss_url: String, store_id: String, fixed_headers: HashMap, + ) -> Result { + let header_provider = Arc::new(FixedHeaders::new(fixed_headers)); + + self.build_with_vss_store_and_header_provider(vss_url, store_id, header_provider) + } + /// Builds a [`Node`] instance with a [VSS] backend and according to the options + /// previously configured. + /// + /// Given `header_provider` is used to attach headers to every request made + /// to VSS. + /// + /// **Caution**: VSS support is in **alpha** and is considered experimental. + /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are + /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. + /// + /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md + #[cfg(any(vss, vss_test))] + pub fn build_with_vss_store_and_header_provider( + &self, vss_url: String, store_id: String, header_provider: Arc, + ) -> Result { let logger = setup_logger(&self.config)?; let seed_bytes = seed_bytes_from_config( @@ -370,23 +403,14 @@ impl NodeBuilder { self.entropy_source_config.as_ref(), Arc::clone(&logger), )?; - let config = Arc::new(self.config.clone()); - let xprv = bitcoin::bip32::Xpriv::new_master(config.network, &seed_bytes).map_err(|e| { - log_error!(logger, "Failed to derive master secret: {}", e); - BuildError::InvalidSeedBytes - })?; + let config = Arc::new(self.config.clone()); - let vss_xprv = xprv - .derive_priv(&Secp256k1::new(), &[ChildNumber::Hardened { index: 877 }]) - .map_err(|e| { - log_error!(logger, "Failed to derive VSS secret: {}", e); - BuildError::KVStoreSetupFailed - })?; + let vss_xprv = derive_vss_xprv(config.clone(), &seed_bytes, Arc::clone(&logger))?; let vss_seed_bytes: [u8; 32] = vss_xprv.private_key.secret_bytes(); - let vss_store = Arc::new(VssStore::new(url, store_id, vss_seed_bytes)); + let vss_store = Arc::new(VssStore::new(vss_url, store_id, vss_seed_bytes, header_provider)); build_with_store_internal( config, self.chain_data_source_config.as_ref(), @@ -1079,6 +1103,23 @@ fn seed_bytes_from_config( } } +#[cfg(any(vss, vss_test))] +fn derive_vss_xprv( + config: Arc, seed_bytes: &[u8; 64], logger: Arc, +) -> Result { + use bitcoin::key::Secp256k1; + + let xprv = Xpriv::new_master(config.network, seed_bytes).map_err(|e| { + log_error!(logger, "Failed to derive master secret: {}", e); + BuildError::InvalidSeedBytes + })?; + + xprv.derive_priv(&Secp256k1::new(), &[ChildNumber::Hardened { index: 877 }]).map_err(|e| { + log_error!(logger, "Failed to derive VSS secret: {}", e); + BuildError::KVStoreSetupFailed + }) +} + /// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string. pub(crate) fn sanitize_alias(alias_str: &str) -> Result { let alias = alias_str.trim(); diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index 474f7dbc7..fbed7f7cf 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -8,6 +8,7 @@ use lightning::io::{self, Error, ErrorKind}; #[cfg(test)] use std::panic::RefUnwindSafe; +use std::sync::Arc; use std::time::Duration; use crate::io::utils::check_namespace_key_validity; @@ -17,6 +18,7 @@ use rand::RngCore; use tokio::runtime::Runtime; use vss_client::client::VssClient; use vss_client::error::VssError; +use vss_client::headers::VssHeaderProvider; use vss_client::types::{ DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest, Storable, @@ -43,7 +45,10 @@ pub struct VssStore { } impl VssStore { - pub(crate) fn new(base_url: String, store_id: String, data_encryption_key: [u8; 32]) -> Self { + pub(crate) fn new( + base_url: String, store_id: String, data_encryption_key: [u8; 32], + header_provider: Arc, + ) -> Self { let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap(); let storable_builder = StorableBuilder::new(data_encryption_key, RandEntropySource); let retry_policy = ExponentialBackoffRetryPolicy::new(Duration::from_millis(100)) @@ -59,7 +64,7 @@ impl VssStore { ) }) as _); - let client = VssClient::new(base_url, retry_policy); + let client = VssClient::new_with_headers(base_url, retry_policy, header_provider); Self { client, store_id, runtime, storable_builder } } @@ -238,6 +243,8 @@ mod tests { use crate::io::test_utils::do_read_write_remove_list_persist; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng, RngCore}; + use std::collections::HashMap; + use vss_client::headers::FixedHeaders; #[test] fn read_write_remove_list_persist() { @@ -246,7 +253,9 @@ mod tests { let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect(); let mut data_encryption_key = [0u8; 32]; rng.fill_bytes(&mut data_encryption_key); - let vss_store = VssStore::new(vss_base_url, rand_store_id, data_encryption_key); + let header_provider = Arc::new(FixedHeaders::new(HashMap::new())); + let vss_store = + VssStore::new(vss_base_url, rand_store_id, data_encryption_key, header_provider); do_read_write_remove_list_persist(&vss_store); } diff --git a/src/lib.rs b/src/lib.rs index 42b99406a..8213712b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,8 @@ pub use bip39; pub use bitcoin; pub use lightning; pub use lightning_invoice; +#[cfg(any(vss, vss_test))] +pub use vss_client; pub use balance::{BalanceDetails, LightningBalance, PendingSweepBalance}; pub use error::Error as NodeError; diff --git a/tests/integration_tests_vss.rs b/tests/integration_tests_vss.rs index 483902375..525c1f1f1 100644 --- a/tests/integration_tests_vss.rs +++ b/tests/integration_tests_vss.rs @@ -10,6 +10,7 @@ mod common; use ldk_node::Builder; +use std::collections::HashMap; #[test] fn channel_full_cycle_with_vss_store() { @@ -20,15 +21,26 @@ fn channel_full_cycle_with_vss_store() { let mut builder_a = Builder::from_config(config_a); builder_a.set_chain_source_esplora(esplora_url.clone(), None); let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap(); - let node_a = - builder_a.build_with_vss_store(vss_base_url.clone(), "node_1_store".to_string()).unwrap(); + let node_a = builder_a + .build_with_vss_store_and_fixed_headers( + vss_base_url.clone(), + "node_1_store".to_string(), + HashMap::new(), + ) + .unwrap(); node_a.start().unwrap(); println!("\n== Node B =="); let config_b = common::random_config(true); let mut builder_b = Builder::from_config(config_b); builder_b.set_chain_source_esplora(esplora_url.clone(), None); - let node_b = builder_b.build_with_vss_store(vss_base_url, "node_2_store".to_string()).unwrap(); + let node_b = builder_b + .build_with_vss_store_and_fixed_headers( + vss_base_url, + "node_2_store".to_string(), + HashMap::new(), + ) + .unwrap(); node_b.start().unwrap(); common::do_channel_full_cycle( From ffea164662187218a4f0dc2e5ff71c343485ccd7 Mon Sep 17 00:00:00 2001 From: G8XSU <3442979+G8XSU@users.noreply.github.com> Date: Wed, 16 Oct 2024 15:24:54 -0700 Subject: [PATCH 2/3] Default to LnUrlJWT auth for using VSS. build_with_vss_store now uses LNURL-Auth as the default method for authentication/authorization. --- src/builder.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/builder.rs b/src/builder.rs index 1a6410241..a4b9a6eab 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -77,7 +77,7 @@ use std::sync::atomic::AtomicBool; use std::sync::{Arc, Mutex, RwLock}; use std::time::SystemTime; #[cfg(any(vss, vss_test))] -use vss_client::headers::{FixedHeaders, VssHeaderProvider}; +use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider}; #[derive(Debug, Clone)] enum ChainDataSourceConfig { @@ -361,10 +361,66 @@ impl NodeBuilder { self.build_with_store(kv_store) } + /// Builds a [`Node`] instance with a [VSS] backend and according to the options + /// previously configured. + /// + /// Uses [LNURL-auth] based authentication scheme as default method for authentication/authorization. + /// + /// The LNURL challenge will be retrieved by making a request to the given `lnurl_auth_server_url`. + /// The returned JWT token in response to the signed LNURL request, will be used for + /// authentication/authorization of all the requests made to VSS. + /// + /// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server. + /// + /// **Caution**: VSS support is in **alpha** and is considered experimental. + /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are + /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. + /// + /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md + /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md + #[cfg(any(vss, vss_test))] + pub fn build_with_vss_store( + &self, vss_url: String, store_id: String, lnurl_auth_server_url: String, + fixed_headers: HashMap, + ) -> Result { + use bitcoin::key::Secp256k1; + + let logger = setup_logger(&self.config)?; + + let seed_bytes = seed_bytes_from_config( + &self.config, + self.entropy_source_config.as_ref(), + Arc::clone(&logger), + )?; + + let config = Arc::new(self.config.clone()); + + let vss_xprv = derive_vss_xprv(config, &seed_bytes, Arc::clone(&logger))?; + + let lnurl_auth_xprv = vss_xprv + .derive_priv(&Secp256k1::new(), &[ChildNumber::Hardened { index: 138 }]) + .map_err(|e| { + log_error!(logger, "Failed to derive VSS secret: {}", e); + BuildError::KVStoreSetupFailed + })?; + + let lnurl_auth_jwt_provider = + LnurlAuthToJwtProvider::new(lnurl_auth_xprv, lnurl_auth_server_url, fixed_headers) + .map_err(|e| { + log_error!(logger, "Failed to create LnurlAuthToJwtProvider: {}", e); + BuildError::KVStoreSetupFailed + })?; + + let header_provider = Arc::new(lnurl_auth_jwt_provider); + + self.build_with_vss_store_and_header_provider(vss_url, store_id, header_provider) + } + /// Builds a [`Node`] instance with a [VSS] backend and according to the options /// previously configured. /// /// Uses [`FixedHeaders`] as default method for authentication/authorization. + /// /// Given `fixed_headers` are included as it is in all the requests made to VSS. /// /// **Caution**: VSS support is in **alpha** and is considered experimental. From cf91516000cd897baa175a039e8c96a8be1e79b9 Mon Sep 17 00:00:00 2001 From: G8XSU <3442979+G8XSU@users.noreply.github.com> Date: Tue, 15 Oct 2024 00:00:30 -0700 Subject: [PATCH 3/3] Add build_with_vss* methods for ArcedNodeBuilder. --- src/builder.rs | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/builder.rs b/src/builder.rs index a4b9a6eab..4fa8f53f5 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -631,6 +631,80 @@ impl ArcedNodeBuilder { self.inner.read().unwrap().build_with_fs_store().map(Arc::new) } + /// Builds a [`Node`] instance with a [VSS] backend and according to the options + /// previously configured. + /// + /// Uses [LNURL-auth] based authentication scheme as default method for authentication/authorization. + /// + /// The LNURL challenge will be retrieved by making a request to the given `lnurl_auth_server_url`. + /// The returned JWT token in response to the signed LNURL request, will be used for + /// authentication/authorization of all the requests made to VSS. + /// + /// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server. + /// + /// **Caution**: VSS support is in **alpha** and is considered experimental. + /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are + /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. + /// + /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md + /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md + #[cfg(any(vss, vss_test))] + pub fn build_with_vss_store( + &self, vss_url: String, store_id: String, lnurl_auth_server_url: String, + fixed_headers: HashMap, + ) -> Result, BuildError> { + self.inner + .read() + .unwrap() + .build_with_vss_store(vss_url, store_id, lnurl_auth_server_url, fixed_headers) + .map(Arc::new) + } + + /// Builds a [`Node`] instance with a [VSS] backend and according to the options + /// previously configured. + /// + /// Uses [`FixedHeaders`] as default method for authentication/authorization. + /// + /// Given `fixed_headers` are included as it is in all the requests made to VSS. + /// + /// **Caution**: VSS support is in **alpha** and is considered experimental. + /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are + /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. + /// + /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md + #[cfg(any(vss, vss_test))] + pub fn build_with_vss_store_and_fixed_headers( + &self, vss_url: String, store_id: String, fixed_headers: HashMap, + ) -> Result, BuildError> { + self.inner + .read() + .unwrap() + .build_with_vss_store_and_fixed_headers(vss_url, store_id, fixed_headers) + .map(Arc::new) + } + + /// Builds a [`Node`] instance with a [VSS] backend and according to the options + /// previously configured. + /// + /// Given `header_provider` is used to attach headers to every request made + /// to VSS. + /// + /// **Caution**: VSS support is in **alpha** and is considered experimental. + /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are + /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted. + /// + /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md + #[cfg(any(vss, vss_test))] + pub fn build_with_vss_store_and_header_provider( + &self, vss_url: String, store_id: String, header_provider: Arc, + ) -> Result, BuildError> { + self.inner + .read() + .unwrap() + .build_with_vss_store_and_header_provider(vss_url, store_id, header_provider) + .map(Arc::new) + } + /// Builds a [`Node`] instance according to the options previously configured. pub fn build_with_store(&self, kv_store: Arc) -> Result, BuildError> { self.inner.read().unwrap().build_with_store(kv_store).map(Arc::new)