1use std::collections::{HashMap, HashSet};
2use std::str::FromStr;
3use std::sync::Arc;
4
5use simplicityhl::Value;
6use simplicityhl::WitnessValues;
7use simplicityhl::elements::pset::PartiallySignedTransaction;
8use simplicityhl::elements::secp256k1_zkp::{All, Keypair, Message, Secp256k1, ecdsa, schnorr};
9use simplicityhl::elements::{Address, AssetId, OutPoint, Script, Transaction, Txid};
10use simplicityhl::simplicity::bitcoin::XOnlyPublicKey;
11use simplicityhl::simplicity::hashes::Hash;
12use simplicityhl::str::WitnessName;
13use simplicityhl::value::ValueConstructible;
14
15use bip39::Mnemonic;
16use bip39::rand::thread_rng;
17
18use elements_miniscript::{
19 ConfidentialDescriptor, Descriptor, DescriptorPublicKey,
20 bitcoin::{NetworkKind, PrivateKey, PublicKey, bip32::DerivationPath},
21 elements::{
22 EcdsaSighashType,
23 bitcoin::bip32::{Fingerprint, Xpriv, Xpub},
24 sighash::SighashCache,
25 },
26 elementssig_to_rawsig,
27 psbt::PsbtExt,
28 slip77::MasterBlindingKey,
29};
30
31use crate::constants::MIN_FEE;
32use crate::program::ProgramTrait;
33use crate::provider::ProviderTrait;
34use crate::provider::SimplicityNetwork;
35use crate::signer::wtns_injector::WtnsInjector;
36use crate::transaction::{FinalTransaction, PartialInput, PartialOutput, RequiredSignature, TxReceipt, UTXO};
37
38use super::error::SignerError;
39
40pub const PLACEHOLDER_FEE: u64 = 1;
42
43pub trait SignerTrait {
45 fn sign_program(
50 &self,
51 pst: &PartiallySignedTransaction,
52 program: &dyn ProgramTrait,
53 input_index: usize,
54 network: &SimplicityNetwork,
55 ) -> Result<schnorr::Signature, SignerError>;
56
57 fn sign_input(
62 &self,
63 pst: &PartiallySignedTransaction,
64 input_index: usize,
65 ) -> Result<(PublicKey, ecdsa::Signature), SignerError>;
66}
67
68pub struct Signer {
71 mnemonic: Mnemonic,
72 xprv: Xpriv,
73 provider: Box<dyn ProviderTrait>,
74 network: SimplicityNetwork,
75 secp: Secp256k1<All>,
76}
77
78impl SignerTrait for Signer {
79 fn sign_program(
80 &self,
81 pst: &PartiallySignedTransaction,
82 program: &dyn ProgramTrait,
83 input_index: usize,
84 network: &SimplicityNetwork,
85 ) -> Result<schnorr::Signature, SignerError> {
86 let env = program.get_env(pst, input_index, network)?;
87 let msg = Message::from_digest(env.c_tx_env().sighash_all().to_byte_array());
88
89 let private_key = self.get_private_key();
90 let keypair = Keypair::from_secret_key(&self.secp, &private_key.inner);
91
92 Ok(self.secp.sign_schnorr(&msg, &keypair))
93 }
94
95 fn sign_input(
96 &self,
97 pst: &PartiallySignedTransaction,
98 input_index: usize,
99 ) -> Result<(PublicKey, ecdsa::Signature), SignerError> {
100 let tx = pst.extract_tx()?;
101
102 let mut sighash_cache = SighashCache::new(&tx);
103 let genesis_hash = elements_miniscript::elements::BlockHash::all_zeros();
104
105 let message = pst
106 .sighash_msg(input_index, &mut sighash_cache, None, genesis_hash)?
107 .to_secp_msg();
108
109 let private_key = self.get_private_key();
110 let public_key = private_key.public_key(&self.secp);
111
112 let signature = self.secp.sign_ecdsa_low_r(&message, &private_key.inner);
113
114 Ok((public_key, signature))
115 }
116}
117
118enum Estimate {
119 Success(Transaction, u64),
120 Failure(u64),
121}
122
123impl Signer {
124 #[must_use]
129 pub fn new(mnemonic: &str, provider: Box<dyn ProviderTrait>) -> Self {
130 let secp = Secp256k1::new();
131 let mnemonic: Mnemonic = mnemonic
132 .parse()
133 .map_err(|e: bip39::Error| SignerError::Mnemonic(e.to_string()))
134 .unwrap();
135 let seed = mnemonic.to_seed("");
136 let xprv = Xpriv::new_master(NetworkKind::Test, &seed).unwrap();
137
138 let network = *provider.get_network();
139
140 Self {
141 mnemonic,
142 xprv,
143 provider,
144 network,
145 secp,
146 }
147 }
148
149 pub fn send(&self, to: Script, amount: u64) -> Result<TxReceipt<'_>, SignerError> {
155 let mut ft = FinalTransaction::new();
156
157 ft.add_output(PartialOutput::new(to, amount, self.network.policy_asset()));
158
159 let (tx, _fee) = self.finalize(&ft)?;
160
161 Ok(self.provider.broadcast_transaction(&tx)?)
162 }
163
164 pub fn broadcast(&self, tx: &FinalTransaction) -> Result<TxReceipt<'_>, SignerError> {
169 let (tx, _fee) = self.finalize(tx)?;
170
171 Ok(self.provider.broadcast_transaction(&tx)?)
172 }
173
174 pub fn finalize(&self, tx: &FinalTransaction) -> Result<(Transaction, u64), SignerError> {
179 let mut signer_utxos = self.get_utxos_asset(self.network.policy_asset())?;
180 let mut set = HashSet::new();
181
182 for input in tx.inputs() {
183 set.insert(OutPoint {
184 txid: input.partial_input.witness_txid,
185 vout: input.partial_input.witness_output_index,
186 });
187 }
188
189 signer_utxos.retain(|utxo| !set.contains(&utxo.outpoint));
190
191 signer_utxos.sort_by_key(|utxo| std::cmp::Reverse(utxo.amount()));
193
194 let mut fee_tx = tx.clone();
195 let mut curr_fee = MIN_FEE;
196 let fee_rate = self.provider.fetch_fee_rate(1)?;
197
198 for utxo in signer_utxos {
199 let policy_amount_delta = fee_tx.calculate_fee_delta(&self.network);
200
201 if policy_amount_delta >= curr_fee.cast_signed() {
202 match self.estimate_tx(fee_tx.clone(), fee_rate, policy_amount_delta.cast_unsigned())? {
203 Estimate::Success(tx, fee) => return Ok((tx, fee)),
204 Estimate::Failure(required_fee) => curr_fee = required_fee,
205 }
206 }
207
208 fee_tx.add_input(PartialInput::new(utxo), RequiredSignature::NativeEcdsa);
209 }
210
211 let policy_amount_delta = fee_tx.calculate_fee_delta(&self.network);
213
214 if policy_amount_delta >= curr_fee.cast_signed() {
215 match self.estimate_tx(fee_tx.clone(), fee_rate, policy_amount_delta.cast_unsigned())? {
216 Estimate::Success(tx, fee) => return Ok((tx, fee)),
217 Estimate::Failure(required_fee) => curr_fee = required_fee,
218 }
219 }
220
221 Err(SignerError::NotEnoughFunds(curr_fee))
222 }
223
224 pub fn finalize_strict(
231 &self,
232 tx: &FinalTransaction,
233 target_blocks: u32,
234 ) -> Result<(Transaction, u64), SignerError> {
235 let policy_amount_delta = tx.calculate_fee_delta(&self.network);
236
237 if policy_amount_delta < MIN_FEE.cast_signed() {
238 return Err(SignerError::DustAmount(policy_amount_delta));
239 }
240
241 let fee_rate = self.provider.fetch_fee_rate(target_blocks)?;
242
243 match self.estimate_tx(tx.clone(), fee_rate, policy_amount_delta.cast_unsigned())? {
245 Estimate::Success(tx, fee) => Ok((tx, fee)),
246 Estimate::Failure(required_fee) => Err(SignerError::NotEnoughFeeAmount(policy_amount_delta, required_fee)),
247 }
248 }
249
250 #[must_use]
252 pub fn get_provider(&self) -> &dyn ProviderTrait {
253 self.provider.as_ref()
254 }
255
256 #[must_use]
261 pub fn get_confidential_address(&self) -> Address {
262 let mut descriptor =
263 ConfidentialDescriptor::<DescriptorPublicKey>::from_str(&self.get_slip77_descriptor().unwrap())
264 .map_err(|e| SignerError::Slip77Descriptor(e.to_string()))
265 .unwrap();
266
267 descriptor.descriptor = descriptor.descriptor.into_single_descriptors().unwrap()[0].clone();
269
270 descriptor
271 .at_derivation_index(1)
272 .unwrap()
273 .address(&self.secp, self.network.address_params())
274 .unwrap()
275 }
276
277 #[must_use]
282 pub fn get_address(&self) -> Address {
283 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(&self.get_wpkh_descriptor().unwrap())
284 .map_err(|e| SignerError::WpkhDescriptor(e.to_string()))
285 .unwrap();
286
287 descriptor.into_single_descriptors().unwrap()[0]
288 .at_derivation_index(1)
289 .unwrap()
290 .address(self.network.address_params())
291 .unwrap()
292 }
293
294 pub fn get_utxos(&self) -> Result<Vec<UTXO>, SignerError> {
299 self.get_utxos_filter(&|_| true, &|_| true)
300 }
301
302 pub fn get_utxos_asset(&self, asset: AssetId) -> Result<Vec<UTXO>, SignerError> {
307 self.get_utxos_filter(&|utxo| utxo.asset() == asset, &|utxo| utxo.asset() == asset)
308 }
309
310 pub fn get_utxos_txid(&self, txid: Txid) -> Result<Vec<UTXO>, SignerError> {
316 self.get_utxos_filter(&|utxo| utxo.outpoint.txid == txid, &|utxo| utxo.outpoint.txid == txid)
317 }
318
319 pub fn get_utxos_filter(
325 &self,
326 explicit_filter: &dyn Fn(&UTXO) -> bool,
327 confidential_filter: &dyn Fn(&UTXO) -> bool,
328 ) -> Result<Vec<UTXO>, SignerError> {
329 let mut all_utxos = self.provider.fetch_address_utxos(&self.get_confidential_address())?;
331
332 let mut confidential_utxos = self.unblind(
334 all_utxos
335 .iter()
336 .filter(|utxo| utxo.txout.value.is_confidential())
337 .cloned()
338 .collect(),
339 )?;
340 all_utxos.retain(|utxo| !utxo.txout.value.is_confidential());
342
343 all_utxos.retain(explicit_filter);
344 confidential_utxos.retain(confidential_filter);
345
346 all_utxos.extend(confidential_utxos);
348
349 Ok(all_utxos)
350 }
351
352 #[must_use]
354 pub fn get_schnorr_public_key(&self) -> XOnlyPublicKey {
355 let private_key = self.get_private_key();
356 let keypair = Keypair::from_secret_key(&self.secp, &private_key.inner);
357
358 keypair.x_only_public_key().0
359 }
360
361 #[must_use]
363 pub fn get_ecdsa_public_key(&self) -> PublicKey {
364 self.get_private_key().public_key(&self.secp)
365 }
366
367 #[must_use]
369 pub fn get_blinding_public_key(&self) -> PublicKey {
370 self.get_blinding_private_key().public_key(&self.secp)
371 }
372
373 #[must_use]
378 pub fn get_private_key(&self) -> PrivateKey {
379 let master_xprv = self.master_xpriv().unwrap();
380 let full_path = self.get_derivation_path().unwrap();
381
382 let derived = full_path.extend(
383 DerivationPath::from_str("0/1")
384 .map_err(|e| SignerError::DerivationPath(e.to_string()))
385 .unwrap(),
386 );
387
388 let ext_derived = master_xprv.derive_priv(&self.secp, &derived).unwrap();
389
390 PrivateKey::new(ext_derived.private_key, NetworkKind::Test)
391 }
392
393 #[must_use]
401 pub fn get_blinding_private_key(&self) -> PrivateKey {
402 let blinding_key = self
403 .master_slip77()
404 .unwrap()
405 .blinding_private_key(&self.get_address().script_pubkey());
406
407 PrivateKey::new(blinding_key, NetworkKind::Test)
408 }
409
410 fn unblind(&self, utxos: Vec<UTXO>) -> Result<Vec<UTXO>, SignerError> {
411 let mut unblinded: Vec<UTXO> = Vec::new();
412
413 for mut utxo in utxos {
414 let blinding_key = self.get_blinding_private_key();
415 let secrets = utxo.txout.unblind(&self.secp, blinding_key.inner)?;
416
417 utxo.secrets = Some(secrets);
418
419 unblinded.push(utxo);
420 }
421
422 Ok(unblinded)
423 }
424
425 fn estimate_tx(
426 &self,
427 mut fee_tx: FinalTransaction,
428 fee_rate: f32,
429 available_delta: u64,
430 ) -> Result<Estimate, SignerError> {
431 fee_tx.add_output(PartialOutput::new(
435 self.get_address().script_pubkey(),
436 PLACEHOLDER_FEE,
437 self.network.policy_asset(),
438 ));
439
440 fee_tx.add_output(PartialOutput::new(
441 Script::new(),
442 PLACEHOLDER_FEE,
443 self.network.policy_asset(),
444 ));
445
446 let final_tx = self.sign_tx(&fee_tx)?;
447 let fee = fee_tx.calculate_fee(final_tx.discount_weight(), fee_rate);
448
449 if available_delta > fee && available_delta - fee >= MIN_FEE {
450 let outputs = fee_tx.outputs_mut();
452
453 outputs[outputs.len() - 2].amount = available_delta - fee;
454 outputs[outputs.len() - 1].amount = fee;
455
456 let final_tx = self.sign_tx(&fee_tx)?;
457
458 return Ok(Estimate::Success(final_tx, fee));
459 }
460
461 fee_tx.remove_output(fee_tx.n_outputs() - 2);
463
464 let final_tx = self.sign_tx(&fee_tx)?;
465 let fee = fee_tx.calculate_fee(final_tx.discount_weight(), fee_rate);
466
467 if available_delta < fee {
468 return Ok(Estimate::Failure(fee));
469 }
470
471 let outputs = fee_tx.outputs_mut();
472
473 outputs[outputs.len() - 1].amount = available_delta;
475
476 let final_tx = self.sign_tx(&fee_tx)?;
478
479 Ok(Estimate::Success(final_tx, fee))
480 }
481
482 fn sign_tx(&self, tx: &FinalTransaction) -> Result<Transaction, SignerError> {
483 let (mut pst, secrets) = tx.extract_pst();
484 let inputs = tx.inputs();
485
486 if tx.needs_blinding() {
487 pst.blind_last(&mut thread_rng(), &self.secp, &secrets)?;
488 }
489
490 for (index, input_i) in inputs.iter().enumerate() {
491 if let Some(program_input) = &input_i.program_input {
493 let signing_info: Option<(&String, &[String])> = match &input_i.required_sig {
494 RequiredSignature::Witness(wtns_name) => Some((wtns_name, &[])),
495 RequiredSignature::WitnessWithPath(wtns_name, sig_path) => Some((wtns_name, sig_path)),
496 _ => None,
497 };
498
499 let signed_witness: Result<WitnessValues, SignerError> = match signing_info {
500 Some((witness_name, sig_path)) => Ok(self.get_signed_program_witness(
502 &pst,
503 program_input.program.as_ref(),
504 &program_input.witness.build_witness(),
505 witness_name,
506 sig_path,
507 index,
508 )?),
509 None => Ok(program_input.witness.build_witness()),
511 };
512
513 let pruned_witness =
514 program_input
515 .program
516 .finalize(&pst, &signed_witness.unwrap(), index, &self.network)?;
517
518 pst.inputs_mut()[index].final_script_witness = Some(pruned_witness);
519 } else {
520 let signed_witness = self.sign_input(&pst, index)?;
523 let raw_sig = elementssig_to_rawsig(&(signed_witness.1, EcdsaSighashType::All));
524
525 pst.inputs_mut()[index].final_script_witness = Some(vec![raw_sig, signed_witness.0.to_bytes()]);
526 }
527 }
528
529 Ok(pst.extract_tx()?)
530 }
531
532 fn get_signed_program_witness(
533 &self,
534 pst: &PartiallySignedTransaction,
535 program: &dyn ProgramTrait,
536 witness: &WitnessValues,
537 witness_name: &str,
538 sig_path: &[String],
539 index: usize,
540 ) -> Result<WitnessValues, SignerError> {
541 let signature = self.sign_program(pst, program, index, &self.network)?;
542
543 let sig_val = if sig_path.is_empty() {
545 Value::byte_array(signature.serialize())
546 } else {
547 let witness_types = program.get_witness_types()?;
548 let witness_type = witness_types
549 .get(&WitnessName::from_str_unchecked(witness_name))
550 .ok_or(SignerError::WtnsFieldNotFound(witness_name.to_string()))?;
551
552 let local_wtns = Arc::new(
553 witness
554 .get(&WitnessName::from_str_unchecked(witness_name))
555 .expect("checked above")
556 .clone(),
557 );
558
559 WtnsInjector::inject_value(
560 &local_wtns,
561 witness_type,
562 sig_path,
563 Value::byte_array(signature.serialize()),
564 )?
565 };
566
567 let mut hm = HashMap::new();
568
569 witness.iter().for_each(|el| {
570 hm.insert(el.0.clone(), el.1.clone());
571 });
572
573 hm.insert(WitnessName::from_str_unchecked(witness_name), sig_val);
574
575 Ok(WitnessValues::from(hm))
576 }
577
578 #[allow(clippy::unnecessary_wraps)]
579 fn master_slip77(&self) -> Result<MasterBlindingKey, SignerError> {
580 let seed = self.mnemonic.to_seed("");
581
582 Ok(MasterBlindingKey::from_seed(&seed[..]))
583 }
584
585 fn derive_xpriv(&self, path: &DerivationPath) -> Result<Xpriv, SignerError> {
586 Ok(self.xprv.derive_priv(&self.secp, &path)?)
587 }
588
589 fn master_xpriv(&self) -> Result<Xpriv, SignerError> {
590 self.derive_xpriv(&DerivationPath::master())
591 }
592
593 fn derive_xpub(&self, path: &DerivationPath) -> Result<Xpub, SignerError> {
594 let derived = self.derive_xpriv(path)?;
595
596 Ok(Xpub::from_priv(&self.secp, &derived))
597 }
598
599 fn master_xpub(&self) -> Result<Xpub, SignerError> {
600 self.derive_xpub(&DerivationPath::master())
601 }
602
603 fn fingerprint(&self) -> Result<Fingerprint, SignerError> {
604 Ok(self.master_xpub()?.fingerprint())
605 }
606
607 fn get_slip77_descriptor(&self) -> Result<String, SignerError> {
608 let wpkh_descriptor = self.get_wpkh_descriptor()?;
609 let blinding_key = self.master_slip77()?;
610
611 Ok(format!("ct(slip77({blinding_key}),{wpkh_descriptor})"))
612 }
613
614 fn get_wpkh_descriptor(&self) -> Result<String, SignerError> {
615 let fingerprint = self.fingerprint()?;
616 let path = self.get_derivation_path()?;
617 let xpub = self.derive_xpub(&path)?;
618
619 Ok(format!("elwpkh([{fingerprint}/{path}]{xpub}/<0;1>/*)"))
620 }
621
622 fn get_derivation_path(&self) -> Result<DerivationPath, SignerError> {
623 let coin_type = if self.network.is_mainnet() { 1776 } else { 1 };
624 let path = format!("84h/{coin_type}h/0h");
625
626 DerivationPath::from_str(&format!("m/{path}")).map_err(|e| SignerError::DerivationPath(e.to_string()))
627 }
628}
629
630#[cfg(test)]
631mod tests {
632 use crate::provider::EsploraProvider;
633 use crate::utils::random_mnemonic;
634
635 use super::*;
636
637 fn create_signer() -> Signer {
638 let url = "https://blockstream.info/liquidtestnet/api".to_string();
639 let network = SimplicityNetwork::LiquidTestnet;
640
641 Signer::new(random_mnemonic().as_str(), Box::new(EsploraProvider::new(url, network)))
642 }
643
644 #[test]
645 fn keys_correspond_to_address() {
646 let signer = create_signer();
647
648 let address = signer.get_address();
649 let pubkey = signer.get_ecdsa_public_key();
650
651 let derived_addr = Address::p2wpkh(&pubkey, None, signer.get_provider().get_network().address_params());
652
653 assert_eq!(derived_addr.to_string(), address.to_string());
654 }
655
656 #[test]
657 fn descriptors() {
658 let signer = create_signer();
659
660 println!("{}", signer.get_address());
661 println!("{}", signer.get_confidential_address());
662 }
663}