1use std::sync::Arc;
2
3use anyhow::anyhow;
4use bip39::Mnemonic;
5use boltz_client::PublicKey;
6use lwk_common::Signer as LwkSigner;
7use lwk_wollet::bitcoin::bip32::Xpriv;
8use lwk_wollet::bitcoin::Network;
9use lwk_wollet::elements_miniscript::{self, ToPublicKey as _};
10use lwk_wollet::elements_miniscript::{
11 bitcoin::{self, bip32::DerivationPath},
12 elements::{
13 bitcoin::bip32::{self, Fingerprint, Xpub},
14 hashes::Hash,
15 pset::PartiallySignedTransaction,
16 secp256k1_zkp::{All, Secp256k1},
17 sighash::SighashCache,
18 },
19 elementssig_to_rawsig,
20 psbt::PsbtExt,
21 slip77::MasterBlindingKey,
22};
23use lwk_wollet::hashes::{sha256, HashEngine, Hmac, HmacEngine};
24use lwk_wollet::secp256k1::ecdsa::Signature;
25use lwk_wollet::secp256k1::Message;
26
27use crate::model::{Signer, SignerError};
28
29#[derive(thiserror::Error, Debug)]
30pub enum SignError {
31 #[error(transparent)]
32 Pset(#[from] elements_miniscript::elements::pset::Error),
33
34 #[error(transparent)]
35 ElementsEncode(#[from] elements_miniscript::elements::encode::Error),
36
37 #[error(transparent)]
38 Sighash(#[from] elements_miniscript::psbt::SighashError),
39
40 #[error(transparent)]
41 PsetParse(#[from] elements_miniscript::elements::pset::ParseError),
42
43 #[error(transparent)]
44 Bip32(#[from] bip32::Error),
45
46 #[error(transparent)]
47 Generic(#[from] anyhow::Error),
48
49 #[error(transparent)]
50 UserSignerError(#[from] crate::model::SignerError),
51}
52
53#[derive(thiserror::Error, Debug)]
55pub enum NewError {
56 #[error(transparent)]
57 Bip39(#[from] bip39::Error),
58
59 #[error(transparent)]
60 Bip32(#[from] bip32::Error),
61
62 #[error(transparent)]
63 Seed(#[from] anyhow::Error),
64}
65
66pub struct SdkLwkSigner {
68 sdk_signer: Arc<Box<dyn Signer>>,
69}
70
71impl SdkLwkSigner {
72 pub fn new(sdk_signer: Arc<Box<dyn Signer>>) -> Result<Self, NewError> {
77 Ok(Self { sdk_signer })
78 }
79
80 pub(crate) fn xpub(&self) -> Result<Xpub, SignError> {
81 let xpub = self.sdk_signer.xpub()?;
82 Ok(Xpub::decode(&xpub)?)
83 }
84
85 pub fn fingerprint(&self) -> Result<Fingerprint, SignError> {
86 let f: Fingerprint = self.xpub()?.identifier()[0..4]
87 .try_into()
88 .map_err(|_| SignError::Generic(anyhow::anyhow!("Wrong fingerprint length")))?;
89 Ok(f)
90 }
91
92 pub(crate) fn sign_ecdsa_recoverable(&self, msg: &Message) -> Result<Vec<u8>, SignError> {
93 let sig_bytes = self
94 .sdk_signer
95 .sign_ecdsa_recoverable(msg.as_ref().to_vec())?;
96 Ok(sig_bytes)
97 }
98}
99
100impl LwkSigner for SdkLwkSigner {
101 type Error = SignError;
102
103 fn sign(&self, pset: &mut PartiallySignedTransaction) -> Result<u32, Self::Error> {
104 let tx = pset.extract_tx()?;
105 let mut sighash_cache = SighashCache::new(&tx);
106 let mut signature_added = 0;
107
108 let genesis_hash = elements_miniscript::elements::BlockHash::all_zeros();
110 let mut messages = vec![];
111 for i in 0..pset.inputs().len() {
112 let msg = pset
115 .sighash_msg(i, &mut sighash_cache, None, genesis_hash)?
116 .to_secp_msg();
117 messages.push(msg);
118 }
119
120 let hash_ty = elements_miniscript::elements::EcdsaSighashType::All;
122
123 let signer_fingerprint = self.fingerprint()?;
124 for (input, msg) in pset.inputs_mut().iter_mut().zip(messages) {
125 for (want_public_key, (fingerprint, derivation_path)) in input.bip32_derivation.iter() {
126 if &signer_fingerprint == fingerprint {
127 let xpub = self.derive_xpub(derivation_path)?;
128 let public_key: PublicKey = xpub.public_key.into();
129 if want_public_key == &public_key {
130 let sig_bytes = self
132 .sdk_signer
133 .sign_ecdsa(msg.as_ref().to_vec(), derivation_path.to_string())?;
134 let sig = Signature::from_der(&sig_bytes).map_err(|_| {
135 SignError::Generic(anyhow::anyhow!("Invalid esda signature"))
136 })?;
137 let sig = elementssig_to_rawsig(&(sig, hash_ty));
138
139 let inserted = input.partial_sigs.insert(public_key, sig);
140 if inserted.is_none() {
141 signature_added += 1;
142 }
143 }
144 }
145 }
146 }
147
148 Ok(signature_added)
149 }
150
151 fn slip77_master_blinding_key(&self) -> Result<MasterBlindingKey, Self::Error> {
152 let bytes: [u8; 32] = self
153 .sdk_signer
154 .slip77_master_blinding_key()?
155 .try_into()
156 .map_err(|_| {
157 SignError::Generic(anyhow::anyhow!("Wrong slip77 master blinding key length"))
158 })?;
159 Ok(bytes.into())
160 }
161
162 fn derive_xpub(&self, path: &DerivationPath) -> Result<Xpub, Self::Error> {
163 let pubkey_bytes = if path.is_empty() {
164 self.sdk_signer.xpub()?
165 } else {
166 self.sdk_signer.derive_xpub(path.to_string())?
167 };
168 let xpub = Xpub::decode(pubkey_bytes.as_slice())?;
169 Ok(xpub)
170 }
171}
172
173pub struct SdkSigner {
174 xprv: Xpriv,
175 secp: Secp256k1<All>, seed: Vec<u8>,
177 network: Network,
178}
179
180impl SdkSigner {
181 pub fn new(mnemonic: &str, passphrase: &str, is_mainnet: bool) -> Result<Self, NewError> {
182 let mnemonic: Mnemonic = mnemonic.parse()?;
183 let seed = mnemonic.to_seed(passphrase).to_vec();
184
185 Self::new_with_seed(seed, is_mainnet)
186 }
187
188 pub fn new_with_seed(seed: Vec<u8>, is_mainnet: bool) -> Result<Self, NewError> {
189 if seed.len() < 32 {
190 return Err(NewError::Seed(anyhow!("Seed must be at least 32 bytes")));
191 }
192
193 let secp = Secp256k1::new();
194 let network = if is_mainnet {
195 bitcoin::Network::Bitcoin
196 } else {
197 bitcoin::Network::Testnet
198 };
199
200 let xprv = Xpriv::new_master(network, &seed)?;
201
202 Ok(Self {
203 xprv,
204 secp,
205 seed,
206 network,
207 })
208 }
209}
210
211impl Signer for SdkSigner {
212 fn xpub(&self) -> Result<Vec<u8>, SignerError> {
213 Ok(Xpub::from_priv(&self.secp, &self.xprv).encode().to_vec())
214 }
215
216 fn derive_xpub(&self, derivation_path: String) -> Result<Vec<u8>, SignerError> {
217 let der: DerivationPath = derivation_path.parse()?;
218 let derived = self.xprv.derive_priv(&self.secp, &der)?;
219 Ok(Xpub::from_priv(&self.secp, &derived).encode().to_vec())
220 }
221
222 fn sign_ecdsa(&self, msg: Vec<u8>, derivation_path: String) -> Result<Vec<u8>, SignerError> {
223 let der: DerivationPath = derivation_path.parse()?;
224 let ext_derived = self.xprv.derive_priv(&self.secp, &der)?;
225 let sig = self.secp.sign_ecdsa_low_r(
226 &Message::from_digest(
227 msg.try_into()
228 .map_err(|_| anyhow::anyhow!("failed to sign"))?,
229 ),
230 &ext_derived.private_key,
231 );
232 Ok(sig.serialize_der().to_vec())
233 }
234
235 fn slip77_master_blinding_key(&self) -> Result<Vec<u8>, SignerError> {
236 let master_blinding_key = MasterBlindingKey::from_seed(&self.seed);
237 Ok(master_blinding_key.as_bytes().to_vec())
238 }
239
240 fn sign_ecdsa_recoverable(&self, msg: Vec<u8>) -> Result<Vec<u8>, SignerError> {
241 let secp = Secp256k1::new();
242 let keypair = Xpriv::new_master(self.network, &self.seed)
243 .map_err(|e| anyhow::anyhow!("Could not get signer keypair: {e}"))?
244 .to_keypair(&secp);
245 let s = msg.as_slice();
246
247 let msg: Message = Message::from_digest_slice(s)
248 .map_err(|e| SignerError::Generic { err: e.to_string() })?;
249 let recoverable_sig = secp.sign_ecdsa_recoverable(&msg, &keypair.secret_key());
251 let (recovery_id, sig) = recoverable_sig.serialize_compact();
252 let mut complete_signature = vec![31 + recovery_id.to_i32() as u8];
253 complete_signature.extend_from_slice(&sig);
254 Ok(complete_signature)
255 }
256
257 fn hmac_sha256(&self, msg: Vec<u8>, derivation_path: String) -> Result<Vec<u8>, SignerError> {
258 let der: DerivationPath = derivation_path.parse()?;
259 let priv_key = self.xprv.derive_priv(&self.secp, &der)?;
260 let mut engine = HmacEngine::<sha256::Hash>::new(priv_key.to_priv().to_bytes().as_slice());
261
262 engine.input(msg.as_slice());
263 Ok(Hmac::<sha256::Hash>::from_engine(engine)
264 .as_byte_array()
265 .to_vec())
266 }
267
268 fn ecies_encrypt(&self, msg: Vec<u8>) -> Result<Vec<u8>, SignerError> {
269 let keypair = self.xprv.to_keypair(&self.secp);
270 let rc_pub = keypair.public_key().to_public_key().to_bytes();
271 ecies::encrypt(&rc_pub, &msg).map_err(|err| SignerError::Generic {
272 err: format!("Could not encrypt data: {err}"),
273 })
274 }
275
276 fn ecies_decrypt(&self, msg: Vec<u8>) -> Result<Vec<u8>, SignerError> {
277 let rc_prv = self.xprv.to_priv().to_bytes();
278 ecies::decrypt(&rc_prv, &msg).map_err(|err| SignerError::Generic {
279 err: format!("Could not decrypt data: {err}"),
280 })
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use bip32::KeySource;
288 use bip39::rand::{self, RngCore};
289 use bitcoin::PublicKey;
290 use elements::{
291 pset::{Input, Output, PartiallySignedTransaction},
292 AssetId, TxOut, Txid,
293 };
294 use lwk_common::{singlesig_desc, Singlesig};
295 use lwk_signer::SwSigner;
296 use lwk_wollet::{
297 elements::{self, Script},
298 ElementsNetwork, NoPersist, Wollet, WolletDescriptor,
299 };
300 use std::collections::BTreeMap;
301
302 #[cfg(feature = "browser-tests")]
303 wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
304
305 fn get_descriptor<S: LwkSigner>(signer: &S) -> Result<WolletDescriptor, anyhow::Error> {
306 let descriptor_str = singlesig_desc(
307 signer,
308 Singlesig::Wpkh,
309 lwk_common::DescriptorBlindingKey::Slip77,
310 )
311 .map_err(|e| anyhow::anyhow!("Invalid descriptor: {e}"))?;
312 Ok(descriptor_str.parse()?)
313 }
314
315 fn create_signers(mnemonic: &str) -> (SwSigner, SdkLwkSigner) {
316 let sw_signer = SwSigner::new(mnemonic, false).unwrap();
317 let sdk_signer: Box<dyn Signer> = Box::new(SdkSigner::new(mnemonic, "", false).unwrap());
318 let sdk_signer = SdkLwkSigner::new(Arc::new(sdk_signer)).unwrap();
319 (sw_signer, sdk_signer)
320 }
321
322 fn create_pset<S: LwkSigner>(signer: &S) -> PartiallySignedTransaction {
323 let mut pset = PartiallySignedTransaction::new_v2();
325
326 let prev_txid = Txid::from_slice(&[0; 32]).unwrap();
328 let prev_vout = 0;
329
330 let derivation_path: DerivationPath = "m/84'/0'/0'/0/0".parse().unwrap();
331 let xpub = signer.derive_xpub(&derivation_path).unwrap();
332 let mut bip32_derivation_map: BTreeMap<PublicKey, KeySource> = BTreeMap::new();
333 bip32_derivation_map.insert(
334 xpub.public_key.into(),
335 (signer.fingerprint().unwrap(), derivation_path),
336 );
337 let input = Input {
338 non_witness_utxo: None,
339 witness_utxo: Some(TxOut::new_fee(
340 100_000_000,
341 AssetId::from_slice(&[1; 32]).unwrap(),
342 )),
343 previous_txid: prev_txid,
344 previous_output_index: prev_vout,
345 bip32_derivation: bip32_derivation_map,
346 ..Default::default()
347 };
348
349 pset.add_input(input);
350
351 let output_script = Script::new();
353 let output_amount = 99_000_000;
354 let output_asset = AssetId::from_slice(&[1; 32]).unwrap();
355 let output = Output::new_explicit(
356 output_script,
357 output_amount,
358 output_asset,
359 None, );
361 pset.add_output(output);
362 pset
363 }
364
365 #[sdk_macros::test_all]
366 fn test_invalid_signer() {
367 let mut rng = rand::thread_rng();
368
369 assert!(SwSigner::new("", false).is_err());
370
371 assert!(SdkSigner::new("", "", false).is_err());
372
373 assert!(SdkSigner::new_with_seed(vec![], false).is_err());
374
375 let mut seed1 = [0u8; 31];
376 rng.fill_bytes(&mut seed1);
377 assert!(SdkSigner::new_with_seed(seed1.to_vec(), false).is_err());
378
379 let mut seed2 = [0u8; 32];
380 rng.fill_bytes(&mut seed2);
381 assert!(SdkSigner::new_with_seed(seed2.to_vec(), false).is_ok());
382 }
383
384 #[sdk_macros::test_all]
385 fn test_sign() {
386 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
387 let (sw_signer, sdk_signer) = create_signers(mnemonic);
388
389 let mut pset_sw = create_pset(&sw_signer);
391 let mut pset_sdk = create_pset(&sdk_signer);
392
393 let sw_sig_count = sw_signer.sign(&mut pset_sw).unwrap();
395 assert_eq!(sw_sig_count, 1);
396
397 let sdk_sig_count = sdk_signer.sign(&mut pset_sdk).unwrap();
399 assert_eq!(sdk_sig_count, 1);
400
401 assert_eq!(pset_sw, pset_sdk);
403
404 let tx_sw = pset_sw.extract_tx().unwrap();
406 let tx_sdk = pset_sdk.extract_tx().unwrap();
407 assert_eq!(tx_sw, tx_sdk);
408 }
409
410 #[sdk_macros::test_all]
411 fn test_slip77_master_blinding_key() {
412 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
413 let (sw_signer, sdk_signer) = create_signers(mnemonic);
414
415 let sw_key = sw_signer.slip77_master_blinding_key().unwrap();
416 let sdk_key = sdk_signer.slip77_master_blinding_key().unwrap();
417
418 assert_eq!(
419 sw_key, sdk_key,
420 "SLIP77 master blinding keys should be identical"
421 );
422 }
423
424 #[sdk_macros::test_all]
425 fn test_derive_xpub() {
426 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
427 let (sw_signer, sdk_signer) = create_signers(mnemonic);
428
429 let path = "m/84'/0'/0'/0/0".parse().unwrap();
430 let sw_xpub = sw_signer.derive_xpub(&path).unwrap();
431 let sdk_xpub = sdk_signer.derive_xpub(&path).unwrap();
432
433 assert_eq!(sw_xpub, sdk_xpub, "Derived xpubs should be identical");
434 }
435
436 #[sdk_macros::test_all]
437 fn test_identifier() {
438 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
439 let (sw_signer, sdk_signer) = create_signers(mnemonic);
440
441 let sw_identifier = sw_signer.xpub().identifier();
442 let sdk_identifier = sdk_signer.xpub().unwrap().identifier();
443
444 assert_eq!(
445 sw_identifier, sdk_identifier,
446 "Identifiers should be identical"
447 );
448 }
449
450 #[sdk_macros::test_all]
451 fn test_fingerprint() {
452 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
453 let (sw_signer, sdk_signer) = create_signers(mnemonic);
454
455 let sw_fingerprint = sw_signer.fingerprint();
456 let sdk_fingerprint = sdk_signer.fingerprint().unwrap();
457 let manual_finger_print = sdk_signer.xpub().unwrap().identifier()[0..4]
458 .try_into()
459 .unwrap();
460 assert_eq!(
461 sw_fingerprint, sdk_fingerprint,
462 "Fingerprints should be identical"
463 );
464
465 assert_eq!(
466 sw_fingerprint, manual_finger_print,
467 "Fingerprints should be identical"
468 );
469 }
470
471 #[sdk_macros::test_all]
472 fn test_sdk_signer_vs_sw_signer() {
473 let mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
475 let network = ElementsNetwork::LiquidTestnet;
476
477 let sw_signer = SwSigner::new(mnemonic, false).unwrap();
479 let sw_wallet = Wollet::new(
480 network,
481 NoPersist::new(),
482 get_descriptor(&sw_signer).unwrap(),
483 )
484 .unwrap();
485
486 let sdk_signer: Box<dyn Signer> = Box::new(SdkSigner::new(mnemonic, "", false).unwrap());
488 let sdk_signer = SdkLwkSigner::new(Arc::new(sdk_signer)).unwrap();
489 let sdk_wallet = Wollet::new(
490 network,
491 NoPersist::new(),
492 get_descriptor(&sdk_signer).unwrap(),
493 )
494 .unwrap();
495
496 let sw_address = sw_wallet.address(None).unwrap();
498 let sdk_address = sdk_wallet.address(None).unwrap();
499
500 assert_eq!(
501 sw_address.address().to_string(),
502 sdk_address.address().to_string(),
503 "Addresses should be identical"
504 );
505 }
506}