1 unstable release
| 0.1.0 | Jan 2, 2026 |
|---|
#20 in #psbt
Used in rustywallet-coinjoin
120KB
2.5K
SLoC
rustywallet-psbt
PSBT (Partially Signed Bitcoin Transaction) implementation for Bitcoin wallet development.
Features
- BIP174 (PSBT v0) - Full support for standard PSBT format
- BIP370 (PSBT v2) - Support for improved PSBT format
- All BIP174 Roles - Creator, Updater, Signer, Combiner, Finalizer, Extractor
- Multiple Input Types - P2PKH, P2WPKH, P2SH, P2WSH, P2TR
- Hardware Wallet Compatible - Interoperable with Ledger, Trezor, Coldcard
Installation
[dependencies]
rustywallet-psbt = "0.1"
Quick Start
Parse PSBT
use rustywallet_psbt::Psbt;
// From base64
let psbt = Psbt::from_base64("cHNidP8BAH...")?;
// From bytes
let psbt = Psbt::from_bytes(&bytes)?;
println!("Inputs: {}", psbt.input_count());
println!("Outputs: {}", psbt.output_count());
Create PSBT
use rustywallet_psbt::Psbt;
// Create from unsigned transaction
let unsigned_tx = vec![/* transaction bytes */];
let psbt = Psbt::from_unsigned_tx(unsigned_tx)?;
// Or create PSBT v2
let psbt = Psbt::new_v2(2, 2); // 2 inputs, 2 outputs
Update PSBT
use rustywallet_psbt::{Psbt, TxOut, KeySource};
let mut psbt = Psbt::from_base64("...")?;
// Add witness UTXO
psbt.update_input_with_utxo(0, TxOut {
value: 100_000,
script_pubkey: vec![0x00, 0x14, /* pubkey hash */],
})?;
// Add BIP32 derivation
psbt.update_input_with_bip32(
0,
pubkey.to_vec(),
KeySource::new([0x01, 0x02, 0x03, 0x04], vec![84 | 0x80000000, 0, 0, 0, 0]),
)?;
Sign PSBT
use rustywallet_psbt::Psbt;
use rustywallet_keys::PrivateKey;
let mut psbt = Psbt::from_base64("...")?;
let private_key = PrivateKey::from_wif("...")?;
// Sign all inputs that match this key
let signed_count = psbt.sign(&private_key)?;
println!("Signed {} inputs", signed_count);
Combine PSBTs
use rustywallet_psbt::Psbt;
let psbt1 = Psbt::from_base64("...")?; // Signed by party 1
let psbt2 = Psbt::from_base64("...")?; // Signed by party 2
// Combine signatures
let combined = Psbt::combine(&[psbt1, psbt2])?;
Finalize and Extract
use rustywallet_psbt::Psbt;
let mut psbt = Psbt::from_base64("...")?;
// Finalize all inputs
psbt.finalize()?;
// Check if finalized
if psbt.is_finalized() {
// Extract signed transaction
let tx = psbt.extract_tx()?;
println!("Transaction: {}", hex::encode(&tx));
}
BIP174 Roles
| Role | Description | Methods |
|---|---|---|
| Creator | Create PSBT from unsigned tx | from_unsigned_tx(), new_v2() |
| Updater | Add UTXO info, scripts, paths | update_input_with_*() |
| Signer | Add partial signatures | sign(), sign_input() |
| Combiner | Merge PSBTs | combine(), merge() |
| Finalizer | Build final scriptSig/witness | finalize(), finalize_input() |
| Extractor | Extract signed transaction | extract_tx() |
Supported Input Types
| Type | Description | Support |
|---|---|---|
| P2PKH | Legacy pay-to-pubkey-hash | ✅ |
| P2WPKH | Native SegWit | ✅ |
| P2SH-P2WPKH | Wrapped SegWit | ✅ |
| P2SH | Pay-to-script-hash | ✅ |
| P2WSH | Native SegWit script | ✅ |
| P2SH-P2WSH | Wrapped SegWit script | ✅ |
| P2TR | Taproot key path | ✅ |
Serialization
// To bytes
let bytes = psbt.to_bytes();
// To base64
let base64 = psbt.to_base64();
// Display (base64)
println!("{}", psbt);
// Parse from string
let psbt: Psbt = "cHNidP8BAH...".parse()?;
Error Handling
use rustywallet_psbt::{Psbt, PsbtError};
match Psbt::from_base64(input) {
Ok(psbt) => { /* success */ }
Err(PsbtError::InvalidMagic) => {
eprintln!("Not a valid PSBT");
}
Err(PsbtError::Base64Error(e)) => {
eprintln!("Invalid base64: {}", e);
}
Err(e) => {
eprintln!("Error: {}", e);
}
}
License
MIT License - see LICENSE for details.
Related Crates
- rustywallet-tx - Transaction building
- rustywallet-keys - Key management
- rustywallet-multisig - Multi-signature wallets
Dependencies
~7.5MB
~105K SLoC