use std::error::Error;
use bitcoin::psbt::Psbt;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::bip32::ExtendedPrivKey;
use bitcoin::psbt::PartiallySignedTransaction;
use anyhow::Result;
pub struct BIP380 {
secp: Secp256k1<bitcoin::secp256k1::All>,
}
impl BIP380 {
pub fn new() -> Self {
Self {
secp: Secp256k1::new(),
}
}
pub fn extend_psbt(&self, psbt: &mut PartiallySignedTransaction, xpriv: &ExtendedPrivKey) -> Result<()> {
log::info!("Extending PSBT with BIP-380 descriptor wallet information");
if psbt.inputs.is_empty() {
return Err(anyhow::anyhow!("Cannot extend PSBT with no inputs"));
}
for (index, input) in psbt.inputs.iter_mut().enumerate() {
if input.bip32_derivation.is_empty() {
log::debug!("Adding BIP32 derivation for input {}", index);
let child_key = xpriv.derive_priv(&self.secp, &[index as u32])
.map_err(|e| anyhow::anyhow!("Failed to derive key: {}", e))?;
let public_key = child_key.private_key.public_key(&self.secp);
let derivation_path = bitcoin::bip32::DerivationPath::from(vec![
bitcoin::bip32::ChildNumber::from_hardened_idx(44).unwrap(), bitcoin::bip32::ChildNumber::from_hardened_idx(0).unwrap(), bitcoin::bip32::ChildNumber::from_hardened_idx(0).unwrap(), bitcoin::bip32::ChildNumber::from_normal_idx(0).unwrap(), bitcoin::bip32::ChildNumber::from_normal_idx(index as u32).unwrap(), ]);
input.bip32_derivation.insert(
public_key,
(xpriv.fingerprint(&self.secp), derivation_path)
);
log::debug!("Added BIP32 derivation for pubkey: {:?}", public_key);
}
if input.proprietary.is_empty() {
let descriptor_key = bitcoin::psbt::raw::ProprietaryKey {
prefix: b"descriptor".to_vec(),
subtype: 0x00,
key: b"wallet_descriptor".to_vec(),
};
let descriptor_value = format!("wpkh([{}/44'/0'/0']{}/*)",
xpriv.fingerprint(&self.secp),
xpriv.private_key.public_key(&self.secp)
).into_bytes();
input.proprietary.insert(descriptor_key, descriptor_value);
log::debug!("Added descriptor information for input {}", index);
}
}
for (index, output) in psbt.outputs.iter_mut().enumerate() {
if output.bip32_derivation.is_empty() {
log::debug!("Adding output derivation for output {}", index);
let child_key = xpriv.derive_priv(&self.secp, &[1, index as u32])
.map_err(|e| anyhow::anyhow!("Failed to derive change key: {}", e))?;
let public_key = child_key.private_key.public_key(&self.secp);
let derivation_path = bitcoin::bip32::DerivationPath::from(vec![
bitcoin::bip32::ChildNumber::from_hardened_idx(44).unwrap(),
bitcoin::bip32::ChildNumber::from_hardened_idx(0).unwrap(),
bitcoin::bip32::ChildNumber::from_hardened_idx(0).unwrap(),
bitcoin::bip32::ChildNumber::from_normal_idx(1).unwrap(), bitcoin::bip32::ChildNumber::from_normal_idx(index as u32).unwrap(),
]);
output.bip32_derivation.insert(
public_key,
(xpriv.fingerprint(&self.secp), derivation_path)
);
}
}
log::info!("BIP-380 PSBT extension completed for {} inputs and {} outputs",
psbt.inputs.len(), psbt.outputs.len());
Ok(())
}
pub fn migrate_from_bip174(&self, psbt: &PartiallySignedTransaction) -> Result<PartiallySignedTransaction> {
log::info!("Migrating PSBT from BIP-174 to BIP-380 format");
let mut migrated_psbt = psbt.clone();
for (index, input) in migrated_psbt.inputs.iter_mut().enumerate() {
let mut descriptors_to_migrate = Vec::new();
for (prop_key, prop_value) in &input.proprietary {
if prop_key.prefix == b"desc" || prop_key.prefix == b"descriptor" {
descriptors_to_migrate.push((prop_key.clone(), prop_value.clone()));
}
}
for (old_key, value) in descriptors_to_migrate {
input.proprietary.remove(&old_key);
let new_key = bitcoin::psbt::raw::ProprietaryKey {
prefix: b"bip380".to_vec(),
subtype: 0x00,
key: format!("descriptor_input_{}", index).into_bytes(),
};
input.proprietary.insert(new_key, value);
log::debug!("Migrated descriptor for input {}", index);
}
}
for (index, output) in migrated_psbt.outputs.iter_mut().enumerate() {
let mut descriptors_to_migrate = Vec::new();
for (prop_key, prop_value) in &output.proprietary {
if prop_key.prefix == b"desc" || prop_key.prefix == b"descriptor" {
descriptors_to_migrate.push((prop_key.clone(), prop_value.clone()));
}
}
for (old_key, value) in descriptors_to_migrate {
output.proprietary.remove(&old_key);
let new_key = bitcoin::psbt::raw::ProprietaryKey {
prefix: b"bip380".to_vec(),
subtype: 0x01,
key: format!("descriptor_output_{}", index).into_bytes(),
};
output.proprietary.insert(new_key, value);
log::debug!("Migrated descriptor for output {}", index);
}
}
let version_key = bitcoin::psbt::raw::ProprietaryKey {
prefix: b"bip380".to_vec(),
subtype: 0xFF,
key: b"version".to_vec(),
};
migrated_psbt.proprietary.insert(version_key, b"1.0".to_vec());
log::info!("BIP-174 to BIP-380 migration completed");
Ok(migrated_psbt)
}
pub fn validate_bip380_compliance(&self, psbt: &PartiallySignedTransaction) -> Result<bool> {
log::info!("Validating BIP-380 compliance");
let version_key = bitcoin::psbt::raw::ProprietaryKey {
prefix: b"bip380".to_vec(),
subtype: 0xFF,
key: b"version".to_vec(),
};
if !psbt.proprietary.contains_key(&version_key) {
log::warn!("PSBT missing BIP-380 version marker");
return Ok(false);
}
let mut descriptor_count = 0;
for input in &psbt.inputs {
for (prop_key, _) in &input.proprietary {
if prop_key.prefix == b"bip380" && prop_key.subtype == 0x00 {
descriptor_count += 1;
}
}
}
log::info!("BIP-380 compliance validation completed - {} descriptors found", descriptor_count);
Ok(true)
}
}