@@ -14,7 +14,9 @@ use bdk_wallet::bitcoin::consensus::encode::serialize;
14
14
use bdk_wallet:: bitcoin:: consensus:: Decodable ;
15
15
use bdk_wallet:: bitcoin:: hashes:: sha256:: Hash as BitcoinSha256Hash ;
16
16
use bdk_wallet:: bitcoin:: hashes:: sha256d:: Hash as BitcoinDoubleSha256Hash ;
17
+ use bdk_wallet:: bitcoin:: hex:: DisplayHex ;
17
18
use bdk_wallet:: bitcoin:: io:: Cursor ;
19
+ use bdk_wallet:: bitcoin:: psbt:: Input as BdkInput ;
18
20
use bdk_wallet:: bitcoin:: secp256k1:: Secp256k1 ;
19
21
use bdk_wallet:: bitcoin:: Amount as BdkAmount ;
20
22
use bdk_wallet:: bitcoin:: BlockHash as BitcoinBlockHash ;
@@ -500,6 +502,267 @@ impl Display for Transaction {
500
502
}
501
503
}
502
504
505
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
506
+ pub struct PartialSig {
507
+ pub pubkey : String ,
508
+ pub signature : Vec < u8 > ,
509
+ }
510
+
511
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
512
+ pub struct Bip32Derivation {
513
+ /// hex-encoded public key (serialized)
514
+ pub pubkey : String ,
515
+ /// master key fingerprint
516
+ pub fingerprint : String ,
517
+ /// derivation path segments
518
+ pub path : String ,
519
+ }
520
+
521
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
522
+ pub struct PreimagePair {
523
+ /// hex-encoded hash (RIPEMD160 / SHA256 / HASH160 / HASH256 as applicable)
524
+ pub hash : String ,
525
+ /// preimage bytes
526
+ pub preimage : Vec < u8 > ,
527
+ }
528
+
529
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
530
+ pub struct TapScriptSig {
531
+ /// x-only pubkey as hex
532
+ pub xonly_pubkey : String ,
533
+ /// tap leaf hash as hex
534
+ pub leaf_hash : String ,
535
+ /// signature bytes
536
+ pub signature : Vec < u8 > ,
537
+ }
538
+
539
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
540
+ pub struct TapScriptEntry {
541
+ /// control block bytes
542
+ pub control_block : String ,
543
+ /// script (reuse existing `Script` FFI type)
544
+ pub script : Arc < Script > ,
545
+ /// leaf version
546
+ pub leaf_version : u8 ,
547
+ }
548
+
549
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
550
+ pub struct TapKeyOrigin {
551
+ /// x-only pubkey as hex
552
+ pub xonly_pubkey : String ,
553
+ /// leaf hashes as hex strings
554
+ pub leaf_hashes : Vec < String > ,
555
+ /// master key fingerprint
556
+ pub fingerprint : String ,
557
+ /// derivation path segments
558
+ pub path : String ,
559
+ }
560
+
561
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
562
+ pub struct KeyValuePair {
563
+ /// raw key bytes (for proprietary / unknown keys)
564
+ pub key : Vec < u8 > ,
565
+ /// raw value bytes
566
+ pub value : Vec < u8 > ,
567
+ /// The type of this PSBT key.
568
+ pub type_value : u8 ,
569
+ }
570
+
571
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
572
+ pub struct ProprietaryKeyValuePair {
573
+ /// Proprietary type prefix used for grouping together keys under some application and avoid namespace collision
574
+ pub prefix : Vec < u8 > ,
575
+ /// Custom proprietary subtype
576
+ pub subtype : u8 ,
577
+ /// Additional key bytes (like serialized public key data etc)
578
+ pub key : Vec < u8 > ,
579
+ /// raw value bytes
580
+ pub value : Vec < u8 > ,
581
+ }
582
+
583
+ /// A key-value map for an input of the corresponding index in the unsigned transaction.
584
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
585
+ pub struct Input {
586
+ /// The non-witness transaction this input spends from. Should only be
587
+ /// `Option::Some` for inputs which spend non-segwit outputs or
588
+ /// if it is unknown whether an input spends a segwit output.
589
+ pub non_witness_utxo : Option < Arc < Transaction > > ,
590
+ /// The transaction output this input spends from. Should only be
591
+ /// `Option::Some` for inputs which spend segwit outputs,
592
+ /// including P2SH embedded ones.
593
+ pub witness_utxo : Option < TxOut > ,
594
+ /// A map from public keys to their corresponding signature as would be
595
+ // pushed to the stack from a scriptSig or witness for a non-taproot inputs.
596
+ pub partial_sigs : Vec < PartialSig > ,
597
+ /// The sighash type to be used for this input. Signatures for this input
598
+ /// must use the sighash type.
599
+ pub sighash_type : Option < String > ,
600
+ /// The redeem script for this input.
601
+ pub redeem_script : Option < Arc < Script > > ,
602
+ /// The witness script for this input.
603
+ pub witness_script : Option < Arc < Script > > ,
604
+ /// A map from public keys needed to sign this input to their corresponding
605
+ /// master key fingerprints and derivation paths.
606
+ pub bip32_derivation : Vec < Bip32Derivation > ,
607
+
608
+ /// The finalized, fully-constructed scriptSig with signatures and any other
609
+ /// scripts necessary for this input to pass validation.
610
+ pub final_script_sig : Option < Arc < Script > > ,
611
+
612
+ /// The finalized, fully-constructed scriptWitness with signatures and any
613
+ /// other scripts necessary for this input to pass validation.
614
+ pub final_script_witness : Option < Vec < Vec < u8 > > > ,
615
+ /// RIPEMD160 hash to preimage map.
616
+ pub ripemd160_preimages : Vec < PreimagePair > ,
617
+ /// SHA256 hash to preimage map.
618
+ pub sha256_preimages : Vec < PreimagePair > ,
619
+ /// HASH160 hash to preimage map.
620
+ pub hash160_preimages : Vec < PreimagePair > ,
621
+ /// HASH256 hash to preimage map.
622
+ pub hash256_preimages : Vec < PreimagePair > ,
623
+ /// Serialized taproot signature with sighash type for key spend.
624
+ pub tap_key_sig : Option < Vec < u8 > > ,
625
+ /// Map of `<xonlypubkey>|<leafhash>` with signature.
626
+ pub tap_script_sigs : Vec < TapScriptSig > ,
627
+ /// Map of Control blocks to Script version pair.
628
+ pub tap_scripts : Vec < TapScriptEntry > ,
629
+ /// Map of tap root x only keys to origin info and leaf hashes contained in it.
630
+ pub tap_key_origins : Vec < TapKeyOrigin > ,
631
+ /// Taproot Internal key.
632
+ pub tap_internal_key : Option < String > ,
633
+ /// Taproot Merkle root.
634
+ pub tap_merkle_root : Option < String > ,
635
+ /// Proprietary key-value pairs for this input.
636
+ pub proprietary : Vec < ProprietaryKeyValuePair > ,
637
+ /// Unknown key-value pairs for this input.
638
+ pub unknown : Vec < KeyValuePair > ,
639
+ }
640
+
641
+ impl From < & BdkInput > for Input {
642
+ fn from ( input : & BdkInput ) -> Self {
643
+ Input {
644
+ non_witness_utxo : input
645
+ . non_witness_utxo
646
+ . as_ref ( )
647
+ . map ( |tx| Arc :: new ( Transaction ( tx. clone ( ) ) ) ) ,
648
+ witness_utxo : input. witness_utxo . as_ref ( ) . map ( TxOut :: from) ,
649
+ partial_sigs : input
650
+ . partial_sigs
651
+ . iter ( )
652
+ . map ( |( k, v) | PartialSig {
653
+ pubkey : k. to_string ( ) ,
654
+ signature : v. to_vec ( ) ,
655
+ } )
656
+ . collect :: < Vec < PartialSig > > ( ) ,
657
+ sighash_type : input. sighash_type . as_ref ( ) . map ( |s| s. to_string ( ) ) ,
658
+ redeem_script : input
659
+ . redeem_script
660
+ . as_ref ( )
661
+ . map ( |s| Arc :: new ( Script ( s. clone ( ) ) ) ) ,
662
+ witness_script : input
663
+ . witness_script
664
+ . as_ref ( )
665
+ . map ( |s| Arc :: new ( Script ( s. clone ( ) ) ) ) ,
666
+ bip32_derivation : input
667
+ . bip32_derivation
668
+ . iter ( )
669
+ . map ( |( k, v) | Bip32Derivation {
670
+ pubkey : k. to_string ( ) ,
671
+ fingerprint : v. 0 . to_string ( ) ,
672
+ path : v. 1 . to_string ( ) ,
673
+ } )
674
+ . collect :: < Vec < Bip32Derivation > > ( ) ,
675
+ final_script_sig : input
676
+ . final_script_sig
677
+ . as_ref ( )
678
+ . map ( |s| Arc :: new ( Script ( s. clone ( ) ) ) ) ,
679
+ final_script_witness : input. final_script_witness . as_ref ( ) . map ( |w| w. to_vec ( ) ) ,
680
+ ripemd160_preimages : input
681
+ . ripemd160_preimages
682
+ . iter ( )
683
+ . map ( |( k, v) | PreimagePair {
684
+ hash : k. to_string ( ) ,
685
+ preimage : v. to_vec ( ) ,
686
+ } )
687
+ . collect :: < Vec < PreimagePair > > ( ) ,
688
+ sha256_preimages : input
689
+ . sha256_preimages
690
+ . iter ( )
691
+ . map ( |( k, v) | PreimagePair {
692
+ hash : k. to_string ( ) ,
693
+ preimage : v. to_vec ( ) ,
694
+ } )
695
+ . collect :: < Vec < PreimagePair > > ( ) ,
696
+ hash160_preimages : input
697
+ . hash160_preimages
698
+ . iter ( )
699
+ . map ( |( k, v) | PreimagePair {
700
+ hash : k. to_string ( ) ,
701
+ preimage : v. to_vec ( ) ,
702
+ } )
703
+ . collect :: < Vec < PreimagePair > > ( ) ,
704
+ hash256_preimages : input
705
+ . hash256_preimages
706
+ . iter ( )
707
+ . map ( |( k, v) | PreimagePair {
708
+ hash : k. to_string ( ) ,
709
+ preimage : v. to_vec ( ) ,
710
+ } )
711
+ . collect :: < Vec < PreimagePair > > ( ) ,
712
+ tap_key_sig : input. tap_key_sig . as_ref ( ) . map ( |s| s. serialize ( ) . to_vec ( ) ) ,
713
+ tap_script_sigs : input
714
+ . tap_script_sigs
715
+ . iter ( )
716
+ . map ( |( k, v) | TapScriptSig {
717
+ xonly_pubkey : k. 0 . to_string ( ) ,
718
+ leaf_hash : k. 1 . to_string ( ) ,
719
+ signature : v. to_vec ( ) ,
720
+ } )
721
+ . collect :: < Vec < TapScriptSig > > ( ) ,
722
+ tap_scripts : input
723
+ . tap_scripts
724
+ . iter ( )
725
+ . map ( |( k, v) | TapScriptEntry {
726
+ control_block : DisplayHex :: to_lower_hex_string ( & k. serialize ( ) ) ,
727
+ script : Arc :: new ( v. 0 . clone ( ) . into ( ) ) ,
728
+ leaf_version : v. 1 . to_consensus ( ) ,
729
+ } )
730
+ . collect :: < Vec < TapScriptEntry > > ( ) ,
731
+ tap_key_origins : input
732
+ . tap_key_origins
733
+ . iter ( )
734
+ . map ( |( k, v) | TapKeyOrigin {
735
+ xonly_pubkey : k. to_string ( ) ,
736
+ leaf_hashes : v. 0 . iter ( ) . map ( |h| h. to_string ( ) ) . collect ( ) ,
737
+ fingerprint : v. 1 . 0 . to_string ( ) ,
738
+ path : v. 1 . 1 . to_string ( ) ,
739
+ } )
740
+ . collect :: < Vec < TapKeyOrigin > > ( ) ,
741
+ tap_internal_key : input. tap_internal_key . as_ref ( ) . map ( |k| k. to_string ( ) ) ,
742
+ tap_merkle_root : input. tap_merkle_root . as_ref ( ) . map ( |k| k. to_string ( ) ) ,
743
+ proprietary : input
744
+ . proprietary
745
+ . iter ( )
746
+ . map ( |( k, v) | ProprietaryKeyValuePair {
747
+ key : k. to_key ( ) . key . clone ( ) ,
748
+ subtype : k. subtype ,
749
+ prefix : k. prefix . to_vec ( ) ,
750
+ value : v. to_vec ( ) ,
751
+ } )
752
+ . collect ( ) ,
753
+ unknown : input
754
+ . unknown
755
+ . iter ( )
756
+ . map ( |( k, v) | KeyValuePair {
757
+ key : k. key . clone ( ) ,
758
+ value : v. to_vec ( ) ,
759
+ type_value : k. type_value ,
760
+ } )
761
+ . collect ( ) ,
762
+ }
763
+ }
764
+ }
765
+
503
766
/// A Partially Signed Transaction.
504
767
#[ derive( uniffi:: Object ) ]
505
768
pub struct Psbt ( pub ( crate ) Mutex < BdkPsbt > ) ;
@@ -625,6 +888,12 @@ impl Psbt {
625
888
let utxo = psbt. spend_utxo ( input_index as usize ) . unwrap ( ) ;
626
889
serde_json:: to_string ( & utxo) . unwrap ( )
627
890
}
891
+
892
+ /// The corresponding key-value map for each input in the unsigned transaction.
893
+ pub fn input ( & self ) -> Vec < Input > {
894
+ let psbt = self . 0 . lock ( ) . unwrap ( ) ;
895
+ psbt. inputs . iter ( ) . map ( |input| input. into ( ) ) . collect ( )
896
+ }
628
897
}
629
898
630
899
impl From < BdkPsbt > for Psbt {
@@ -1135,10 +1404,8 @@ mod tests {
1135
1404
1136
1405
#[ test]
1137
1406
fn test_psbt_spend_utxo ( ) {
1138
- let psbt = Psbt :: new ( "cHNidP8BAH0CAAAAAXHl8cCbj84lm1v42e54IGI6CQru/nBXwrPE3q2fiGO4AAAAAAD9////Ar4DAAAAAAAAIgAgYw/rnGd4Bifj8s7TaMgR2tal/lq+L1jVv2Sqd1mxMbJEEQAAAAAAABYAFNVpt8vHYUPZNSF6Hu07uP1YeHts4QsAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BAJ+CwD/////AkAlAAAAAAAAIgAgQyrnn86L9D3vDiH959KJbPudDHc/bp6nI9E5EBLQD1YAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErQCUAAAAAAAAiACBDKuefzov0Pe8OIf3n0ols+50Mdz9unqcj0TkQEtAPViICAy4V+d/Qff71zzPXxK4FWG5x+wL/Ku93y/LG5p+0rI2xSDBFAiEA9b0OdASAs0P2uhQinjN7QGP5jX/b32LcShBmny8U0RUCIBebxvCDbpchCjqLAhOMjydT80DAzokaalGzV7XVTsbiASICA1tMY+46EgxIHU18bgHnUvAAlAkMq5LfwkpOGZ97sDKRRzBEAiBpmlZwJocNEiKLxexEX0Par6UgG8a89AklTG3/z9AHlAIgQH/ybCvfKJzr2dq0+IyueDebm7FamKIJdzBYWMXRr/wBIgID+aCzK9nclwhbbN7KbIVGUQGLWZsjcaqWPxk9gFeG+FxIMEUCIQDRPBzb0i9vaUmxCcs1yz8uq4tq1mdDAYvvYn3isKEhFAIgfmeTLLzMo0mmQ23ooMnyx6iPceE8xV5CvARuJsd88tEBAQVpUiEDW0xj7joSDEgdTXxuAedS8ACUCQyrkt/CSk4Zn3uwMpEhAy4V+d/Qff71zzPXxK4FWG5x+wL/Ku93y/LG5p+0rI2xIQP5oLMr2dyXCFts3spshUZRAYtZmyNxqpY/GT2AV4b4XFOuIgYDLhX539B9/vXPM9fErgVYbnH7Av8q73fL8sbmn7SsjbEYCapBE1QAAIABAACAAAAAgAAAAAAAAAAAIgYDW0xj7joSDEgdTXxuAedS8ACUCQyrkt/CSk4Zn3uwMpEY2bvrelQAAIABAACAAAAAgAAAAAAAAAAAIgYD+aCzK9nclwhbbN7KbIVGUQGLWZsjcaqWPxk9gFeG+FwYAKVFVFQAAIABAACAAAAAgAAAAAAAAAAAAAEBaVIhA7cr8fTHOPtE+t0zM3iWJvpfPvsNaVyQ0Sar6nIe9tQXIQMm7k7OY+q+Lsge3bVACuSa9r19Js+lNuTtEhehWkpe1iECelHmzmhzDsQTDnApIcnWRz3oFR68UX1ag8jfk/SKuopTriICAnpR5s5ocw7EEw5wKSHJ1kc96BUevFF9WoPI35P0irqKGAClRVRUAACAAQAAgAAAAIABAAAAAAAAACICAybuTs5j6r4uyB7dtUAK5Jr2vX0mz6U25O0SF6FaSl7WGAmqQRNUAACAAQAAgAAAAIABAAAAAAAAACICA7cr8fTHOPtE+t0zM3iWJvpfPvsNaVyQ0Sar6nIe9tQXGNm763pUAACAAQAAgAAAAIABAAAAAAAAAAAA" . to_string ( ) )
1139
- . unwrap ( ) ;
1407
+ let psbt = sample_psbt ( ) ;
1140
1408
let psbt_utxo = psbt. spend_utxo ( 0 ) ;
1141
-
1142
1409
println ! ( "Psbt utxo: {:?}" , psbt_utxo) ;
1143
1410
1144
1411
assert_eq ! (
@@ -1147,4 +1414,18 @@ mod tests {
1147
1414
"Psbt utxo does not match the expected value"
1148
1415
) ;
1149
1416
}
1417
+
1418
+ #[ test]
1419
+ fn test_psbt_input ( ) {
1420
+ let psbt = sample_psbt ( ) ;
1421
+ let psbt_inputs = psbt. input ( ) ;
1422
+ println ! ( "Psbt Input: {:?}" , psbt_inputs) ;
1423
+
1424
+ assert_eq ! ( psbt_inputs. len( ) , 1 ) ;
1425
+ }
1426
+
1427
+ fn sample_psbt ( ) -> Psbt {
1428
+ Psbt :: new ( "cHNidP8BAH0CAAAAAXHl8cCbj84lm1v42e54IGI6CQru/nBXwrPE3q2fiGO4AAAAAAD9////Ar4DAAAAAAAAIgAgYw/rnGd4Bifj8s7TaMgR2tal/lq+L1jVv2Sqd1mxMbJEEQAAAAAAABYAFNVpt8vHYUPZNSF6Hu07uP1YeHts4QsAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BAJ+CwD/////AkAlAAAAAAAAIgAgQyrnn86L9D3vDiH959KJbPudDHc/bp6nI9E5EBLQD1YAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErQCUAAAAAAAAiACBDKuefzov0Pe8OIf3n0ols+50Mdz9unqcj0TkQEtAPViICAy4V+d/Qff71zzPXxK4FWG5x+wL/Ku93y/LG5p+0rI2xSDBFAiEA9b0OdASAs0P2uhQinjN7QGP5jX/b32LcShBmny8U0RUCIBebxvCDbpchCjqLAhOMjydT80DAzokaalGzV7XVTsbiASICA1tMY+46EgxIHU18bgHnUvAAlAkMq5LfwkpOGZ97sDKRRzBEAiBpmlZwJocNEiKLxexEX0Par6UgG8a89AklTG3/z9AHlAIgQH/ybCvfKJzr2dq0+IyueDebm7FamKIJdzBYWMXRr/wBIgID+aCzK9nclwhbbN7KbIVGUQGLWZsjcaqWPxk9gFeG+FxIMEUCIQDRPBzb0i9vaUmxCcs1yz8uq4tq1mdDAYvvYn3isKEhFAIgfmeTLLzMo0mmQ23ooMnyx6iPceE8xV5CvARuJsd88tEBAQVpUiEDW0xj7joSDEgdTXxuAedS8ACUCQyrkt/CSk4Zn3uwMpEhAy4V+d/Qff71zzPXxK4FWG5x+wL/Ku93y/LG5p+0rI2xIQP5oLMr2dyXCFts3spshUZRAYtZmyNxqpY/GT2AV4b4XFOuIgYDLhX539B9/vXPM9fErgVYbnH7Av8q73fL8sbmn7SsjbEYCapBE1QAAIABAACAAAAAgAAAAAAAAAAAIgYDW0xj7joSDEgdTXxuAedS8ACUCQyrkt/CSk4Zn3uwMpEY2bvrelQAAIABAACAAAAAgAAAAAAAAAAAIgYD+aCzK9nclwhbbN7KbIVGUQGLWZsjcaqWPxk9gFeG+FwYAKVFVFQAAIABAACAAAAAgAAAAAAAAAAAAAEBaVIhA7cr8fTHOPtE+t0zM3iWJvpfPvsNaVyQ0Sar6nIe9tQXIQMm7k7OY+q+Lsge3bVACuSa9r19Js+lNuTtEhehWkpe1iECelHmzmhzDsQTDnApIcnWRz3oFR68UX1ag8jfk/SKuopTriICAnpR5s5ocw7EEw5wKSHJ1kc96BUevFF9WoPI35P0irqKGAClRVRUAACAAQAAgAAAAIABAAAAAAAAACICAybuTs5j6r4uyB7dtUAK5Jr2vX0mz6U25O0SF6FaSl7WGAmqQRNUAACAAQAAgAAAAIABAAAAAAAAACICA7cr8fTHOPtE+t0zM3iWJvpfPvsNaVyQ0Sar6nIe9tQXGNm763pUAACAAQAAgAAAAIABAAAAAAAAAAAA" . to_string ( ) )
1429
+ . unwrap ( )
1430
+ }
1150
1431
}
0 commit comments