@@ -147,9 +147,7 @@ private async Task<IActionResult> processSamRockProtocolRequest(SamRockProtocolR
147147 var key = SamRockProtocolKeys . BTC ;
148148 try
149149 {
150- // Parse output descriptor format and convert to NBXplorer format
151- // Input: wpkh([8f681564/84'/0'/0']xpub...xxx/0/*)#8m68c9t7
152- var descriptor = setupModel . BTC . Descriptor ;
150+ var descriptor = NormalizeDescriptor ( setupModel . BTC . Descriptor ) ;
153151
154152 // Extract script type, fingerprint, derivation path, xpub, and address derivation suffix
155153 var match = Regex . Match ( descriptor , @"^(\w+)\(\[([a-fA-F0-9]{8})/([^\]]+)\](xpub[^/\)]+)(/[^\)]+)?\)(?:#[a-zA-Z0-9]+)?$" ) ;
@@ -162,7 +160,7 @@ private async Task<IActionResult> processSamRockProtocolRequest(SamRockProtocolR
162160 {
163161 var scriptType = match . Groups [ 1 ] . Value ;
164162 var fingerprint = match . Groups [ 2 ] . Value ;
165- var basePath = match . Groups [ 3 ] . Value ;
163+ var basePath = NormalizeDerivationPath ( match . Groups [ 3 ] . Value ) ;
166164 var xpub = match . Groups [ 4 ] . Value ;
167165 var addressSuffix = match . Groups [ 5 ] . Value ; // e.g., "/0/*"
168166
@@ -197,43 +195,16 @@ private async Task<IActionResult> processSamRockProtocolRequest(SamRockProtocolR
197195 {
198196 try
199197 {
200- // Parse LBTC output descriptor format and convert to NBXplorer format
201- // Input: ct(slip77(blinding_key),elsh(wpkh([fingerprint/path]xpub...)))
202- var descriptor = setupModel . LBTC . Descriptor ;
203-
204- // Extract slip77 blinding key, script type, fingerprint, derivation path, xpub, and address derivation suffix
205- var match = Regex . Match ( descriptor ,
206- @"^ct\(slip77\(([a-fA-F0-9]{64})\),elsh\((\w+)\(\[([a-fA-F0-9]{8})/([^\]]+)\](xpub[^/\)]+)(/[^\)]+)?\)\)\)(?:#[a-zA-Z0-9]+)?" ) ;
207- if ( ! match . Success )
198+ var descriptor = NormalizeDescriptor ( setupModel . LBTC . Descriptor ) ;
199+ if ( ! TryParseLiquidDescriptor ( descriptor , out var blindingKey , out var suffix , out var fingerprint ,
200+ out var derivationPath , out var xpub , out var error ) )
208201 {
209- result . Results [ key ] = new SamRockProtocolResponse ( false ,
210- "Invalid LBTC descriptor format - could not parse slip77, script type, fingerprint, derivation path, and xpub." , null ) ;
202+ result . Results [ key ] = new SamRockProtocolResponse ( false , error , null ) ;
211203 }
212204 else
213205 {
214- var blindingKey = match . Groups [ 1 ] . Value ;
215- var scriptType = match . Groups [ 2 ] . Value ;
216- var fingerprint = match . Groups [ 3 ] . Value ;
217- var basePath = match . Groups [ 4 ] . Value ;
218- var xpub = match . Groups [ 5 ] . Value ;
219- var addressSuffix = match . Groups [ 6 ] . Value ; // e.g., "/0/*"
220-
221- // TODO: Check whether you need to combine base derivation path with address derivation suffix
222- var derivationPath = basePath ; // + (addressSuffix ?? "");
223-
224- // Convert script type to NBXplorer suffix format
225- //var suffix = GetNBXplorerSuffix(scriptType, descriptor);
226- var suffix = "-[p2sh]" ; // For LBTC at the moment of launch, we assume P2SH_P2WPKH
227- if ( suffix == null )
228- {
229- result . Results [ key ] = new SamRockProtocolResponse ( false , $ "Unsupported LBTC script type: { scriptType } ", null ) ;
230- }
231- else
232- {
233- // Create NBXplorer format derivation scheme for LBTC: xpub + suffix + slip77
234- var derivationScheme = $ "{ xpub } { suffix } -[slip77={ blindingKey } ]";
235- await SetupWalletAsync ( derivationScheme , fingerprint , derivationPath , "LBTC" , storeData , key , result ) ;
236- }
206+ var derivationScheme = $ "{ xpub } { suffix } -[slip77={ blindingKey } ]";
207+ await SetupWalletAsync ( derivationScheme , fingerprint , derivationPath , "LBTC" , storeData , key , result ) ;
237208 }
238209 }
239210 catch ( Exception lbtcex )
@@ -259,7 +230,7 @@ private async Task<IActionResult> processSamRockProtocolRequest(SamRockProtocolR
259230 }
260231 else
261232 {
262- await boltzWrapper . SetBoltz ( StoreId , setupModel . BTCLN . LBTC . Descriptor , result ) ;
233+ await boltzWrapper . SetBoltz ( StoreId , NormalizeDescriptor ( setupModel . BTCLN . LBTC . Descriptor ) , result ) ;
263234 }
264235 }
265236 else
@@ -291,6 +262,76 @@ private async Task<IActionResult> processSamRockProtocolRequest(SamRockProtocolR
291262 } ) ;
292263 }
293264
265+ private static string NormalizeDescriptor ( string descriptor )
266+ {
267+ if ( descriptor == null )
268+ return null ;
269+
270+ return Regex . Replace ( descriptor , @"\s+" , string . Empty ) ;
271+ }
272+
273+ private static string NormalizeDerivationPath ( string path )
274+ {
275+ if ( string . IsNullOrEmpty ( path ) )
276+ return path ;
277+
278+ return string . Join ( '/' , path . Split ( '/' ) . Select ( component =>
279+ component . EndsWith ( "h" , StringComparison . OrdinalIgnoreCase )
280+ ? component [ ..^ 1 ] + "'"
281+ : component ) ) ;
282+ }
283+
284+ private static bool TryParseLiquidDescriptor ( string descriptor , out string blindingKey , out string suffix ,
285+ out string fingerprint , out string derivationPath , out string xpub , out string error )
286+ {
287+ blindingKey = null ;
288+ suffix = null ;
289+ fingerprint = null ;
290+ derivationPath = null ;
291+ xpub = null ;
292+ error = null ;
293+
294+ if ( string . IsNullOrWhiteSpace ( descriptor ) )
295+ {
296+ error = "Invalid LBTC descriptor format - descriptor is empty." ;
297+ return false ;
298+ }
299+
300+ var nativeMatch = Regex . Match ( descriptor ,
301+ @"^ct\(slip77\(([a-fA-F0-9]{64})\),elwpkh\(\[([a-fA-F0-9]{8})/([^\]]+)\](xpub[^/\)]+)(/[^\)]+)?\)\)(?:#[a-zA-Z0-9]+)?$" ) ;
302+ if ( nativeMatch . Success )
303+ {
304+ blindingKey = nativeMatch . Groups [ 1 ] . Value ;
305+ fingerprint = nativeMatch . Groups [ 2 ] . Value ;
306+ derivationPath = NormalizeDerivationPath ( nativeMatch . Groups [ 3 ] . Value ) ;
307+ xpub = nativeMatch . Groups [ 4 ] . Value ;
308+ suffix = "" ;
309+ return true ;
310+ }
311+
312+ var wrappedMatch = Regex . Match ( descriptor ,
313+ @"^ct\(slip77\(([a-fA-F0-9]{64})\),elsh\((\w+)\(\[([a-fA-F0-9]{8})/([^\]]+)\](xpub[^/\)]+)(/[^\)]+)?\)\)\)(?:#[a-zA-Z0-9]+)?$" ) ;
314+ if ( wrappedMatch . Success )
315+ {
316+ var scriptType = wrappedMatch . Groups [ 2 ] . Value ;
317+ if ( ! string . Equals ( scriptType , "wpkh" , StringComparison . OrdinalIgnoreCase ) )
318+ {
319+ error = $ "Unsupported LBTC script type: elsh({ scriptType } )";
320+ return false ;
321+ }
322+
323+ blindingKey = wrappedMatch . Groups [ 1 ] . Value ;
324+ fingerprint = wrappedMatch . Groups [ 3 ] . Value ;
325+ derivationPath = NormalizeDerivationPath ( wrappedMatch . Groups [ 4 ] . Value ) ;
326+ xpub = wrappedMatch . Groups [ 5 ] . Value ;
327+ suffix = "-[p2sh]" ;
328+ return true ;
329+ }
330+
331+ error = "Invalid LBTC descriptor format - expected ct(slip77(...),elwpkh(...)) or ct(slip77(...),elsh(wpkh(...)))." ;
332+ return false ;
333+ }
334+
294335 private async Task SetupWalletAsync ( string derivationScheme , string fingerprint , string derivationPath , string networkCode ,
295336 StoreData storeData , SamRockProtocolKeys key , SamRockProtocolSetupResponse result )
296337 {
0 commit comments