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, UTXO};
37
38use super::error::SignerError;
39
40pub const PLACEHOLDER_FEE: u64 = 1;
41
42pub trait SignerTrait {
43 fn sign_program(
44 &self,
45 pst: &PartiallySignedTransaction,
46 program: &dyn ProgramTrait,
47 input_index: usize,
48 network: &SimplicityNetwork,
49 ) -> Result<schnorr::Signature, SignerError>;
50
51 fn sign_input(
52 &self,
53 pst: &PartiallySignedTransaction,
54 input_index: usize,
55 ) -> Result<(PublicKey, ecdsa::Signature), SignerError>;
56}
57
58pub struct Signer {
59 mnemonic: Mnemonic,
60 xprv: Xpriv,
61 provider: Box<dyn ProviderTrait>,
62 network: SimplicityNetwork,
63 secp: Secp256k1<All>,
64}
65
66impl SignerTrait for Signer {
67 fn sign_program(
68 &self,
69 pst: &PartiallySignedTransaction,
70 program: &dyn ProgramTrait,
71 input_index: usize,
72 network: &SimplicityNetwork,
73 ) -> Result<schnorr::Signature, SignerError> {
74 let env = program.get_env(pst, input_index, network)?;
75 let msg = Message::from_digest(env.c_tx_env().sighash_all().to_byte_array());
76
77 let private_key = self.get_private_key();
78 let keypair = Keypair::from_secret_key(&self.secp, &private_key.inner);
79
80 Ok(self.secp.sign_schnorr(&msg, &keypair))
81 }
82
83 fn sign_input(
84 &self,
85 pst: &PartiallySignedTransaction,
86 input_index: usize,
87 ) -> Result<(PublicKey, ecdsa::Signature), SignerError> {
88 let tx = pst.extract_tx()?;
89
90 let mut sighash_cache = SighashCache::new(&tx);
91 let genesis_hash = elements_miniscript::elements::BlockHash::all_zeros();
92
93 let message = pst
94 .sighash_msg(input_index, &mut sighash_cache, None, genesis_hash)?
95 .to_secp_msg();
96
97 let private_key = self.get_private_key();
98 let public_key = private_key.public_key(&self.secp);
99
100 let signature = self.secp.sign_ecdsa_low_r(&message, &private_key.inner);
101
102 Ok((public_key, signature))
103 }
104}
105
106enum Estimate {
107 Success(Transaction, u64),
108 Failure(u64),
109}
110
111impl Signer {
112 pub fn new(mnemonic: &str, provider: Box<dyn ProviderTrait>) -> Self {
113 let secp = Secp256k1::new();
114 let mnemonic: Mnemonic = mnemonic
115 .parse()
116 .map_err(|e: bip39::Error| SignerError::Mnemonic(e.to_string()))
117 .unwrap();
118 let seed = mnemonic.to_seed("");
119 let xprv = Xpriv::new_master(NetworkKind::Test, &seed).unwrap();
120
121 let network = *provider.get_network();
122
123 Self {
124 mnemonic,
125 xprv,
126 provider,
127 network,
128 secp,
129 }
130 }
131
132 pub fn send(&self, to: Script, amount: u64) -> Result<Txid, SignerError> {
134 let mut ft = FinalTransaction::new();
135
136 ft.add_output(PartialOutput::new(to, amount, self.network.policy_asset()));
137
138 let (tx, _fee) = self.finalize(&ft)?;
139
140 Ok(self.provider.broadcast_transaction(&tx)?)
141 }
142
143 pub fn broadcast(&self, tx: &FinalTransaction) -> Result<Txid, SignerError> {
144 let (tx, _fee) = self.finalize(tx)?;
145
146 Ok(self.provider.broadcast_transaction(&tx)?)
147 }
148
149 pub fn finalize(&self, tx: &FinalTransaction) -> Result<(Transaction, u64), SignerError> {
150 let mut signer_utxos = self.get_utxos_asset(self.network.policy_asset())?;
151 let mut set = HashSet::new();
152
153 for input in tx.inputs() {
154 set.insert(OutPoint {
155 txid: input.partial_input.witness_txid,
156 vout: input.partial_input.witness_output_index,
157 });
158 }
159
160 signer_utxos.retain(|utxo| !set.contains(&utxo.outpoint));
161
162 signer_utxos.sort_by(|a, b| {
164 let a_value = match a.secrets {
165 Some(secrets) => secrets.value,
166 None => a.explicit_amount(),
167 };
168 let b_value = match b.secrets {
169 Some(secrets) => secrets.value,
170 None => b.explicit_amount(),
171 };
172
173 b_value.cmp(&a_value)
174 });
175
176 let mut fee_tx = tx.clone();
177 let mut curr_fee = MIN_FEE;
178 let fee_rate = self.provider.fetch_fee_rate(1)?;
179
180 for utxo in signer_utxos {
181 let policy_amount_delta = fee_tx.calculate_fee_delta(&self.network);
182
183 if policy_amount_delta >= curr_fee as i64 {
184 match self.estimate_tx(fee_tx.clone(), fee_rate, policy_amount_delta as u64)? {
185 Estimate::Success(tx, fee) => return Ok((tx, fee)),
186 Estimate::Failure(required_fee) => curr_fee = required_fee,
187 }
188 }
189
190 fee_tx.add_input(PartialInput::new(utxo), RequiredSignature::NativeEcdsa);
191 }
192
193 let policy_amount_delta = fee_tx.calculate_fee_delta(&self.network);
195
196 if policy_amount_delta >= curr_fee as i64 {
197 match self.estimate_tx(fee_tx.clone(), fee_rate, policy_amount_delta as u64)? {
198 Estimate::Success(tx, fee) => return Ok((tx, fee)),
199 Estimate::Failure(required_fee) => curr_fee = required_fee,
200 }
201 }
202
203 Err(SignerError::NotEnoughFunds(curr_fee))
204 }
205
206 pub fn finalize_strict(
207 &self,
208 tx: &FinalTransaction,
209 target_blocks: u32,
210 ) -> Result<(Transaction, u64), SignerError> {
211 let policy_amount_delta = tx.calculate_fee_delta(&self.network);
212
213 if policy_amount_delta < MIN_FEE as i64 {
214 return Err(SignerError::DustAmount(policy_amount_delta));
215 }
216
217 let fee_rate = self.provider.fetch_fee_rate(target_blocks)?;
218
219 match self.estimate_tx(tx.clone(), fee_rate, policy_amount_delta as u64)? {
221 Estimate::Success(tx, fee) => Ok((tx, fee)),
222 Estimate::Failure(required_fee) => Err(SignerError::NotEnoughFeeAmount(policy_amount_delta, required_fee)),
223 }
224 }
225
226 pub fn get_provider(&self) -> &dyn ProviderTrait {
227 self.provider.as_ref()
228 }
229
230 pub fn get_confidential_address(&self) -> Address {
231 let mut descriptor =
232 ConfidentialDescriptor::<DescriptorPublicKey>::from_str(&self.get_slip77_descriptor().unwrap())
233 .map_err(|e| SignerError::Slip77Descriptor(e.to_string()))
234 .unwrap();
235
236 descriptor.descriptor = descriptor.descriptor.into_single_descriptors().unwrap()[0].clone();
238
239 descriptor
240 .at_derivation_index(1)
241 .unwrap()
242 .address(&self.secp, self.network.address_params())
243 .unwrap()
244 }
245
246 pub fn get_address(&self) -> Address {
247 let descriptor = Descriptor::<DescriptorPublicKey>::from_str(&self.get_wpkh_descriptor().unwrap())
248 .map_err(|e| SignerError::WpkhDescriptor(e.to_string()))
249 .unwrap();
250
251 descriptor.into_single_descriptors().unwrap()[0]
252 .at_derivation_index(1)
253 .unwrap()
254 .address(self.network.address_params())
255 .unwrap()
256 }
257
258 pub fn get_utxos(&self) -> Result<Vec<UTXO>, SignerError> {
259 self.get_utxos_filter(&|_| true, &|_| true)
260 }
261
262 pub fn get_utxos_asset(&self, asset: AssetId) -> Result<Vec<UTXO>, SignerError> {
263 self.get_utxos_filter(&|utxo| utxo.explicit_asset() == asset, &|utxo| {
264 utxo.unblinded_asset() == asset
265 })
266 }
267
268 pub fn get_utxos_txid(&self, txid: Txid) -> Result<Vec<UTXO>, SignerError> {
270 self.get_utxos_filter(&|utxo| utxo.outpoint.txid == txid, &|utxo| utxo.outpoint.txid == txid)
271 }
272
273 pub fn get_utxos_filter(
274 &self,
275 explicit_filter: &dyn Fn(&UTXO) -> bool,
276 confidential_filter: &dyn Fn(&UTXO) -> bool,
277 ) -> Result<Vec<UTXO>, SignerError> {
278 let mut all_utxos = self.provider.fetch_address_utxos(&self.get_confidential_address())?;
280
281 let mut confidential_utxos = self.unblind(
283 all_utxos
284 .iter()
285 .filter(|utxo| utxo.txout.value.is_confidential())
286 .cloned()
287 .collect(),
288 )?;
289 all_utxos.retain(|utxo| !utxo.txout.value.is_confidential());
291
292 all_utxos.retain(explicit_filter);
293 confidential_utxos.retain(confidential_filter);
294
295 all_utxos.extend(confidential_utxos);
297
298 Ok(all_utxos)
299 }
300
301 pub fn get_schnorr_public_key(&self) -> XOnlyPublicKey {
302 let private_key = self.get_private_key();
303 let keypair = Keypair::from_secret_key(&self.secp, &private_key.inner);
304
305 keypair.x_only_public_key().0
306 }
307
308 pub fn get_ecdsa_public_key(&self) -> PublicKey {
309 self.get_private_key().public_key(&self.secp)
310 }
311
312 pub fn get_blinding_public_key(&self) -> PublicKey {
313 self.get_blinding_private_key().public_key(&self.secp)
314 }
315
316 pub fn get_private_key(&self) -> PrivateKey {
317 let master_xprv = self.master_xpriv().unwrap();
318 let full_path = self.get_derivation_path().unwrap();
319
320 let derived = full_path.extend(
321 DerivationPath::from_str("0/1")
322 .map_err(|e| SignerError::DerivationPath(e.to_string()))
323 .unwrap(),
324 );
325
326 let ext_derived = master_xprv.derive_priv(&self.secp, &derived).unwrap();
327
328 PrivateKey::new(ext_derived.private_key, NetworkKind::Test)
329 }
330
331 pub fn get_blinding_private_key(&self) -> PrivateKey {
332 let blinding_key = self
333 .master_slip77()
334 .unwrap()
335 .blinding_private_key(&self.get_address().script_pubkey());
336
337 PrivateKey::new(blinding_key, NetworkKind::Test)
338 }
339
340 fn unblind(&self, utxos: Vec<UTXO>) -> Result<Vec<UTXO>, SignerError> {
341 let mut unblinded: Vec<UTXO> = Vec::new();
342
343 for mut utxo in utxos {
344 let blinding_key = self.get_blinding_private_key();
345 let secrets = utxo.txout.unblind(&self.secp, blinding_key.inner)?;
346
347 utxo.secrets = Some(secrets);
348
349 unblinded.push(utxo);
350 }
351
352 Ok(unblinded)
353 }
354
355 fn estimate_tx(
356 &self,
357 mut fee_tx: FinalTransaction,
358 fee_rate: f32,
359 available_delta: u64,
360 ) -> Result<Estimate, SignerError> {
361 fee_tx.add_output(PartialOutput::new(
365 self.get_address().script_pubkey(),
366 PLACEHOLDER_FEE,
367 self.network.policy_asset(),
368 ));
369
370 fee_tx.add_output(PartialOutput::new(
371 Script::new(),
372 PLACEHOLDER_FEE,
373 self.network.policy_asset(),
374 ));
375
376 let final_tx = self.sign_tx(&fee_tx)?;
377 let fee = fee_tx.calculate_fee(final_tx.discount_weight(), fee_rate);
378
379 if available_delta > fee && available_delta - fee >= MIN_FEE {
380 let outputs = fee_tx.outputs_mut();
382
383 outputs[outputs.len() - 2].amount = available_delta - fee;
384 outputs[outputs.len() - 1].amount = fee;
385
386 let final_tx = self.sign_tx(&fee_tx)?;
387
388 return Ok(Estimate::Success(final_tx, fee));
389 }
390
391 fee_tx.remove_output(fee_tx.n_outputs() - 2);
393
394 let final_tx = self.sign_tx(&fee_tx)?;
395 let fee = fee_tx.calculate_fee(final_tx.discount_weight(), fee_rate);
396
397 if available_delta < fee {
398 return Ok(Estimate::Failure(fee));
399 }
400
401 let outputs = fee_tx.outputs_mut();
402
403 outputs[outputs.len() - 1].amount = available_delta;
405
406 let final_tx = self.sign_tx(&fee_tx)?;
408
409 Ok(Estimate::Success(final_tx, fee))
410 }
411
412 fn sign_tx(&self, tx: &FinalTransaction) -> Result<Transaction, SignerError> {
413 let (mut pst, secrets) = tx.extract_pst();
414 let inputs = tx.inputs();
415
416 if tx.needs_blinding() {
417 pst.blind_last(&mut thread_rng(), &self.secp, &secrets)?;
418 }
419
420 for (index, input_i) in inputs.iter().enumerate() {
421 if let Some(program_input) = &input_i.program_input {
423 let signing_info: Option<(&String, &[String])> = match &input_i.required_sig {
424 RequiredSignature::Witness(wtns_name) => Some((wtns_name, &[])),
425 RequiredSignature::WitnessWithPath(wtns_name, sig_path) => Some((wtns_name, sig_path)),
426 _ => None,
427 };
428
429 let signed_witness: Result<WitnessValues, SignerError> = match signing_info {
430 Some((witness_name, sig_path)) => Ok(self.get_signed_program_witness(
432 &pst,
433 program_input.program.as_ref(),
434 &program_input.witness.build_witness(),
435 witness_name,
436 sig_path,
437 index,
438 )?),
439 None => Ok(program_input.witness.build_witness()),
441 };
442
443 let pruned_witness =
444 program_input
445 .program
446 .finalize(&pst, &signed_witness.unwrap(), index, &self.network)?;
447
448 pst.inputs_mut()[index].final_script_witness = Some(pruned_witness);
449 } else {
450 let signed_witness = self.sign_input(&pst, index)?;
453 let raw_sig = elementssig_to_rawsig(&(signed_witness.1, EcdsaSighashType::All));
454
455 pst.inputs_mut()[index].final_script_witness = Some(vec![raw_sig, signed_witness.0.to_bytes()]);
456 }
457 }
458
459 Ok(pst.extract_tx()?)
460 }
461
462 fn get_signed_program_witness(
463 &self,
464 pst: &PartiallySignedTransaction,
465 program: &dyn ProgramTrait,
466 witness: &WitnessValues,
467 witness_name: &str,
468 sig_path: &[String],
469 index: usize,
470 ) -> Result<WitnessValues, SignerError> {
471 let signature = self.sign_program(pst, program, index, &self.network)?;
472
473 let sig_val = if !sig_path.is_empty() {
475 let witness_types = program.get_witness_types()?;
476 let witness_type = witness_types
477 .get(&WitnessName::from_str_unchecked(witness_name))
478 .ok_or(SignerError::WtnsFieldNotFound(witness_name.to_string()))?;
479
480 let local_wtns = Arc::new(
481 witness
482 .get(&WitnessName::from_str_unchecked(witness_name))
483 .expect("checked above")
484 .clone(),
485 );
486
487 WtnsInjector::inject_value(
488 &local_wtns,
489 witness_type,
490 sig_path,
491 Value::byte_array(signature.serialize()),
492 )?
493 } else {
494 Value::byte_array(signature.serialize())
495 };
496
497 let mut hm = HashMap::new();
498
499 witness.iter().for_each(|el| {
500 hm.insert(el.0.clone(), el.1.clone());
501 });
502
503 hm.insert(WitnessName::from_str_unchecked(witness_name), sig_val);
504
505 Ok(WitnessValues::from(hm))
506 }
507
508 fn master_slip77(&self) -> Result<MasterBlindingKey, SignerError> {
509 let seed = self.mnemonic.to_seed("");
510
511 Ok(MasterBlindingKey::from_seed(&seed[..]))
512 }
513
514 fn derive_xpriv(&self, path: &DerivationPath) -> Result<Xpriv, SignerError> {
515 Ok(self.xprv.derive_priv(&self.secp, &path)?)
516 }
517
518 fn master_xpriv(&self) -> Result<Xpriv, SignerError> {
519 self.derive_xpriv(&DerivationPath::master())
520 }
521
522 fn derive_xpub(&self, path: &DerivationPath) -> Result<Xpub, SignerError> {
523 let derived = self.derive_xpriv(path)?;
524
525 Ok(Xpub::from_priv(&self.secp, &derived))
526 }
527
528 fn master_xpub(&self) -> Result<Xpub, SignerError> {
529 self.derive_xpub(&DerivationPath::master())
530 }
531
532 fn fingerprint(&self) -> Result<Fingerprint, SignerError> {
533 Ok(self.master_xpub()?.fingerprint())
534 }
535
536 fn get_slip77_descriptor(&self) -> Result<String, SignerError> {
537 let wpkh_descriptor = self.get_wpkh_descriptor()?;
538 let blinding_key = self.master_slip77()?;
539
540 Ok(format!("ct(slip77({blinding_key}),{wpkh_descriptor})"))
541 }
542
543 fn get_wpkh_descriptor(&self) -> Result<String, SignerError> {
544 let fingerprint = self.fingerprint()?;
545 let path = self.get_derivation_path()?;
546 let xpub = self.derive_xpub(&path)?;
547
548 Ok(format!("elwpkh([{fingerprint}/{path}]{xpub}/<0;1>/*)"))
549 }
550
551 fn get_derivation_path(&self) -> Result<DerivationPath, SignerError> {
552 let coin_type = if self.network.is_mainnet() { 1776 } else { 1 };
553 let path = format!("84h/{coin_type}h/0h");
554
555 DerivationPath::from_str(&format!("m/{path}")).map_err(|e| SignerError::DerivationPath(e.to_string()))
556 }
557}
558
559#[cfg(test)]
560mod tests {
561 use crate::provider::EsploraProvider;
562 use crate::utils::random_mnemonic;
563
564 use super::*;
565
566 fn create_signer() -> Signer {
567 let url = "https://blockstream.info/liquidtestnet/api".to_string();
568 let network = SimplicityNetwork::LiquidTestnet;
569
570 Signer::new(random_mnemonic().as_str(), Box::new(EsploraProvider::new(url, network)))
571 }
572
573 #[test]
574 fn keys_correspond_to_address() {
575 let signer = create_signer();
576
577 let address = signer.get_address();
578 let pubkey = signer.get_ecdsa_public_key();
579
580 let derived_addr = Address::p2wpkh(&pubkey, None, signer.get_provider().get_network().address_params());
581
582 assert_eq!(derived_addr.to_string(), address.to_string());
583 }
584
585 #[test]
586 fn descriptors() {
587 let signer = create_signer();
588
589 println!("{}", signer.get_address());
590 println!("{}", signer.get_confidential_address());
591 }
592}