@@ -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,249 @@ 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
+ }
568
+ /// A key-value map for an input of the corresponding index in the unsigned transaction.
569
+ #[ derive( Clone , Debug , uniffi:: Record ) ]
570
+ pub struct Input {
571
+ /// The non-witness transaction this input spends from. Should only be
572
+ /// `Option::Some` for inputs which spend non-segwit outputs or
573
+ /// if it is unknown whether an input spends a segwit output.
574
+ pub non_witness_utxo : Option < Arc < Transaction > > ,
575
+ /// The transaction output this input spends from. Should only be
576
+ /// `Option::Some` for inputs which spend segwit outputs,
577
+ /// including P2SH embedded ones.
578
+ pub witness_utxo : Option < TxOut > ,
579
+ /// A map from public keys to their corresponding signature as would be
580
+ // pushed to the stack from a scriptSig or witness for a non-taproot inputs.
581
+ pub partial_sigs : Vec < PartialSig > ,
582
+ /// The sighash type to be used for this input. Signatures for this input
583
+ /// must use the sighash type.
584
+ pub sighash_type : Option < String > ,
585
+ /// The redeem script for this input.
586
+ pub redeem_script : Option < Arc < Script > > ,
587
+ /// The witness script for this input.
588
+ pub witness_script : Option < Arc < Script > > ,
589
+ /// A map from public keys needed to sign this input to their corresponding
590
+ /// master key fingerprints and derivation paths.
591
+ pub bip32_derivation : Vec < Bip32Derivation > ,
592
+
593
+ /// The finalized, fully-constructed scriptSig with signatures and any other
594
+ /// scripts necessary for this input to pass validation.
595
+ pub final_script_sig : Option < Arc < Script > > ,
596
+
597
+ /// The finalized, fully-constructed scriptWitness with signatures and any
598
+ /// other scripts necessary for this input to pass validation.
599
+ pub final_script_witness : Option < Vec < Vec < u8 > > > ,
600
+ /// RIPEMD160 hash to preimage map.
601
+ pub ripemd160_preimages : Vec < PreimagePair > ,
602
+ /// SHA256 hash to preimage map.
603
+ pub sha256_preimages : Vec < PreimagePair > ,
604
+ /// HSAH160 hash to preimage map.
605
+ pub hash160_preimages : Vec < PreimagePair > ,
606
+ /// HAS256 hash to preimage map.
607
+ pub hash256_preimages : Vec < PreimagePair > ,
608
+ /// Serialized taproot signature with sighash type for key spend.
609
+ pub tap_key_sig : Option < Vec < u8 > > ,
610
+ /// Map of `<xonlypubkey>|<leafhash>` with signature.
611
+ pub tap_script_sigs : Vec < TapScriptSig > ,
612
+ /// Map of Control blocks to Script version pair.
613
+ pub tap_scripts : Vec < TapScriptEntry > ,
614
+ /// Map of tap root x only keys to origin info and leaf hashes contained in it.
615
+ pub tap_key_origins : Vec < TapKeyOrigin > ,
616
+ /// Taproot Internal key.
617
+ pub tap_internal_key : Option < String > ,
618
+ /// Taproot Merkle root.
619
+ pub tap_merkle_root : Option < String > ,
620
+ /// Proprietary key-value pairs for this input.
621
+ pub proprietary : Vec < KeyValuePair > ,
622
+ /// Unknown key-value pairs for this input.
623
+ pub unknown : Vec < KeyValuePair > ,
624
+ }
625
+
626
+ impl From < & BdkInput > for Input {
627
+ fn from ( input : & BdkInput ) -> Self {
628
+ Input {
629
+ non_witness_utxo : input
630
+ . non_witness_utxo
631
+ . as_ref ( )
632
+ . map ( |tx| Arc :: new ( Transaction ( tx. clone ( ) ) ) ) ,
633
+ witness_utxo : input. witness_utxo . as_ref ( ) . map ( TxOut :: from) ,
634
+ partial_sigs : input
635
+ . partial_sigs
636
+ . iter ( )
637
+ . map ( |( k, v) | PartialSig {
638
+ pubkey : k. to_string ( ) ,
639
+ signature : v. to_vec ( ) ,
640
+ } )
641
+ . collect :: < Vec < PartialSig > > ( ) ,
642
+ sighash_type : input. sighash_type . as_ref ( ) . map ( |s| s. to_string ( ) ) ,
643
+ redeem_script : input
644
+ . redeem_script
645
+ . as_ref ( )
646
+ . map ( |s| Arc :: new ( Script ( s. clone ( ) ) ) ) ,
647
+ witness_script : input
648
+ . witness_script
649
+ . as_ref ( )
650
+ . map ( |s| Arc :: new ( Script ( s. clone ( ) ) ) ) ,
651
+ bip32_derivation : input
652
+ . bip32_derivation
653
+ . iter ( )
654
+ . map ( |( k, v) | Bip32Derivation {
655
+ pubkey : k. to_string ( ) ,
656
+ fingerprint : v. 0 . to_string ( ) ,
657
+ path : v. 1 . to_string ( ) ,
658
+ } )
659
+ . collect :: < Vec < Bip32Derivation > > ( ) ,
660
+ final_script_sig : input
661
+ . final_script_sig
662
+ . as_ref ( )
663
+ . map ( |s| Arc :: new ( Script ( s. clone ( ) ) ) ) ,
664
+ final_script_witness : input. final_script_witness . as_ref ( ) . map ( |w| w. to_vec ( ) ) ,
665
+ ripemd160_preimages : input
666
+ . ripemd160_preimages
667
+ . iter ( )
668
+ . map ( |( k, v) | PreimagePair {
669
+ hash : k. to_string ( ) ,
670
+ preimage : v. to_vec ( ) ,
671
+ } )
672
+ . collect :: < Vec < PreimagePair > > ( ) ,
673
+ sha256_preimages : input
674
+ . sha256_preimages
675
+ . iter ( )
676
+ . map ( |( k, v) | PreimagePair {
677
+ hash : k. to_string ( ) ,
678
+ preimage : v. to_vec ( ) ,
679
+ } )
680
+ . collect :: < Vec < PreimagePair > > ( ) ,
681
+ hash160_preimages : input
682
+ . hash160_preimages
683
+ . iter ( )
684
+ . map ( |( k, v) | PreimagePair {
685
+ hash : k. to_string ( ) ,
686
+ preimage : v. to_vec ( ) ,
687
+ } )
688
+ . collect :: < Vec < PreimagePair > > ( ) ,
689
+ hash256_preimages : input
690
+ . hash256_preimages
691
+ . iter ( )
692
+ . map ( |( k, v) | PreimagePair {
693
+ hash : k. to_string ( ) ,
694
+ preimage : v. to_vec ( ) ,
695
+ } )
696
+ . collect :: < Vec < PreimagePair > > ( ) ,
697
+ tap_key_sig : input. tap_key_sig . as_ref ( ) . map ( |s| s. serialize ( ) . to_vec ( ) ) ,
698
+ tap_script_sigs : input
699
+ . tap_script_sigs
700
+ . iter ( )
701
+ . map ( |( k, v) | TapScriptSig {
702
+ xonly_pubkey : k. 0 . to_string ( ) ,
703
+ leaf_hash : k. 1 . to_string ( ) ,
704
+ signature : v. to_vec ( ) ,
705
+ } )
706
+ . collect :: < Vec < TapScriptSig > > ( ) ,
707
+ tap_scripts : input
708
+ . tap_scripts
709
+ . iter ( )
710
+ . map ( |( k, v) | TapScriptEntry {
711
+ control_block : DisplayHex :: to_lower_hex_string ( & k. serialize ( ) ) ,
712
+ script : Arc :: new ( v. 0 . clone ( ) . into ( ) ) ,
713
+ leaf_version : v. 1 . to_consensus ( ) ,
714
+ } )
715
+ . collect :: < Vec < TapScriptEntry > > ( ) ,
716
+ tap_key_origins : input
717
+ . tap_key_origins
718
+ . iter ( )
719
+ . map ( |( k, v) | TapKeyOrigin {
720
+ xonly_pubkey : k. to_string ( ) ,
721
+ leaf_hashes : v. 0 . iter ( ) . map ( |h| h. to_string ( ) ) . collect ( ) ,
722
+ fingerprint : v. 1 . 0 . to_string ( ) ,
723
+ path : v. 1 . 1 . to_string ( ) ,
724
+ } )
725
+ . collect :: < Vec < TapKeyOrigin > > ( ) ,
726
+ tap_internal_key : input. tap_internal_key . as_ref ( ) . map ( |k| k. to_string ( ) ) ,
727
+ tap_merkle_root : input. tap_merkle_root . as_ref ( ) . map ( |k| k. to_string ( ) ) ,
728
+ proprietary : input
729
+ . proprietary
730
+ . iter ( )
731
+ . map ( |( k, v) | KeyValuePair {
732
+ key : k. to_key ( ) . key . clone ( ) ,
733
+ value : v. to_vec ( ) ,
734
+ } )
735
+ . collect ( ) ,
736
+ unknown : input
737
+ . unknown
738
+ . iter ( )
739
+ . map ( |( k, v) | KeyValuePair {
740
+ key : k. key . clone ( ) ,
741
+ value : v. to_vec ( ) ,
742
+ } )
743
+ . collect ( ) ,
744
+ }
745
+ }
746
+ }
747
+
503
748
/// A Partially Signed Transaction.
504
749
#[ derive( uniffi:: Object ) ]
505
750
pub struct Psbt ( pub ( crate ) Mutex < BdkPsbt > ) ;
@@ -625,6 +870,12 @@ impl Psbt {
625
870
let utxo = psbt. spend_utxo ( input_index as usize ) . unwrap ( ) ;
626
871
serde_json:: to_string ( & utxo) . unwrap ( )
627
872
}
873
+
874
+ /// The corresponding key-value map for each input in the unsigned transaction.
875
+ pub fn input ( & self ) -> Vec < Input > {
876
+ let psbt = self . 0 . lock ( ) . unwrap ( ) ;
877
+ psbt. inputs . iter ( ) . map ( |input| input. into ( ) ) . collect ( )
878
+ }
628
879
}
629
880
630
881
impl From < BdkPsbt > for Psbt {
@@ -1135,10 +1386,8 @@ mod tests {
1135
1386
1136
1387
#[ test]
1137
1388
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 ( ) ;
1389
+ let psbt = sample_psbt ( ) ;
1140
1390
let psbt_utxo = psbt. spend_utxo ( 0 ) ;
1141
-
1142
1391
println ! ( "Psbt utxo: {:?}" , psbt_utxo) ;
1143
1392
1144
1393
assert_eq ! (
@@ -1147,4 +1396,18 @@ mod tests {
1147
1396
"Psbt utxo does not match the expected value"
1148
1397
) ;
1149
1398
}
1399
+
1400
+ #[ test]
1401
+ fn test_psbt_input ( ) {
1402
+ let psbt = sample_psbt ( ) ;
1403
+ let psbt_inputs = psbt. input ( ) ;
1404
+ println ! ( "Psbt Input: {:?}" , psbt_inputs) ;
1405
+
1406
+ assert_eq ! ( psbt_inputs. len( ) , 1 ) ;
1407
+ }
1408
+
1409
+ fn sample_psbt ( ) -> Psbt {
1410
+ 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 ( ) )
1411
+ . unwrap ( )
1412
+ }
1150
1413
}
0 commit comments