@@ -3740,6 +3740,8 @@ pub(crate) async fn start_ldk(
37403740 // before ack); ChannelManager and aux state keep the local-first `kv_store`
37413741 // sync facade. Both share the same local DB and (when configured) VSS store.
37423742 #[ cfg( feature = "vss" ) ]
3743+ let mut fence_guard: Option < crate :: vss_kv_store:: FenceReleaseGuard > = None ;
3744+ #[ cfg( feature = "vss" ) ]
37433745 let ( kv_store, monitor_kv_store) = if let ( Some ( ref vss_url) , Some ( ref identity) ) =
37443746 ( & static_state. vss_url , & vss_identity)
37453747 {
@@ -3760,6 +3762,16 @@ pub(crate) async fn start_ldk(
37603762 . acquire_fence ( )
37613763 . map_err ( |e| APIError :: FailedVssInit ( e. to_string ( ) ) ) ?;
37623764
3765+ // Release the just-acquired fence if the rest of startup fails.
3766+ fence_guard = Some ( crate :: vss_kv_store:: FenceReleaseGuard :: new ( {
3767+ let store = Arc :: clone ( & vss_kv_store) ;
3768+ move || {
3769+ if let Err ( e) = store. release_fence_if_owned ( ) {
3770+ tracing:: warn!( error = %e, "failed to release VSS fence after aborted unlock" ) ;
3771+ }
3772+ }
3773+ } ) ) ;
3774+
37633775 let monitor_kv_store = Arc :: new ( RemoteFirstKvStore :: new (
37643776 Arc :: clone ( & local_kv_store) ,
37653777 Some ( Arc :: clone ( & vss_kv_store) ) ,
@@ -4271,8 +4283,20 @@ pub(crate) async fn start_ldk(
42714283 witness_version : WitnessVersion :: Taproot ,
42724284 } ;
42734285 let reuse_addresses = static_state. reuse_addresses ;
4274- let mut rgb_wallet = tokio:: task:: spawn_blocking ( move || {
4275- RgbLibWallet :: new (
4286+ let indexer_url_owned = indexer_url. to_string ( ) ;
4287+ #[ cfg( feature = "vss" ) ]
4288+ let rgb_vss_backup = match ( & static_state. vss_url , & vss_identity) {
4289+ ( Some ( vss_url) , Some ( identity) ) => Some ( (
4290+ vss_url. clone ( ) ,
4291+ format ! ( "{}_rgb" , identity. pubkey_hex) ,
4292+ identity. signing_key ,
4293+ ) ) ,
4294+ _ => None ,
4295+ } ;
4296+ // go_online and configure_vss_backup drive blocking rgb-lib HTTP clients;
4297+ // run them off the async runtime so they don't fail on a single-vCPU host.
4298+ let ( rgb_wallet, rgb_online) = tokio:: task:: spawn_blocking ( move || {
4299+ let mut rgb_wallet = RgbLibWallet :: new (
42764300 WalletData {
42774301 data_dir,
42784302 bitcoin_network,
@@ -4283,40 +4307,31 @@ pub(crate) async fn start_ldk(
42834307 } ,
42844308 keys,
42854309 )
4286- . expect ( "valid rgb-lib wallet" )
4310+ . expect ( "valid rgb-lib wallet" ) ;
4311+ let rgb_online = rgb_wallet. go_online ( OnlineOptions {
4312+ indexer_url : indexer_url_owned,
4313+ skip_consistency_check : false ,
4314+ vanilla_sync_lookback : 20 ,
4315+ } ) ?;
4316+ #[ cfg( feature = "vss" ) ]
4317+ if let Some ( ( vss_url, rgb_store_id, signing_key) ) = rgb_vss_backup {
4318+ let vss_config =
4319+ rgb_lib:: wallet:: vss:: VssBackupConfig :: new ( vss_url, rgb_store_id, signing_key)
4320+ . with_encryption ( true )
4321+ . with_auto_backup ( true )
4322+ . with_backup_mode ( rgb_lib:: wallet:: vss:: VssBackupMode :: Blocking ) ;
4323+ // Fail closed: a misconfigured backup must not silently run local-only.
4324+ rgb_wallet. configure_vss_backup ( vss_config) . map_err ( |e| {
4325+ APIError :: FailedVssInit ( format ! (
4326+ "Failed to configure VSS backup for RGB wallet: {e}"
4327+ ) )
4328+ } ) ?;
4329+ tracing:: info!( "VSS auto-backup (blocking) enabled for RGB wallet" ) ;
4330+ }
4331+ Ok :: < _ , APIError > ( ( rgb_wallet, rgb_online) )
42874332 } )
42884333 . await
4289- . unwrap ( ) ;
4290- let rgb_online = rgb_wallet. go_online ( OnlineOptions {
4291- indexer_url : indexer_url. to_string ( ) ,
4292- skip_consistency_check : false ,
4293- vanilla_sync_lookback : 20 ,
4294- } ) ?;
4295-
4296- // Configure VSS backup for the RGB wallet if VSS is enabled. Reuses the
4297- // identity derived once at the top of this function — see N3.1 / the
4298- // `derive_vss_identity` helper.
4299- #[ cfg( feature = "vss" ) ]
4300- if let ( Some ( ref vss_url) , Some ( ref identity) ) = ( & static_state. vss_url , & vss_identity) {
4301- let rgb_store_id = format ! ( "{}_rgb" , identity. pubkey_hex) ;
4302- let vss_config = rgb_lib:: wallet:: vss:: VssBackupConfig :: new (
4303- vss_url. clone ( ) ,
4304- rgb_store_id,
4305- identity. signing_key ,
4306- )
4307- . with_encryption ( true )
4308- . with_auto_backup ( true )
4309- // Blocking: each RGB op waits until its VSS backup is durable.
4310- . with_backup_mode ( rgb_lib:: wallet:: vss:: VssBackupMode :: Blocking ) ;
4311-
4312- // Fail closed: a misconfigured backup must not silently run local-only.
4313- rgb_wallet. configure_vss_backup ( vss_config) . map_err ( |e| {
4314- APIError :: FailedVssInit ( format ! (
4315- "Failed to configure VSS backup for RGB wallet: {e}"
4316- ) )
4317- } ) ?;
4318- tracing:: info!( "VSS auto-backup (blocking) enabled for RGB wallet" ) ;
4319- }
4334+ . map_err ( |e| APIError :: Unexpected ( format ! ( "rgb-lib wallet setup task failed: {e}" ) ) ) ??;
43204335 save_config (
43214336 & static_state. db ( ) ,
43224337 kv_store. as_ref ( ) ,
@@ -5094,6 +5109,11 @@ pub(crate) async fn start_ldk(
50945109 tracing:: info!( "LDK logs are available at <your-supplied-ldk-data-dir-path>/.ldk/logs" ) ;
50955110 tracing:: info!( "Local Node ID is {}" , channel_manager. get_our_node_id( ) ) ;
50965111
5112+ #[ cfg( feature = "vss" ) ]
5113+ if let Some ( guard) = fence_guard. as_mut ( ) {
5114+ guard. disarm ( ) ;
5115+ }
5116+
50975117 Ok ( (
50985118 LdkBackgroundServices {
50995119 stop_processing,
@@ -5183,6 +5203,27 @@ pub(crate) async fn stop_ldk(app_state: Arc<AppState>) {
51835203 join_handle. await . unwrap ( ) . unwrap ( ) ;
51845204 }
51855205
5206+ // Graceful teardown (lock, shutdown, signal): release the VSS fence so
5207+ // the next unlock — a fresh instance id — takes over without an explicit
5208+ // /vssclearfence. Hard kills still leave the fence behind by design.
5209+ #[ cfg( feature = "vss" ) ]
5210+ {
5211+ let kv_store = app_state
5212+ . get_unlocked_app_state ( )
5213+ . await
5214+ . as_ref ( )
5215+ . map ( |unlocked| Arc :: clone ( & unlocked. kv_store ) ) ;
5216+ if let Some ( kv_store) = kv_store {
5217+ match tokio:: task:: spawn_blocking ( move || kv_store. release_vss_fence_if_owned ( ) ) . await {
5218+ Ok ( Ok ( ( ) ) ) => { }
5219+ Ok ( Err ( e) ) => {
5220+ tracing:: warn!( error = %e, "failed to release VSS fence during shutdown" )
5221+ }
5222+ Err ( e) => tracing:: warn!( error = %e, "VSS fence release task failed" ) ,
5223+ }
5224+ }
5225+ }
5226+
51865227 // connect to the peer port so it can be released
51875228 let peer_port = app_state. static_state . ldk_peer_listening_port ;
51885229 let sock_addr = SocketAddr :: from ( ( [ 127 , 0 , 0 , 1 ] , peer_port) ) ;
0 commit comments