Skip to content

Commit 86aea5d

Browse files
kumulynjathunderbiscuit
authored andcommitted
feat: expose only_witness_utxo and add_foreign_utxo on TxBuilder
1 parent 7e76be6 commit 86aea5d

File tree

4 files changed

+952
-7
lines changed

4 files changed

+952
-7
lines changed

bdk-ffi/src/bitcoin.rs

Lines changed: 336 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ use bdk_wallet::bitcoin::io::Cursor;
2020
use bdk_wallet::bitcoin::psbt::Input as BdkInput;
2121
use bdk_wallet::bitcoin::psbt::Output as BdkOutput;
2222
use bdk_wallet::bitcoin::secp256k1::Secp256k1;
23-
use std::collections::HashMap;
24-
use std::convert::TryFrom;
2523

2624
use bdk_wallet::bitcoin::bip32::ChildNumber as BdkChildNumber;
2725
use bdk_wallet::bitcoin::taproot::LeafNode as BdkLeafNode;
@@ -42,6 +40,8 @@ use bdk_wallet::bitcoin::Wtxid as BitcoinWtxid;
4240
use bdk_wallet::miniscript::psbt::PsbtExt;
4341
use bdk_wallet::serde_json;
4442

43+
use std::collections::HashMap;
44+
use std::convert::TryFrom;
4545
use std::fmt::Display;
4646
use std::fs::File;
4747
use std::io::{BufReader, BufWriter};
@@ -692,6 +692,8 @@ pub struct Input {
692692
pub unknown: HashMap<Key, Vec<u8>>,
693693
}
694694

