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