Skip to content

Commit e3d3318

Browse files
authored
Merge pull request #755 from TheBlueMatt/2026-01-vss-sigs-auth
Add support for the simple "sigs-based auth" VSS scheme
2 parents a8b956a + 01a232e commit e3d3318

File tree

6 files changed

+286
-114
lines changed

6 files changed

+286
-114
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CI Checks - VSS No-Auth Integration Tests
2+
3+
on: [push, pull_request]
4+
5+
concurrency:
6+
group: ${{ github.workflow }}-${{ github.ref }}
7+
cancel-in-progress: true
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: ubuntu-latest
12+
13+
services:
14+
postgres:
15+
image: postgres:latest
16+
ports:
17+
- 5432:5432
18+
env:
19+
POSTGRES_DB: postgres
20+
POSTGRES_USER: postgres
21+
POSTGRES_PASSWORD: postgres
22+
options: >-
23+
--health-cmd pg_isready
24+
--health-interval 10s
25+
--health-timeout 5s
26+
--health-retries 5
27+
28+
steps:
29+
- name: Checkout code
30+
uses: actions/checkout@v3
31+
with:
32+
path: ldk-node
33+
- name: Checkout VSS
34+
uses: actions/checkout@v3
35+
with:
36+
repository: lightningdevkit/vss-server
37+
path: vss-server
38+
39+
- name: Build and Deploy VSS Server
40+
run: |
41+
cd vss-server/rust
42+
RUSTFLAGS=--cfg=noop_authorizer cargo run --no-default-features server/vss-server-config.toml&
43+
- name: Run VSS Integration tests
44+
run: |
45+
cd ldk-node
46+
export TEST_VSS_BASE_URL="http://localhost:8080/vss"
47+
RUSTFLAGS="--cfg vss_test --cfg cycle_tests" cargo test --test integration_tests_vss_no_auth

