55// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66// accordance with one or both of these licenses.
77
8+ //! Objects related to [`VssStore`] live here.
9+
810use std:: boxed:: Box ;
911use std:: collections:: HashMap ;
12+ use std:: fmt;
1013use std:: future:: Future ;
1114#[ cfg( test) ]
1215use std:: panic:: RefUnwindSafe ;
@@ -15,7 +18,10 @@ use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
1518use std:: sync:: { Arc , Mutex } ;
1619use std:: time:: Duration ;
1720
21+ use bitcoin:: bip32:: { ChildNumber , Xpriv } ;
1822use bitcoin:: hashes:: { sha256, Hash , HashEngine , Hmac , HmacEngine } ;
23+ use bitcoin:: key:: Secp256k1 ;
24+ use bitcoin:: Network ;
1925use lightning:: impl_writeable_tlv_based_enum;
2026use lightning:: io:: { self , Error , ErrorKind } ;
2127use lightning:: util:: persist:: { KVStore , KVStoreSync } ;
@@ -24,7 +30,7 @@ use prost::Message;
2430use rand:: RngCore ;
2531use vss_client:: client:: VssClient ;
2632use vss_client:: error:: VssError ;
27- use vss_client:: headers:: VssHeaderProvider ;
33+ use vss_client:: headers:: { FixedHeaders , LnurlAuthToJwtProvider , VssHeaderProvider } ;
2834use vss_client:: types:: {
2935 DeleteObjectRequest , GetObjectRequest , KeyValue , ListKeyVersionsRequest , PutObjectRequest ,
3036 Storable ,
@@ -36,6 +42,7 @@ use vss_client::util::retry::{
3642} ;
3743use vss_client:: util:: storable_builder:: { EntropySource , StorableBuilder } ;
3844
45+ use crate :: entropy:: NodeEntropy ;
3946use crate :: io:: utils:: check_namespace_key_validity;
4047
4148type CustomRetryPolicy = FilteredRetryPolicy <
@@ -61,13 +68,17 @@ impl_writeable_tlv_based_enum!(VssSchemaVersion,
6168 ( 1 , V1 ) => { } ,
6269) ;
6370
71+ const VSS_HARDENED_CHILD_INDEX : u32 = 877 ;
72+ const VSS_LNURL_AUTH_HARDENED_CHILD_INDEX : u32 = 138 ;
6473const VSS_SCHEMA_VERSION_KEY : & str = "vss_schema_version" ;
6574
6675// We set this to a small number of threads that would still allow to make some progress if one
6776// would hit a blocking case
6877const INTERNAL_RUNTIME_WORKERS : usize = 2 ;
6978
70- /// A [`KVStoreSync`] implementation that writes to and reads from a [VSS](https://github.com/lightningdevkit/vss-server/blob/main/README.md) backend.
79+ /// A [`KVStore`]/[`KVStoreSync`] implementation that writes to and reads from a [VSS] backend.
80+ ///
81+ /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
7182pub struct VssStore {
7283 inner : Arc < VssStoreInner > ,
7384 // Version counter to ensure that writes are applied in the correct order. It is assumed that read and list
@@ -139,6 +150,12 @@ impl VssStore {
139150
140151 Ok ( Self { inner, next_version, internal_runtime : Some ( internal_runtime) } )
141152 }
153+ /// Returns a [`VssStoreBuilder`] allowing to build a [`VssStore`].
154+ pub fn builder (
155+ node_entropy : NodeEntropy , vss_url : String , store_id : String , network : Network ,
156+ ) -> VssStoreBuilder {
157+ VssStoreBuilder :: new ( node_entropy, vss_url, store_id, network)
158+ }
142159
143160 // Same logic as for the obfuscated keys below, but just for locking, using the plaintext keys
144161 fn build_locking_key (
@@ -800,6 +817,149 @@ impl EntropySource for RandEntropySource {
800817#[ cfg( test) ]
801818impl RefUnwindSafe for VssStore { }
802819
820+ /// An error that could arise during [`VssStore`] building.
821+ #[ derive( Debug , Clone , PartialEq ) ]
822+ pub enum VssStoreBuildError {
823+ /// Key derivation failed
824+ KeyDerivationFailed ,
825+ /// Authentication provider setup failed
826+ AuthProviderSetupFailed ,
827+ /// Store setup failed
828+ StoreSetupFailed ,
829+ }
830+
831+ impl fmt:: Display for VssStoreBuildError {
832+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
833+ match * self {
834+ Self :: KeyDerivationFailed => write ! ( f, "Key derivation failed" ) ,
835+ Self :: AuthProviderSetupFailed => write ! ( f, "Authentication provider setup failed" ) ,
836+ Self :: StoreSetupFailed => write ! ( f, "Store setup failed" ) ,
837+ }
838+ }
839+ }
840+
841+ impl std:: error:: Error for VssStoreBuildError { }
842+
843+ /// A builder for a [`VssStore`] instance.
844+ pub struct VssStoreBuilder {
845+ node_entropy : NodeEntropy ,
846+ vss_url : String ,
847+ store_id : String ,
848+ network : Network ,
849+ }
850+
851+ impl VssStoreBuilder {
852+ /// Create a new [`VssStoreBuilder`].
853+ pub fn new (
854+ node_entropy : NodeEntropy , vss_url : String , store_id : String , network : Network ,
855+ ) -> Self {
856+ Self { node_entropy, vss_url, store_id, network }
857+ }
858+
859+ /// Builds a [`VssStore`] with [LNURL-auth] based authentication scheme as default method for
860+ /// authentication/authorization.
861+ ///
862+ /// The LNURL challenge will be retrieved by making a request to the given
863+ /// `lnurl_auth_server_url`. The returned JWT token in response to the signed LNURL request,
864+ /// will be used for authentication/authorization of all the requests made to VSS.
865+ ///
866+ /// `fixed_headers` are included as it is in all the requests made to VSS and LNURL auth
867+ /// server.
868+ ///
869+ /// **Caution**: VSS support is in **alpha** and is considered experimental. Using VSS (or any
870+ /// remote persistence) may cause LDK to panic if persistence failures are unrecoverable, i.e.,
871+ /// if they remain unresolved after internal retries are exhausted.
872+ ///
873+ /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
874+ /// [LNURL-auth]: https://github.com/lnurl/luds/blob/luds/04.md
875+ pub fn build (
876+ & self , lnurl_auth_server_url : String , fixed_headers : HashMap < String , String > ,
877+ ) -> Result < VssStore , VssStoreBuildError > {
878+ use bitcoin:: key:: Secp256k1 ;
879+
880+ let seed_bytes = self . node_entropy . to_seed_bytes ( ) ;
881+ let vss_xprv = Xpriv :: new_master ( self . network , & seed_bytes)
882+ . map_err ( |_| VssStoreBuildError :: KeyDerivationFailed )
883+ . and_then ( |master| {
884+ master
885+ . derive_priv (
886+ & Secp256k1 :: new ( ) ,
887+ & [ ChildNumber :: Hardened { index : VSS_HARDENED_CHILD_INDEX } ] ,
888+ )
889+ . map_err ( |_| VssStoreBuildError :: KeyDerivationFailed )
890+ } ) ?;
891+
892+ let lnurl_auth_xprv = vss_xprv
893+ . derive_priv (
894+ & Secp256k1 :: new ( ) ,
895+ & [ ChildNumber :: Hardened { index : VSS_LNURL_AUTH_HARDENED_CHILD_INDEX } ] ,
896+ )
897+ . map_err ( |_| VssStoreBuildError :: KeyDerivationFailed ) ?;
898+
899+ let lnurl_auth_jwt_provider =
900+ LnurlAuthToJwtProvider :: new ( lnurl_auth_xprv, lnurl_auth_server_url, fixed_headers)
901+ . map_err ( |_| VssStoreBuildError :: AuthProviderSetupFailed ) ?;
902+
903+ let header_provider = Arc :: new ( lnurl_auth_jwt_provider) ;
904+
905+ self . build_with_header_provider ( header_provider)
906+ }
907+
908+ /// Builds a [`VssStore`] with [`FixedHeaders`] as default method for
909+ /// authentication/authorization.
910+ ///
911+ /// Given `fixed_headers` are included as it is in all the requests made to VSS.
912+ ///
913+ /// **Caution**: VSS support is in **alpha** and is considered experimental. Using VSS (or any
914+ /// remote persistence) may cause LDK to panic if persistence failures are unrecoverable, i.e.,
915+ /// if they remain unresolved after internal retries are exhausted.
916+ ///
917+ /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
918+ pub fn build_with_fixed_headers (
919+ & self , fixed_headers : HashMap < String , String > ,
920+ ) -> Result < VssStore , VssStoreBuildError > {
921+ let header_provider = Arc :: new ( FixedHeaders :: new ( fixed_headers) ) ;
922+ self . build_with_header_provider ( header_provider)
923+ }
924+
925+ /// Builds a [`VssStore`] with [`VssHeaderProvider`].
926+ ///
927+ /// Any headers provided by `header_provider` will be attached to every request made to VSS.
928+ ///
929+ /// **Caution**: VSS support is in **alpha** and is considered experimental.
930+ /// Using VSS (or any remote persistence) may cause LDK to panic if persistence failures are
931+ /// unrecoverable, i.e., if they remain unresolved after internal retries are exhausted.
932+ ///
933+ /// [VSS]: https://github.com/lightningdevkit/vss-server/blob/main/README.md
934+ pub fn build_with_header_provider (
935+ & self , header_provider : Arc < dyn VssHeaderProvider > ,
936+ ) -> Result < VssStore , VssStoreBuildError > {
937+ let seed_bytes = self . node_entropy . to_seed_bytes ( ) ;
938+ let vss_xprv = Xpriv :: new_master ( self . network , & seed_bytes)
939+ . map_err ( |_| VssStoreBuildError :: KeyDerivationFailed )
940+ . and_then ( |master| {
941+ master
942+ . derive_priv (
943+ & Secp256k1 :: new ( ) ,
944+ & [ ChildNumber :: Hardened { index : VSS_HARDENED_CHILD_INDEX } ] ,
945+ )
946+ . map_err ( |_| VssStoreBuildError :: KeyDerivationFailed )
947+ } ) ?;
948+
949+ let vss_seed_bytes: [ u8 ; 32 ] = vss_xprv. private_key . secret_bytes ( ) ;
950+
951+ let vss_store = VssStore :: new (
952+ self . vss_url . clone ( ) ,
953+ self . store_id . clone ( ) ,
954+ vss_seed_bytes,
955+ header_provider,
956+ )
957+ . map_err ( |_| VssStoreBuildError :: StoreSetupFailed ) ?;
958+
959+ Ok ( vss_store)
960+ }
961+ }
962+
803963#[ cfg( test) ]
804964#[ cfg( vss_test) ]
805965mod tests {
0 commit comments