Skip to content

Commit 9e2a1b5

Browse files
committed
feat: Add Input for PSBT
This allows easier access to the inputs of our PSBT
1 parent 8d996a6 commit 9e2a1b5

File tree

1 file changed

+97
-1
lines changed

1 file changed

+97
-1
lines changed

bdk-ffi/src/bitcoin.rs

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use bdk_wallet::bitcoin::consensus::Decodable;
1515
use bdk_wallet::bitcoin::hashes::sha256::Hash as BitcoinSha256Hash;
1616
use bdk_wallet::bitcoin::hashes::sha256d::Hash as BitcoinDoubleSha256Hash;
1717
use bdk_wallet::bitcoin::io::Cursor;
18+
use bdk_wallet::bitcoin::psbt::Input as BdkInput;
1819
use bdk_wallet::bitcoin::secp256k1::Secp256k1;
1920
use bdk_wallet::bitcoin::Amount as BdkAmount;
2021
use bdk_wallet::bitcoin::BlockHash as BitcoinBlockHash;
@@ -482,6 +483,94 @@ impl Display for Transaction {
482483
}
483484
}
484485

486+
#[derive(Clone, Debug, uniffi::Record)]
487+
pub struct PartialSig {
488+
pub pubkey: String,
489+
pub signature: Vec<u8>,
490+
}
491+
492+
#[derive(Clone, Debug, uniffi::Record)]
493+
pub struct Bip32Derivation {
494+
/// hex-encoded public key (serialized)
495+
pub pubkey: String,
496+
/// Full information on the used extended public key: fingerprint of the master extended public key and a derivation path from it.
497+
pub keysource: String,
498+
}
499+
500+
/// A key-value map for an input of the corresponding index in the unsigned transaction.
501+
#[derive(Clone, Debug, uniffi::Record)]
502+
pub struct Input {
503+
/// The non-witness transaction this input spends from. Should only be
504+
/// `Option::Some` for inputs which spend non-segwit outputs or
505+
/// if it is unknown whether an input spends a segwit output.
506+
pub non_witness_utxo: Option<Arc<Transaction>>,
507+
/// The transaction output this input spends from. Should only be
508+
/// `Option::Some` for inputs which spend segwit outputs,
509+
/// including P2SH embedded ones.
510+
pub witness_utxo: Option<TxOut>,
511+
/// A map from public keys to their corresponding signature as would be
512+
// pushed to the stack from a scriptSig or witness for a non-taproot inputs.
513+
pub partial_sigs: Option<Vec<PartialSig>>,
514+
/// The sighash type to be used for this input. Signatures for this input
515+
/// must use the sighash type.
516+
pub sighash_type: Option<String>,
517+
/// The redeem script for this input.
518+
pub redeem_script: Option<Arc<Script>>,
519+
/// The witness script for this input.
520+
pub witness_script: Option<Arc<Script>>,
521+
/// A map from public keys needed to sign this input to their corresponding
522+
/// master key fingerprints and derivation paths.
523+
pub bip32_derivation: Vec<Bip32Derivation>,
524+
}
525+
526+
impl From<&BdkInput> for Input {
527+
fn from(input: &BdkInput) -> Self {
528+
Input {
529+
non_witness_utxo: input
530+
.non_witness_utxo
531+
.as_ref()
532+
.map(|tx| Arc::new(Transaction(tx.clone()))),
533+
witness_utxo: input.witness_utxo.as_ref().map(TxOut::from),
534+
535+
partial_sigs: if input.partial_sigs.is_empty() {
536+
None
537+
} else {
538+
Some(
539+
input
540+
.partial_sigs
541+
.iter()
542+
.map(|(k, v)| PartialSig {
543+
pubkey: k.to_string(),
544+
signature: v.to_vec(), // raw bytes
545+
})
546+
.collect::<Vec<PartialSig>>(),
547+
)
548+
},
549+
sighash_type: input.sighash_type.as_ref().map(|s| s.to_string()),
550+
redeem_script: input
551+
.redeem_script
552+
.as_ref()
553+
.map(|s| Arc::new(Script(s.clone()))),
554+
witness_script: input
555+
.witness_script
556+
.as_ref()
557+
.map(|s| Arc::new(Script(s.clone()))),
558+
bip32_derivation: input
559+
.bip32_derivation
560+
.iter()
561+
.map(|(k, v)| {
562+
let fingerprint = v.0.to_string();
563+
let derivation_path = v.1.to_string();
564+
Bip32Derivation {
565+
pubkey: k.to_string(),
566+
keysource: format!("{}:{}", fingerprint, derivation_path),
567+
}
568+
})
569+
.collect::<Vec<Bip32Derivation>>(),
570+
}
571+
}
572+
}
573+
485574
/// A Partially Signed Transaction.
486575
#[derive(uniffi::Object)]
487576
pub struct Psbt(pub(crate) Mutex<BdkPsbt>);
@@ -607,6 +696,12 @@ impl Psbt {
607696
let utxo = psbt.spend_utxo(input_index as usize).unwrap();
608697
serde_json::to_string(&utxo).unwrap()
609698
}
699+
700+
/// The corresponding key-value map for each input in the unsigned transaction.
701+
pub fn input(&self) -> Vec<Input> {
702+
let psbt = self.0.lock().unwrap();
703+
psbt.inputs.iter().map(|input| input.into()).collect()
704+
}
610705
}
611706

612707
impl From<BdkPsbt> for Psbt {
@@ -1120,7 +1215,8 @@ mod tests {
11201215
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())
11211216
.unwrap();
11221217
let psbt_utxo = psbt.spend_utxo(0);
1123-
1218+
let psbt_inputs = psbt.input();
1219+
println!("Psbt Input: {:?}", psbt_inputs);
11241220
println!("Psbt utxo: {:?}", psbt_utxo);
11251221

11261222
assert_eq!(

0 commit comments

Comments
 (0)