bindings/ldk_node.udl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ interface Builder {
6262
[Throws=BuildError]
6363
Node build_with_fs_store(NodeEntropy node_entropy);
6464
[Throws=BuildError]
65-
Node build_with_vss_store(NodeEntropy node_entropy, string vss_url, string store_id, string lnurl_auth_server_url, record<string, string> fixed_headers);
65+
Node build_with_vss_store(NodeEntropy node_entropy, string vss_url, string store_id, record<string, string> fixed_headers);
66+
[Throws=BuildError]
67+
Node build_with_vss_store_and_lnurl_auth(NodeEntropy node_entropy, string vss_url, string store_id, string lnurl_auth_server_url, record<string, string> fixed_headers);
6668
[Throws=BuildError]
6769
Node build_with_vss_store_and_fixed_headers(NodeEntropy node_entropy, string vss_url, string store_id, record<string, string> fixed_headers);
6870
[Throws=BuildError]

src/builder.rs

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,37 @@ impl NodeBuilder {
586586
self.build_with_store(node_entropy, kv_store)
587587
}
588588

589+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
590+
/// previously configured.
591+
///
592+
/// Uses a simple authentication scheme proving knowledge of a secret key.
593+
///
594+
/// `fixed_headers` are included as it is in all the requests made to VSS.
595+
///
596+
/// `store_id` allows you to segment LDK Node storage from other storage accessed with
597+
/// [`VssStoreBuilder`] using the same [`NodeEntropy`] (as storage with different keys is
598+
/// obviously segmented to prevent wallets from reading data for unrelated wallets). It can be
599+
/// any value.
600+
///
601+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
602+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
603+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
604+
///
605+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
606+
pub fn build_with_vss_store(
607+
&self, node_entropy: NodeEntropy, vss_url: String, store_id: String,
608+
fixed_headers: HashMap<String, String>,
609+
) -> Result<Node, BuildError> {
610+
let logger = setup_logger(&self.log_writer_config, &self.config)?;
611+
let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network);
612+
let vss_store = builder.build_with_sigs_auth(fixed_headers).map_err(|e| {
613+
log_error!(logger, "Failed to setup VSS store: {}", e);
614+
BuildError::KVStoreSetupFailed
615+
})?;
616+
617+
self.build_with_store(node_entropy, vss_store)
618+
}
619+
589620
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
590621
/// previously configured.
591622
///
@@ -595,6 +626,11 @@ impl NodeBuilder {
595626
/// The returned JWT token in response to the signed LNURL request, will be used for
596627
/// authentication/authorization of all the requests made to VSS.
597628
///
629+
/// `store_id` allows you to segment LDK Node storage from other storage accessed with
630+
/// [`VssStoreBuilder`] using the same authentication (as storage with different keys is
631+
/// obviously segmented to prevent wallets from reading data for unrelated wallets). It can be
632+
/// any value.
633+
///
598634
/// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server.
599635
///
600636
/// **Caution**: VSS support is in **alpha** and is considered experimental.
@@ -603,16 +639,17 @@ impl NodeBuilder {
603639
///
604640
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
605641
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
606-
pub fn build_with_vss_store(
642+
pub fn build_with_vss_store_and_lnurl_auth(
607643
&self, node_entropy: NodeEntropy, vss_url: String, store_id: String,
608644
lnurl_auth_server_url: String, fixed_headers: HashMap<String, String>,
609645
) -> Result<Node, BuildError> {
610646
let logger = setup_logger(&self.log_writer_config, &self.config)?;
611647
let builder = VssStoreBuilder::new(node_entropy, vss_url, store_id, self.config.network);
612-
let vss_store = builder.build(lnurl_auth_server_url, fixed_headers).map_err(|e| {
613-
log_error!(logger, "Failed to setup VSS store: {}", e);
614-
BuildError::KVStoreSetupFailed
615-
})?;
648+
let vss_store =
649+
builder.build_with_lnurl(lnurl_auth_server_url, fixed_headers).map_err(|e| {
650+
log_error!(logger, "Failed to setup VSS store: {}", e);
651+
BuildError::KVStoreSetupFailed
652+
})?;
616653

617654
self.build_with_store(node_entropy, vss_store)
618655
}
@@ -958,6 +995,34 @@ impl ArcedNodeBuilder {
958995
self.inner.read().unwrap().build_with_fs_store(*node_entropy).map(Arc::new)
959996
}
960997

998+
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
999+
/// previously configured.
1000+
///
1001+
/// Uses a simple authentication scheme proving knowledge of a secret key.
1002+
///
1003+
/// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server.
1004+
///
1005+
/// `store_id` allows you to segment LDK Node storage from other storage accessed with
1006+
/// [`VssStoreBuilder`] using the same [`NodeEntropy`] (as storage with different keys is
1007+
/// obviously segmented to prevent wallets from reading data for unrelated wallets). It can be
1008+
/// any value.
1009+
///
1010+
/// **Caution**: VSS support is in **alpha** and is considered experimental.
1011+
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
1012+
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
1013+
///
1014+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
1015+
pub fn build_with_vss_store(
1016+
&self, node_entropy: Arc<NodeEntropy>, vss_url: String, store_id: String,
1017+
fixed_headers: HashMap<String, String>,
1018+
) -> Result<Arc<Node>, BuildError> {
1019+
self.inner
1020+
.read()
1021+
.unwrap()
1022+
.build_with_vss_store(*node_entropy, vss_url, store_id, fixed_headers)
1023+
.map(Arc::new)
1024+
}
1025+
9611026
/// Builds a [`Node`] instance with a [VSS] backend and according to the options
9621027
/// previously configured.
9631028
///
@@ -969,20 +1034,25 @@ impl ArcedNodeBuilder {
9691034
///
9701035
/// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth server.
9711036
///
1037+
/// `store_id` allows you to segment LDK Node storage from other storage accessed with
1038+
/// [`VssStoreBuilder`] using the same authentication (as storage with different keys is
1039+
/// obviously segmented to prevent wallets from reading data for unrelated wallets). It can be
1040+
/// any value.
1041+
///
9721042
/// **Caution**: VSS support is in **alpha** and is considered experimental.
9731043
/// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
9741044
/// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
9751045
///
9761046
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
9771047
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
978-
pub fn build_with_vss_store(
1048+
pub fn build_with_vss_store_and_lnurl_auth(
9791049
&self, node_entropy: Arc<NodeEntropy>, vss_url: String, store_id: String,
9801050
lnurl_auth_server_url: String, fixed_headers: HashMap<String, String>,
9811051
) -> Result<Arc<Node>, BuildError> {
9821052
self.inner
9831053
.read()
9841054
.unwrap()
985-
.build_with_vss_store(
1055+
.build_with_vss_store_and_lnurl_auth(
9861056
*node_entropy,
9871057
vss_url,
9881058
store_id,

src/io/vss_store.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use lightning::util::ser::{Readable, Writeable};
2929
use prost::Message;
3030
use vss_client::client::VssClient;
3131
use vss_client::error::VssError;
32+
use vss_client::headers::sigs_auth::SigsAuthProvider;
3233
use vss_client::headers::{FixedHeaders, LnurlAuthToJwtProvider, VssHeaderProvider};
3334
use vss_client::types::{
3435
DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest,
@@ -69,6 +70,7 @@ impl_writeable_tlv_based_enum!(VssSchemaVersion,
6970
);
7071

7172
const VSS_HARDENED_CHILD_INDEX: u32 = 877;
73+
const VSS_SIGS_AUTH_HARDENED_CHILD_INDEX: u32 = 139;
7274
const VSS_SCHEMA_VERSION_KEY: &str = "vss_schema_version";
7375

7476
// We set this to a small number of threads that would still allow to make some progress if one
@@ -867,6 +869,44 @@ impl VssStoreBuilder {
867869
Self { node_entropy, vss_url, store_id, network }
868870
}
869871

872+
/// Builds a [`VssStore`] with the simple signature-based authentication scheme.
873+
///
874+
/// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth
875+
/// server.
876+
///
877+
/// **Caution**: VSS support is in **alpha** and is considered experimental. Using VSS (or any
878+
/// remote persistence) may cause LDK to panic if persistence failures are unrecoverable, i.e.,
879+
/// if they remain unresolved after internal retries are exhausted.
880+
///
881+
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
882+
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
883+
pub fn build_with_sigs_auth(
884+
&self, fixed_headers: HashMap<String, String>,
885+
) -> Result<VssStore, VssStoreBuildError> {
886+
let secp_ctx = Secp256k1::new();
887+
let seed_bytes = self.node_entropy.to_seed_bytes();
888+
let vss_xprv = Xpriv::new_master(self.network, &seed_bytes)
889+
.map_err(|_| VssStoreBuildError::KeyDerivationFailed)
890+
.and_then(|master| {
891+
master
892+
.derive_priv(
893+
&secp_ctx,
894+
&[ChildNumber::Hardened { index: VSS_HARDENED_CHILD_INDEX }],
895+
)
896+
.map_err(|_| VssStoreBuildError::KeyDerivationFailed)
897+
})?;
898+
899+
let sigs_auth_xprv = vss_xprv
900+
.derive_priv(
901+
&secp_ctx,
902+
&[ChildNumber::Hardened { index: VSS_SIGS_AUTH_HARDENED_CHILD_INDEX }],
903+
)
904+
.map_err(|_| VssStoreBuildError::KeyDerivationFailed)?;
905+
906+
let auth_provider = SigsAuthProvider::new(sigs_auth_xprv.private_key, fixed_headers);
907+
self.build_with_header_provider(Arc::new(auth_provider))
908+
}
909+
870910
/// Builds a [`VssStore`] with [LNURL-auth] based authentication scheme as default method for
871911
/// authentication/authorization.
872912
///
@@ -883,7 +923,7 @@ impl VssStoreBuilder {
883923
///
884924
/// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
885925
/// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
886-
pub fn build(
926+
pub fn build_with_lnurl(
887927
&self, lnurl_auth_server_url: String, fixed_headers: HashMap<String, String>,
888928
) -> Result<VssStore, VssStoreBuildError> {
889929
let secp_ctx = Secp256k1::new();
@@ -976,7 +1016,6 @@ mod tests {
9761016

9771017
use rand::distr::Alphanumeric;
9781018
use rand::{rng, Rng, RngCore};
979-
use vss_client::headers::FixedHeaders;
9801019

9811020
use super::*;
9821021
use crate::io::test_utils::do_read_write_remove_list_persist;
@@ -986,11 +1025,13 @@ mod tests {
9861025
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
9871026
let mut rng = rng();
9881027
let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
989-
let mut vss_seed = [0u8; 32];
990-
rng.fill_bytes(&mut vss_seed);
991-
let header_provider = Arc::new(FixedHeaders::new(HashMap::new()));
1028+
let mut node_seed = [0u8; 64];
1029+
rng.fill_bytes(&mut node_seed);
1030+
let entropy = NodeEntropy::from_seed_bytes(node_seed);
9921031
let vss_store =
993-
VssStore::new(vss_base_url, rand_store_id, vss_seed, header_provider).unwrap();
1032+
VssStoreBuilder::new(entropy, vss_base_url, rand_store_id, Network::Testnet)
1033+
.build_with_sigs_auth(HashMap::new())
1034+
.unwrap();
9941035
do_read_write_remove_list_persist(&vss_store);
9951036
}
9961037

@@ -999,11 +1040,13 @@ mod tests {
9991040
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
10001041
let mut rng = rng();
10011042
let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
1002-
let mut vss_seed = [0u8; 32];
1003-
rng.fill_bytes(&mut vss_seed);
1004-
let header_provider = Arc::new(FixedHeaders::new(HashMap::new()));
1043+
let mut node_seed = [0u8; 64];
1044+
rng.fill_bytes(&mut node_seed);
1045+
let entropy = NodeEntropy::from_seed_bytes(node_seed);
10051046
let vss_store =
1006-
VssStore::new(vss_base_url, rand_store_id, vss_seed, header_provider).unwrap();
1047+
VssStoreBuilder::new(entropy, vss_base_url, rand_store_id, Network::Testnet)
1048+
.build_with_sigs_auth(HashMap::new())
1049+
.unwrap();
10071050

10081051
do_read_write_remove_list_persist(&vss_store);
10091052
drop(vss_store)

0 commit comments

Comments
 (0)