@@ -480,7 +480,7 @@ pub struct WalletLoadRequest {
480
480
const RPC_WALLET_NOT_LOADED : i32 = -18 ;
481
481
482
482
impl WalletManager {
483
- pub async fn import_wallet ( & self , wallet : WalletExport ) -> anyhow:: Result < ( ) > {
483
+ pub async fn import_wallet ( & self , mut wallet : WalletExport ) -> anyhow:: Result < ( ) > {
484
484
let wallet_path = self . data_dir . join ( & wallet. label ) ;
485
485
if wallet_path. exists ( ) {
486
486
return Err ( anyhow ! ( format!(
@@ -489,6 +489,37 @@ impl WalletManager {
489
489
) ) ) ;
490
490
}
491
491
492
+ // If this is a hex-based descriptor, convert it to proper xprv format before storing
493
+ if let Some ( descriptor) = & wallet. descriptor {
494
+ if descriptor. starts_with ( "tr([hex:" ) {
495
+ if let Some ( hex_secret) = & wallet. hex_secret {
496
+ let ( network, _) = self . fallback_network ( ) ;
497
+ let xpriv = Self :: xpriv_from_hex_secret ( network, hex_secret) ?;
498
+ let ( external_desc, internal_desc) = Self :: default_descriptors ( xpriv) ;
499
+
500
+ // Create a temporary wallet to get the descriptor strings
501
+ let temp_wallet = bdk:: Wallet :: create ( external_desc, internal_desc)
502
+ . network ( network)
503
+ . create_wallet_no_persist ( ) ?;
504
+
505
+ // Get proper xprv-based descriptor string
506
+ let proper_descriptor = temp_wallet
507
+ . public_descriptor ( KeychainKind :: External )
508
+ . to_string_with_secret (
509
+ & temp_wallet
510
+ . get_signers ( KeychainKind :: External )
511
+ . as_key_map ( temp_wallet. secp_ctx ( ) ) ,
512
+ ) ;
513
+ let proper_descriptor = Self :: remove_checksum ( proper_descriptor) ;
514
+
515
+ // Update the wallet to store the proper xprv descriptor
516
+ wallet. descriptor = Some ( proper_descriptor) ;
517
+ } else {
518
+ return Err ( anyhow ! ( "Hex-based descriptor found but no hex_secret in wallet" ) ) ;
519
+ }
520
+ }
521
+ }
522
+
492
523
fs:: create_dir_all ( & wallet_path) ?;
493
524
let wallet_export_path = wallet_path. join ( "wallet.json" ) ;
494
525
let mut file = fs:: File :: create ( wallet_export_path) ?;
@@ -506,14 +537,20 @@ impl WalletManager {
506
537
let wallet = fs:: read_to_string ( wallet_dir. join ( "wallet.json" ) ) ?;
507
538
let mut export: WalletExport = serde_json:: from_str ( & wallet) ?;
508
539
509
- // If hex_secret is requested, we need to extract it from the wallet descriptor
540
+ // If hex_secret is requested, use the stored hex_secret if available,
541
+ // otherwise extract it from the xprv descriptor
510
542
if hex_secret {
511
- // Parse the descriptor to extract the xprv and get the hex secret
512
- if let Some ( descriptor) = & export. descriptor {
513
- if let Some ( hex_secret_value) = self . extract_hex_secret_from_descriptor ( descriptor) ? {
514
- export. hex_secret = Some ( hex_secret_value) ;
543
+ if export. hex_secret . is_none ( ) {
544
+ // Only extract from descriptor if no hex_secret was stored
545
+ if let Some ( descriptor) = & export. descriptor {
546
+ if let Some ( hex_secret_value) = self . extract_hex_secret_from_descriptor ( descriptor) ? {
547
+ export. hex_secret = Some ( hex_secret_value) ;
548
+ }
515
549
}
516
550
}
551
+ } else {
552
+ // If hex_secret is not requested, remove it from the export
553
+ export. hex_secret = None ;
517
554
}
518
555
519
556
Ok ( export)
@@ -644,7 +681,7 @@ impl WalletManager {
644
681
let file = fs:: File :: open ( wallet_dir. join ( "wallet.json" ) ) ?;
645
682
646
683
let ( network, genesis_hash) = self . fallback_network ( ) ;
647
- let export: WalletExport = serde_json:: from_reader ( file) ?;
684
+ let mut export: WalletExport = serde_json:: from_reader ( file) ?;
648
685
649
686
let wallet_config = WalletConfig {
650
687
start_block : export. blockheight ,
@@ -656,50 +693,11 @@ impl WalletManager {
656
693
let external_descriptor = export. descriptor ( ) . expect ( "expected a descriptor" ) ;
657
694
let internal_descriptor = export. change_descriptor ( ) . expect ( "expected a change descriptor" ) ;
658
695
659
- // Check if this is a hex-based descriptor and convert it
660
- let ( external, internal) = if external_descriptor. starts_with ( "tr([hex:" ) {
661
- // Extract hex secret from descriptor
662
- if let Some ( hex_secret) = export. hex_secret . as_ref ( ) {
663
- // Convert hex secret to proper xprv and create descriptors
664
- let xpriv = Self :: xpriv_from_hex_secret ( network, hex_secret) ?;
665
- let ( external_desc, internal_desc) = Self :: default_descriptors ( xpriv) ;
666
-
667
- // Create a temporary wallet to get the descriptor strings
668
- let temp_wallet = bdk:: Wallet :: create ( external_desc, internal_desc)
669
- . network ( network)
670
- . create_wallet_no_persist ( ) ?;
671
-
672
- // Get descriptor strings using the same method as WalletExport
673
- let external_str = temp_wallet
674
- . public_descriptor ( KeychainKind :: External )
675
- . to_string_with_secret (
676
- & temp_wallet
677
- . get_signers ( KeychainKind :: External )
678
- . as_key_map ( temp_wallet. secp_ctx ( ) ) ,
679
- ) ;
680
- let internal_str = temp_wallet
681
- . public_descriptor ( KeychainKind :: Internal )
682
- . to_string_with_secret (
683
- & temp_wallet
684
- . get_signers ( KeychainKind :: Internal )
685
- . as_key_map ( temp_wallet. secp_ctx ( ) ) ,
686
- ) ;
687
-
688
- // Remove checksums (same as WalletExport does)
689
- let external_str = Self :: remove_checksum ( external_str) ;
690
- let internal_str = Self :: remove_checksum ( internal_str) ;
691
-
692
- ( external_str, internal_str)
693
- } else {
694
- return Err ( anyhow ! ( "Hex-based descriptor found but no hex_secret in export" ) ) ;
695
- }
696
- } else {
697
- ( external_descriptor, internal_descriptor)
698
- } ;
699
-
696
+ // At this point, the descriptor should already be in proper xprv format
697
+ // since import_wallet converts hex-based descriptors before storing
700
698
WalletDescriptors {
701
- external,
702
- internal,
699
+ external : external_descriptor ,
700
+ internal : internal_descriptor ,
703
701
}
704
702
} ,
705
703
} ;
@@ -739,7 +737,7 @@ impl WalletManager {
739
737
}
740
738
741
739
fn xpriv_from_hex_secret ( network : Network , hex_secret : & str ) -> anyhow:: Result < Xpriv > {
742
- use spaces_protocol:: bitcoin:: bip32:: Xpriv ;
740
+ use spaces_protocol:: bitcoin:: bip32:: { Xpriv , ChainCode , ChildNumber } ;
743
741
use spaces_protocol:: bitcoin:: key:: Secp256k1 ;
744
742
use spaces_protocol:: bitcoin:: secp256k1:: SecretKey ;
745
743
@@ -755,10 +753,20 @@ impl WalletManager {
755
753
let secret_key = SecretKey :: from_slice ( & secret_bytes)
756
754
. map_err ( |e| anyhow ! ( "Invalid secret key: {}" , e) ) ?;
757
755
758
- // Create Xpriv from secret key
756
+ // Create Xpriv directly from the private key bytes
757
+ // We need to create a proper xprv structure with the exact private key
759
758
let secp = Secp256k1 :: new ( ) ;
760
- let xpriv = Xpriv :: new_master ( network, & secret_key. secret_bytes ( ) )
761
- . map_err ( |e| anyhow ! ( "Failed to create xpriv: {}" , e) ) ?;
759
+
760
+ // Create the xprv by manually constructing it with the exact private key
761
+ // This ensures the private key bytes are preserved exactly
762
+ let xpriv = Xpriv {
763
+ network : network. into ( ) ,
764
+ depth : 0 ,
765
+ parent_fingerprint : Default :: default ( ) ,
766
+ child_number : ChildNumber :: from_normal_idx ( 0 ) ?,
767
+ chain_code : [ 0u8 ; 32 ] . into ( ) , // Use zero chain code for direct private key
768
+ private_key : secret_key,
769
+ } ;
762
770
763
771
Ok ( xpriv)
764
772
}
0 commit comments