695+
use crate::error::AddForeignUtxoError;
696+
695697
impl From<&BdkInput> for Input {
696698
fn from(input: &BdkInput) -> Self {
697699
Input {
@@ -832,6 +834,338 @@ impl From<&BdkInput> for Input {
832834
}
833835
}
834836

837+
impl TryFrom<Input> for BdkInput {
838+
type Error = AddForeignUtxoError;
839+
840+
fn try_from(input: Input) -> Result<Self, Self::Error> {
841+
use bdk_wallet::bitcoin::ecdsa;
842+
use bdk_wallet::bitcoin::hashes::Hash as HashTrait;
843+
use bdk_wallet::bitcoin::key::PublicKey as Secp256k1PublicKey;
844+
use bdk_wallet::bitcoin::psbt::PsbtSighashType;
845+
use bdk_wallet::bitcoin::secp256k1::XOnlyPublicKey;
846+
use bdk_wallet::bitcoin::taproot::{
847+
ControlBlock as BdkControlBlock, LeafVersion, TapLeafHash, TapNodeHash,
848+
};
849+
use std::str::FromStr;
850+
851+
let non_witness_utxo = input.non_witness_utxo.map(|tx| tx.0.clone());
852+
853+
let witness_utxo = input.witness_utxo.map(|txout| txout.into());
854+
855+
let partial_sigs = input
856+
.partial_sigs
857+
.into_iter()
858+
.map(|(k, v)| {
859+
let pubkey = Secp256k1PublicKey::from_str(&k).map_err(|e| {
860+
AddForeignUtxoError::InputConversionError {
861+
error_message: format!("invalid public key in partial_sigs: {}", e),
862+
}
863+
})?;
864+
let sig = ecdsa::Signature::from_slice(&v).map_err(|e| {
865+
AddForeignUtxoError::InputConversionError {
866+
error_message: format!("invalid signature in partial_sigs: {}", e),
867+
}
868+
})?;
869+
Ok((pubkey, sig))
870+
})
871+
.collect::<Result<std::collections::BTreeMap<_, _>, AddForeignUtxoError>>()?;
872+
873+
let sighash_type = input
874+
.sighash_type
875+
.map(|s| {
876+
PsbtSighashType::from_str(&s).map_err(|e| {
877+
AddForeignUtxoError::InputConversionError {
878+
error_message: format!("invalid sighash type: {}", e),
879+
}
880+
})
881+
})
882+
.transpose()?;
883+
884+
let redeem_script = input.redeem_script.map(|s| s.0.clone());
885+
let witness_script = input.witness_script.map(|s| s.0.clone());
886+
887+
let bip32_derivation = input
888+
.bip32_derivation
889+
.into_iter()
890+
.map(|(k, v)| {
891+
use bdk_wallet::bitcoin::bip32::{DerivationPath, Fingerprint};
892+
use bdk_wallet::bitcoin::secp256k1::PublicKey as Secp256k1RawPublicKey;
893+
let pubkey = Secp256k1RawPublicKey::from_str(&k).map_err(|e| {
894+
AddForeignUtxoError::InputConversionError {
895+
error_message: format!("invalid public key in bip32_derivation: {}", e),
896+
}
897+
})?;
898+
let fingerprint = Fingerprint::from_str(&v.fingerprint).map_err(|e| {
899+
AddForeignUtxoError::InputConversionError {
900+
error_message: format!("invalid fingerprint: {}", e),
901+
}
902+
})?;
903+
let path: DerivationPath = v.path.0.clone();
904+
Ok((pubkey, (fingerprint, path)))
905+
})
906+
.collect::<Result<std::collections::BTreeMap<_, _>, AddForeignUtxoError>>()?;
907+
908+
let final_script_sig = input.final_script_sig.map(|s| s.0.clone());
909+
910+
let final_script_witness = input.final_script_witness.map(|w| {
911+
use bdk_wallet::bitcoin::Witness;
912+
Witness::from_slice(&w)
913+
});
914+
915+
let ripemd160_preimages = input
916+
.ripemd160_preimages
917+
.into_iter()
918+
.map(|(k, v)| {
919+
use bdk_wallet::bitcoin::hashes::ripemd160;
920+
let hash = ripemd160::Hash::from_str(&k).map_err(|e| {
921+
AddForeignUtxoError::InputConversionError {
922+
error_message: format!("invalid ripemd160 hash: {}", e),
923+
}
924+
})?;
925+
Ok((hash, v))
926+
})
927+
.collect::<Result<_, AddForeignUtxoError>>()?;
928+
929+
let sha256_preimages = input
930+
.sha256_preimages
931+
.into_iter()
932+
.map(|(k, v)| {
933+
use bdk_wallet::bitcoin::hashes::sha256;
934+
let hash = sha256::Hash::from_str(&k).map_err(|e| {
935+
AddForeignUtxoError::InputConversionError {
936+
error_message: format!("invalid sha256 hash: {}", e),
937+
}
938+
})?;
939+
Ok((hash, v))
940+
})
941+
.collect::<Result<_, AddForeignUtxoError>>()?;
942+
943+
let hash160_preimages = input
944+
.hash160_preimages
945+
.into_iter()
946+
.map(|(k, v)| {
947+
use bdk_wallet::bitcoin::hashes::hash160;
948+
let hash = hash160::Hash::from_str(&k).map_err(|e| {
949+
AddForeignUtxoError::InputConversionError {
950+
error_message: format!("invalid hash160: {}", e),
951+
}
952+
})?;
953+
Ok((hash, v))
954+
})
955+
.collect::<Result<_, AddForeignUtxoError>>()?;
956+
957+
let hash256_preimages = input
958+
.hash256_preimages
959+
.into_iter()
960+
.map(|(k, v)| {
961+
use bdk_wallet::bitcoin::hashes::sha256d;
962+
let hash = sha256d::Hash::from_str(&k).map_err(|e| {
963+
AddForeignUtxoError::InputConversionError {
964+
error_message: format!("invalid hash256: {}", e),
965+
}
966+
})?;
967+
Ok((hash, v))
968+
})
969+
.collect::<Result<_, AddForeignUtxoError>>()?;
970+
971+
let tap_key_sig = input
972+
.tap_key_sig
973+
.map(|s| {
974+
use bdk_wallet::bitcoin::taproot::Signature;
975+
Signature::from_slice(&s).map_err(|e| AddForeignUtxoError::InputConversionError {
976+
error_message: format!("invalid taproot signature: {}", e),
977+
})
978+
})
979+
.transpose()?;
980+
981+
let tap_script_sigs = input
982+
.tap_script_sigs
983+
.into_iter()
984+
.map(|(k, v)| {
985+
use bdk_wallet::bitcoin::taproot::Signature;
986+
let xonly = XOnlyPublicKey::from_str(&k.xonly_pubkey).map_err(|e| {
987+
AddForeignUtxoError::InputConversionError {
988+
error_message: format!("invalid xonly pubkey: {}", e),
989+
}
990+
})?;
991+
let leaf_hash = TapLeafHash::from_str(&k.tap_leaf_hash).map_err(|e| {
992+
AddForeignUtxoError::InputConversionError {
993+
error_message: format!("invalid tap leaf hash: {}", e),
994+
}
995+
})?;
996+
let sig = Signature::from_slice(&v).map_err(|e| {
997+
AddForeignUtxoError::InputConversionError {
998+
error_message: format!("invalid taproot script signature: {}", e),
999+
}
1000+
})?;
1001+
Ok(((xonly, leaf_hash), sig))
1002+
})
1003+
.collect::<Result<_, AddForeignUtxoError>>()?;
1004+
1005+
let tap_scripts = input
1006+
.tap_scripts
1007+
.into_iter()
1008+
.map(|(k, v)| {
1009+
use bdk_wallet::bitcoin::key::XOnlyPublicKey as BdkXOnlyPublicKey;
1010+
use bdk_wallet::bitcoin::taproot::TapNodeHash;
1011+
1012+
let internal_key = BdkXOnlyPublicKey::from_slice(&k.internal_key).map_err(|e| {
1013+
AddForeignUtxoError::InputConversionError {
1014+
error_message: format!("invalid internal key: {}", e),
1015+
}
1016+
})?;
1017+
1018+
let output_key_parity = k.output_key_parity;
1019+
let leaf_version_u8 = k.leaf_version;
1020+
1021+
let merkle_branch: Vec<TapNodeHash> = k
1022+
.merkle_branch
1023+
.into_iter()
1024+
.map(|h| {
1025+
TapNodeHash::from_str(&h).map_err(|e| {
1026+
AddForeignUtxoError::InputConversionError {
1027+
error_message: format!("invalid merkle branch hash: {}", e),
1028+
}
1029+
})
1030+
})
1031+
.collect::<Result<_, AddForeignUtxoError>>()?;
1032+
1033+
let mut control_block_bytes = vec![output_key_parity | leaf_version_u8];
1034+
control_block_bytes.extend_from_slice(&internal_key.serialize());
1035+
for hash in &merkle_branch {
1036+
control_block_bytes.extend_from_slice(&hash.to_byte_array());
1037+
}
1038+
1039+
let control_block = BdkControlBlock::decode(&control_block_bytes).map_err(|e| {
1040+
AddForeignUtxoError::InputConversionError {
1041+
error_message: format!("invalid control block: {}", e),
1042+
}
1043+
})?;
1044+
1045+
let leaf_version = LeafVersion::from_consensus(leaf_version_u8).map_err(|_| {
1046+
AddForeignUtxoError::InputConversionError {
1047+
error_message: format!("invalid leaf version: {}", leaf_version_u8),
1048+
}
1049+
})?;
1050+
1051+
Ok((control_block, (v.script.0.clone(), leaf_version)))
1052+
})
1053+
.collect::<Result<_, AddForeignUtxoError>>()?;
1054+
1055+
let tap_key_origins = input
1056+
.tap_key_origins
1057+
.into_iter()
1058+
.map(|(k, v)| {
1059+
use bdk_wallet::bitcoin::bip32::{DerivationPath, Fingerprint};
1060+
1061+
let xonly = XOnlyPublicKey::from_str(&k).map_err(|e| {
1062+
AddForeignUtxoError::InputConversionError {
1063+
error_message: format!("invalid xonly pubkey in tap_key_origins: {}", e),
1064+
}
1065+
})?;
1066+
1067+
let leaf_hashes: Vec<TapLeafHash> = v
1068+
.tap_leaf_hashes
1069+
.into_iter()
1070+
.map(|h| {
1071+
TapLeafHash::from_str(&h).map_err(|e| {
1072+
AddForeignUtxoError::InputConversionError {
1073+
error_message: format!("invalid tap leaf hash: {}", e),
1074+
}
1075+
})
1076+
})
1077+
.collect::<Result<_, AddForeignUtxoError>>()?;
1078+
1079+
let fingerprint =
1080+
Fingerprint::from_str(&v.key_source.fingerprint).map_err(|e| {
1081+
AddForeignUtxoError::InputConversionError {
1082+
error_message: format!("invalid fingerprint in tap_key_origins: {}", e),
1083+
}
1084+
})?;
1085+
1086+
let path: DerivationPath = v.key_source.path.0.clone();
1087+
1088+
Ok((xonly, (leaf_hashes, (fingerprint, path))))
1089+
})
1090+
.collect::<Result<_, AddForeignUtxoError>>()?;
1091+
1092+
let tap_internal_key = input
1093+
.tap_internal_key
1094+
.map(|k| {
1095+
XOnlyPublicKey::from_str(&k).map_err(|e| {
1096+
AddForeignUtxoError::InputConversionError {
1097+
error_message: format!("invalid tap internal key: {}", e),
1098+
}
1099+
})
1100+
})
1101+
.transpose()?;
1102+
1103+
let tap_merkle_root = input
1104+
.tap_merkle_root
1105+
.map(|k| {
1106+
TapNodeHash::from_str(&k).map_err(|e| AddForeignUtxoError::InputConversionError {
1107+
error_message: format!("invalid tap merkle root: {}", e),
1108+
})
1109+
})
1110+
.transpose()?;
1111+
1112+
let proprietary = input
1113+
.proprietary
1114+
.into_iter()
1115+
.map(|(k, v)| {
1116+
use bdk_wallet::bitcoin::psbt::raw::ProprietaryKey as BdkProprietaryKey;
1117+
(
1118+
BdkProprietaryKey {
1119+
prefix: k.prefix,
1120+
subtype: k.subtype,
1121+
key: k.key,
1122+
},
1123+
v,
1124+
)
1125+
})
1126+
.collect();
1127+
1128+
let unknown = input
1129+
.unknown
1130+
.into_iter()
1131+
.map(|(k, v)| {
1132+
use bdk_wallet::bitcoin::psbt::raw::Key as BdkKey;
1133+
(
1134+
BdkKey {
1135+
type_value: k.type_value,
1136+
key: k.key,
1137+
},
1138+
v,
1139+
)
1140+
})
1141+
.collect();
1142+
1143+
Ok(BdkInput {
1144+
non_witness_utxo,
1145+
witness_utxo,
1146+
partial_sigs,
1147+
sighash_type,
1148+
redeem_script,
1149+
witness_script,
1150+
bip32_derivation,
1151+
final_script_sig,
1152+
final_script_witness,
1153+
ripemd160_preimages,
1154+
sha256_preimages,
1155+
hash160_preimages,
1156+
hash256_preimages,
1157+
tap_key_sig,
1158+
tap_script_sigs,
1159+
tap_scripts,
1160+
tap_key_origins,
1161+
tap_internal_key,
1162+
tap_merkle_root,
1163+
proprietary,
1164+
unknown,
1165+
})
1166+
}
1167+
}
1168+
8351169
/// Store information about taproot leaf node.
8361170
#[derive(Debug, uniffi::Object)]
8371171
#[uniffi::export(Display)]

0 commit comments

Comments
 (0)