diff --git a/src/builder.rs b/src/builder.rs index 0e8e8c166..65f622dd0 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -568,6 +568,33 @@ impl NodeBuilder { self.build_with_store(node_entropy, kv_store) } + /// Builds a [`Node`] instance with a [VSS] backend and according to the options + /// previously configured. + /// + /// Uses a simple authentication scheme proving knowledge of a secret key and a `store_id` of + /// "ldk-node". + /// + /// `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 + pub fn build_with_vss_store( + &self, node_entropy: NodeEntropy, vss_url: String, fixed_headers: HashMap, + ) -> Result { + let logger = setup_logger(&self.log_writer_config, &self.config)?; + let store_id = "ldk-node".to_owned(); + let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network); + let vss_store = builder.build_with_sigs_auth(fixed_headers).map_err(|e| { + log_error!(logger, "Failed to setup VSS store: {}", e); + BuildError::KVStoreSetupFailed + })?; + + self.build_with_store(node_entropy, vss_store) + } + /// Builds a [`Node`] instance with a [VSS] backend and according to the options /// previously configured. /// @@ -585,13 +612,13 @@ impl NodeBuilder { /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md - pub fn build_with_vss_store( + pub fn build_with_vss_store_and_lnurl_auth( &self, node_entropy: NodeEntropy, vss_url: String, store_id: String, lnurl_auth_server_url: String, fixed_headers: HashMap, ) -> Result { let logger = setup_logger(&self.log_writer_config, &self.config)?; let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network); - let vss_store = builder.build(lnurl_auth_server_url, fixed_headers).map_err(|e| { + let vss_store = builder.build_with_lnurl(lnurl_auth_server_url, fixed_headers).map_err(|e| { log_error!(logger, "Failed to setup VSS store: {}", e); BuildError::KVStoreSetupFailed })?; diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index b4fdc770a..66dd13781 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -30,6 +30,7 @@ use rand::RngCore; use vss_client::client::VssClient; use vss_client::error::VssError; use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider}; +use vss_client::sigs_auth::SigsAuthProvider; use vss_client::types::{ DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest, Storable, @@ -69,6 +70,7 @@ impl_writeable_tlv_based_enum!(VssSchemaVersion, const VSS_HARDENED_CHILD_INDEX: u32 = 877; const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX: u32 = 138; +const VSS_SIGS_AUTH_HARDENED_CHILD_INDEX: u32 = 139; const VSS_SCHEMA_VERSION_KEY: &str = "vss_schema_version"; // We set this to a small number of threads that would still allow to make some progress if one @@ -856,6 +858,44 @@ impl VssStoreBuilder { Self { node_entropy, vss_url, store_id, network } } + /// Builds a [`VssStore`] with the simple signature-based authentication scheme. + /// + /// `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 + pub fn build_with_sigs_auth( + &self, fixed_headers: HashMap, + ) -> Result { + let secp_ctx = Secp256k1::new(); + let seed_bytes = self.node_entropy.to_seed_bytes(); + let vss_xprv = Xpriv::new_master(self.network, &seed_bytes) + .map_err(|_| VssStoreBuildError::KeyDerivationFailed) + .and_then(|master| { + master + .derive_priv( + &secp_ctx, + &[ChildNumber::Hardened { index: VSS_HARDENED_CHILD_INDEX }], + ) + .map_err(|_| VssStoreBuildError::KeyDerivationFailed) + })?; + + let sigs_auth_xprv = vss_xprv + .derive_priv( + &secp_ctx, + &[ChildNumber::Hardened { index: VSS_SIGS_AUTH_HARDENED_CHILD_INDEX }], + ) + .map_err(|_| VssStoreBuildError::KeyDerivationFailed)?; + + let auth_provider = SigsAuthProvider::new(sigs_auth_xprv.private_key, fixed_headers); + self.build_with_header_provider(Arc::new(auth_provider)) + } + /// Builds a [`VssStore`] with [LNURL-auth] based authentication scheme as default method for /// authentication/authorization. /// @@ -872,7 +912,7 @@ impl VssStoreBuilder { /// /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md - pub fn build( + pub fn build_with_lnurl( &self, lnurl_auth_server_url: String, fixed_headers: HashMap, ) -> Result { let secp_ctx = Secp256k1::new(); diff --git a/tests/integration_tests_vss.rs b/tests/integration_tests_vss.rs index 54912b358..24dd9089d 100644 --- a/tests/integration_tests_vss.rs +++ b/tests/integration_tests_vss.rs @@ -25,12 +25,7 @@ async fn channel_full_cycle_with_vss_store() { 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_and_fixed_headers( - config_a.node_entropy, - vss_base_url.clone(), - "node_1_store".to_string(), - HashMap::new(), - ) + .build_with_vss_store(config_a.node_entropy, vss_base_url.clone(), HashMap::new()) .unwrap(); node_a.start().unwrap(); @@ -39,12 +34,7 @@ async fn channel_full_cycle_with_vss_store() { let mut builder_b = Builder::from_config(config_b.node_config); builder_b.set_chain_source_esplora(esplora_url.clone(), None); let node_b = builder_b - .build_with_vss_store_and_fixed_headers( - config_b.node_entropy, - vss_base_url, - "node_2_store".to_string(), - HashMap::new(), - ) + .build_with_vss_store(config_b.node_entropy, vss_base_url, HashMap::new()) .unwrap(); node_b.start().unwrap(); @@ -66,11 +56,9 @@ async fn vss_v0_schema_backwards_compatibility() { let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap()); let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap(); - let rand_suffix: String = - (0..7).map(|_| rng().sample(rand::distr::Alphanumeric) as char).collect(); - let store_id = format!("v0_compat_test_{}", rand_suffix); let storage_path = common::random_storage_path().to_str().unwrap().to_owned(); - let seed_bytes = [42u8; 64]; + let mut seed_bytes = [42u8; 64]; + rand::thread_rng().fill_bytes(&mut seed_bytes); let node_entropy = NodeEntropy::from_seed_bytes(seed_bytes); // Setup a v0.6.2 `Node` persisted with the v0 scheme. @@ -81,11 +69,7 @@ async fn vss_v0_schema_backwards_compatibility() { builder_old.set_entropy_seed_bytes(seed_bytes); builder_old.set_chain_source_esplora(esplora_url.clone(), None); let node_old = builder_old - .build_with_vss_store_and_fixed_headers( - vss_base_url.clone(), - store_id.clone(), - HashMap::new(), - ) + .build_with_vss_store(node_entropy, vss_base_url.clone(), HashMap::new()) .unwrap(); node_old.start().unwrap(); @@ -119,11 +103,9 @@ async fn vss_v0_schema_backwards_compatibility() { builder_new.set_chain_source_esplora(esplora_url, None); let node_new = builder_new - .build_with_vss_store_and_fixed_headers( + .build_with_vss_store( node_entropy, vss_base_url, - store_id, - HashMap::new(), ) .unwrap(); @@ -145,11 +127,9 @@ async fn vss_node_restart() { let esplora_url = format!("http://{}", electrsd.esplora_url.as_ref().unwrap()); let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap(); - let rand_suffix: String = - (0..7).map(|_| rng().sample(rand::distr::Alphanumeric) as char).collect(); - let store_id = format!("restart_test_{}", rand_suffix); let storage_path = common::random_storage_path().to_str().unwrap().to_owned(); - let seed_bytes = [42u8; 64]; + let mut seed_bytes = [42u8; 64]; + rand::thread_rng().fill_bytes(&mut seed_bytes); let node_entropy = NodeEntropy::from_seed_bytes(seed_bytes); // Setup initial node and fund it. @@ -159,12 +139,7 @@ async fn vss_node_restart() { builder.set_storage_dir_path(storage_path.clone()); builder.set_chain_source_esplora(esplora_url.clone(), None); let node = builder - .build_with_vss_store_and_fixed_headers( - node_entropy, - vss_base_url.clone(), - store_id.clone(), - HashMap::new(), - ) + .build_with_vss_store(node_entropy, vss_base_url.clone(), HashMap::new()) .unwrap(); node.start().unwrap(); @@ -192,14 +167,7 @@ async fn vss_node_restart() { builder.set_storage_dir_path(storage_path); builder.set_chain_source_esplora(esplora_url, None); - let node = builder - .build_with_vss_store_and_fixed_headers( - node_entropy, - vss_base_url, - store_id, - HashMap::new(), - ) - .unwrap(); + let node = builder.build_with_vss_store(node_entropy, vss_base_url, HashMap::new()).unwrap(); node.start().unwrap(); node.sync_wallets().unwrap();