Skip to content

Commit 9ba95be

Browse files
authored
Merge pull request #369 from G8XSU/vss-lnauth
Enable using VssStore with VssHeaderProvider.
2 parents 137bfff + cf91516 commit 9ba95be

File tree

4 files changed

+210
-16
lines changed

4 files changed

+210
-16
lines changed

src/builder.rs

Lines changed: 181 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ use bitcoin::secp256k1::PublicKey;
6565
use bitcoin::{BlockHash, Network};
6666

6767
#[cfg(any(vss, vss_test))]
68-
use bitcoin::bip32::ChildNumber;
68+
use bitcoin::bip32::{ChildNumber, Xpriv};
69+
#[cfg(any(vss, vss_test))]
70+
use std::collections::HashMap;
6971
use std::convert::TryInto;
7072
use std::default::Default;
7173
use std::fmt;
@@ -74,6 +76,8 @@ use std::path::PathBuf;
7476
use std::sync::atomic::AtomicBool;
7577
use std::sync::{Arc, Mutex, RwLock};
7678
use std::time::SystemTime;
79+
#[cfg(any(vss, vss_test))]
80+
use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider};
7781

7882
#[derive(Debug, Clone)]
7983
enum ChainDataSourceConfig {
@@ -368,10 +372,28 @@ impl NodeBuilder {
368372
self.build_with_store(kv_store)
369373
}
370374

371-
/// Builds a [`Node`] instance with a [`VssStore`] backend and according to the options
375+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
372376
/// previously configured.
377+
///
378+
/// Uses [LNURL-auth] based authentication scheme as default method for authentication/authorization.
379+
///
380+
/// The LNURL challenge will be retrieved by making a request to the given `lnurl_auth_server_url`.
381+
/// The returned JWT token in response to the signed LNURL request, will be used for
382+
/// authentication/authorization of all the requests made to VSS.
383+
///
384+
/// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server.
385+
///
386+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
387+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
388+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
389+
///
390+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
391+
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
373392
#[cfg(any(vss, vss_test))]
374-
pub fn build_with_vss_store(&self, url: String, store_id: String) -> Result<Node, BuildError> {
393+
pub fn build_with_vss_store(
394+
&self, vss_url: String, store_id: String, lnurl_auth_server_url: String,
395+
fixed_headers: HashMap<String, String>,
396+
) -> Result<Node, BuildError> {
375397
use bitcoin::key::Secp256k1;
376398

377399
let logger = setup_logger(&self.config)?;
@@ -381,23 +403,81 @@ impl NodeBuilder {
381403
self.entropy_source_config.as_ref(),
382404
Arc::clone(&logger),
383405
)?;
406+
384407
let config = Arc::new(self.config.clone());
385408

386-
let xprv = bitcoin::bip32::Xpriv::new_master(config.network, &seed_bytes).map_err(|e| {
387-
log_error!(logger, "Failed to derive master secret: {}", e);
388-
BuildError::InvalidSeedBytes
389-
})?;
409+
let vss_xprv = derive_vss_xprv(config, &seed_bytes, Arc::clone(&logger))?;
390410

391-
let vss_xprv = xprv
392-
.derive_priv(&Secp256k1::new(), &[ChildNumber::Hardened { index: 877 }])
411+
let lnurl_auth_xprv = vss_xprv
412+
.derive_priv(&Secp256k1::new(), &[ChildNumber::Hardened { index: 138 }])
393413
.map_err(|e| {
394414
log_error!(logger, "Failed to derive VSS secret: {}", e);
395415
BuildError::KVStoreSetupFailed
396416
})?;
397417

418+
let lnurl_auth_jwt_provider =
419+
LnurlAuthToJwtProvider::new(lnurl_auth_xprv, lnurl_auth_server_url, fixed_headers)
420+
.map_err(|e| {
421+
log_error!(logger, "Failed to create LnurlAuthToJwtProvider: {}", e);
422+
BuildError::KVStoreSetupFailed
423+
})?;
424+
425+
let header_provider = Arc::new(lnurl_auth_jwt_provider);
426+
427+
self.build_with_vss_store_and_header_provider(vss_url, store_id, header_provider)
428+
}
429+
430+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
431+
/// previously configured.
432+
///
433+
/// Uses [`FixedHeaders`] as default method for authentication/authorization.
434+
///
435+
/// Given `fixed_headers` are included as it is in all the requests made to VSS.
436+
///
437+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
438+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
439+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
440+
///
441+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
442+
#[cfg(any(vss, vss_test))]
443+
pub fn build_with_vss_store_and_fixed_headers(
444+
&self, vss_url: String, store_id: String, fixed_headers: HashMap<String, String>,
445+
) -> Result<Node, BuildError> {
446+
let header_provider = Arc::new(FixedHeaders::new(fixed_headers));
447+
448+
self.build_with_vss_store_and_header_provider(vss_url, store_id, header_provider)
449+
}
450+
451+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
452+
/// previously configured.
453+
///
454+
/// Given `header_provider` is used to attach headers to every request made
455+
/// to VSS.
456+
///
457+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
458+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
459+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
460+
///
461+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
462+
#[cfg(any(vss, vss_test))]
463+
pub fn build_with_vss_store_and_header_provider(
464+
&self, vss_url: String, store_id: String, header_provider: Arc<dyn VssHeaderProvider>,
465+
) -> Result<Node, BuildError> {
466+
let logger = setup_logger(&self.config)?;
467+
468+
let seed_bytes = seed_bytes_from_config(
469+
&self.config,
470+
self.entropy_source_config.as_ref(),
471+
Arc::clone(&logger),
472+
)?;
473+
474+
let config = Arc::new(self.config.clone());
475+
476+
let vss_xprv = derive_vss_xprv(config.clone(), &seed_bytes, Arc::clone(&logger))?;
477+
398478
let vss_seed_bytes: [u8; 32] = vss_xprv.private_key.secret_bytes();
399479

400-
let vss_store = Arc::new(VssStore::new(url, store_id, vss_seed_bytes));
480+
let vss_store = Arc::new(VssStore::new(vss_url, store_id, vss_seed_bytes, header_provider));
401481
build_with_store_internal(
402482
config,
403483
self.chain_data_source_config.as_ref(),
@@ -575,6 +655,80 @@ impl ArcedNodeBuilder {
575655
self.inner.read().unwrap().build_with_fs_store().map(Arc::new)
576656
}
577657

658+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
659+
/// previously configured.
660+
///
661+
/// Uses [LNURL-auth] based authentication scheme as default method for authentication/authorization.
662+
///
663+
/// The LNURL challenge will be retrieved by making a request to the given `lnurl_auth_server_url`.
664+
/// The returned JWT token in response to the signed LNURL request, will be used for
665+
/// authentication/authorization of all the requests made to VSS.
666+
///
667+
/// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server.
668+
///
669+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
670+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
671+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
672+
///
673+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
674+
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
675+
#[cfg(any(vss, vss_test))]
676+
pub fn build_with_vss_store(
677+
&self, vss_url: String, store_id: String, lnurl_auth_server_url: String,
678+
fixed_headers: HashMap<String, String>,
679+
) -> Result<Arc<Node>, BuildError> {
680+
self.inner
681+
.read()
682+
.unwrap()
683+
.build_with_vss_store(vss_url, store_id, lnurl_auth_server_url, fixed_headers)
684+
.map(Arc::new)
685+
}
686+
687+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
688+
/// previously configured.
689+
///
690+
/// Uses [`FixedHeaders`] as default method for authentication/authorization.
691+
///
692+
/// Given `fixed_headers` are included as it is in all the requests made to VSS.
693+
///
694+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
695+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
696+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
697+
///
698+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
699+
#[cfg(any(vss, vss_test))]
700+
pub fn build_with_vss_store_and_fixed_headers(
701+
&self, vss_url: String, store_id: String, fixed_headers: HashMap<String, String>,
702+
) -> Result<Arc<Node>, BuildError> {
703+
self.inner
704+
.read()
705+
.unwrap()
706+
.build_with_vss_store_and_fixed_headers(vss_url, store_id, fixed_headers)
707+
.map(Arc::new)
708+
}
709+
710+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
711+
/// previously configured.
712+
///
713+
/// Given `header_provider` is used to attach headers to every request made
714+
/// to VSS.
715+
///
716+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
717+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
718+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
719+
///
720+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
721+
#[cfg(any(vss, vss_test))]
722+
pub fn build_with_vss_store_and_header_provider(
723+
&self, vss_url: String, store_id: String, header_provider: Arc<dyn VssHeaderProvider>,
724+
) -> Result<Arc<Node>, BuildError> {
725+
self.inner
726+
.read()
727+
.unwrap()
728+
.build_with_vss_store_and_header_provider(vss_url, store_id, header_provider)
729+
.map(Arc::new)
730+
}
731+
578732
/// Builds a [`Node`] instance according to the options previously configured.
579733
pub fn build_with_store(&self, kv_store: Arc<DynStore>) -> Result<Arc<Node>, BuildError> {
580734
self.inner.read().unwrap().build_with_store(kv_store).map(Arc::new)
@@ -1118,6 +1272,23 @@ fn seed_bytes_from_config(
11181272
}
11191273
}
11201274

1275+
#[cfg(any(vss, vss_test))]
1276+
fn derive_vss_xprv(
1277+
config: Arc<Config>, seed_bytes: &[u8; 64], logger: Arc<FilesystemLogger>,
1278+
) -> Result<Xpriv, BuildError> {
1279+
use bitcoin::key::Secp256k1;
1280+
1281+
let xprv = Xpriv::new_master(config.network, seed_bytes).map_err(|e| {
1282+
log_error!(logger, "Failed to derive master secret: {}", e);
1283+
BuildError::InvalidSeedBytes
1284+
})?;
1285+
1286+
xprv.derive_priv(&Secp256k1::new(), &[ChildNumber::Hardened { index: 877 }]).map_err(|e| {
1287+
log_error!(logger, "Failed to derive VSS secret: {}", e);
1288+
BuildError::KVStoreSetupFailed
1289+
})
1290+
}
1291+
11211292
/// Sanitize the user-provided node alias to ensure that it is a valid protocol-specified UTF-8 string.
11221293
pub(crate) fn sanitize_alias(alias_str: &str) -> Result<NodeAlias, BuildError> {
11231294
let alias = alias_str.trim();

src/io/vss_store.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use lightning::io::{self, Error, ErrorKind};
99
#[cfg(test)]
1010
use std::panic::RefUnwindSafe;
11+
use std::sync::Arc;
1112
use std::time::Duration;
1213

1314
use crate::io::utils::check_namespace_key_validity;
@@ -17,6 +18,7 @@ use rand::RngCore;
1718
use tokio::runtime::Runtime;
1819
use vss_client::client::VssClient;
1920
use vss_client::error::VssError;
21+
use vss_client::headers::VssHeaderProvider;
2022
use vss_client::types::{
2123
DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest,
2224
Storable,
@@ -43,7 +45,10 @@ pub struct VssStore {
4345
}
4446

4547
impl VssStore {
46-
pub(crate) fn new(base_url: String, store_id: String, data_encryption_key: [u8; 32]) -> Self {
48+
pub(crate) fn new(
49+
base_url: String, store_id: String, data_encryption_key: [u8; 32],
50+
header_provider: Arc<dyn VssHeaderProvider>,
51+
) -> Self {
4752
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
4853
let storable_builder = StorableBuilder::new(data_encryption_key, RandEntropySource);
4954
let retry_policy = ExponentialBackoffRetryPolicy::new(Duration::from_millis(10))
@@ -59,7 +64,7 @@ impl VssStore {
5964
)
6065
}) as _);
6166

62-
let client = VssClient::new(base_url, retry_policy);
67+
let client = VssClient::new_with_headers(base_url, retry_policy, header_provider);
6368
Self { client, store_id, runtime, storable_builder }
6469
}
6570

@@ -238,6 +243,8 @@ mod tests {
238243
use crate::io::test_utils::do_read_write_remove_list_persist;
239244
use rand::distributions::Alphanumeric;
240245
use rand::{thread_rng, Rng, RngCore};
246+
use std::collections::HashMap;
247+
use vss_client::headers::FixedHeaders;
241248

242249
#[test]
243250
fn read_write_remove_list_persist() {
@@ -246,7 +253,9 @@ mod tests {
246253
let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
247254
let mut data_encryption_key = [0u8; 32];
248255
rng.fill_bytes(&mut data_encryption_key);
249-
let vss_store = VssStore::new(vss_base_url, rand_store_id, data_encryption_key);
256+
let header_provider = Arc::new(FixedHeaders::new(HashMap::new()));
257+
let vss_store =
258+
VssStore::new(vss_base_url, rand_store_id, data_encryption_key, header_provider);
250259

251260
do_read_write_remove_list_persist(&vss_store);
252261
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ pub use bip39;
100100
pub use bitcoin;
101101
pub use lightning;
102102
pub use lightning_invoice;
103+
#[cfg(any(vss, vss_test))]
104+
pub use vss_client;
103105

104106
pub use balance::{BalanceDetails, LightningBalance, PendingSweepBalance};
105107
pub use error::Error as NodeError;

tests/integration_tests_vss.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
mod common;
1111

1212
use ldk_node::Builder;
13+
use std::collections::HashMap;
1314

1415
#[test]
1516
fn channel_full_cycle_with_vss_store() {
@@ -20,15 +21,26 @@ fn channel_full_cycle_with_vss_store() {
2021
let mut builder_a = Builder::from_config(config_a);
2122
builder_a.set_chain_source_esplora(esplora_url.clone(), None);
2223
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
23-
let node_a =
24-
builder_a.build_with_vss_store(vss_base_url.clone(), "node_1_store".to_string()).unwrap();
24+
let node_a = builder_a
25+
.build_with_vss_store_and_fixed_headers(
26+
vss_base_url.clone(),
27+
"node_1_store".to_string(),
28+
HashMap::new(),
29+
)
30+
.unwrap();
2531
node_a.start().unwrap();
2632

2733
println!("\n== Node B ==");
2834
let config_b = common::random_config(true);
2935
let mut builder_b = Builder::from_config(config_b);
3036
builder_b.set_chain_source_esplora(esplora_url.clone(), None);
31-
let node_b = builder_b.build_with_vss_store(vss_base_url, "node_2_store".to_string()).unwrap();
37+
let node_b = builder_b
38+
.build_with_vss_store_and_fixed_headers(
39+
vss_base_url,
40+
"node_2_store".to_string(),
41+
HashMap::new(),
42+
)
43+
.unwrap();
3244
node_b.start().unwrap();
3345

3446
common::do_channel_full_cycle(

0 commit comments

Comments
 (0)