clementine_tx_sender/
signer.rs1use bitcoin::hashes::Hash;
2use bitcoin::secp256k1::schnorr;
3use bitcoin::secp256k1::{All, Keypair, Message, Secp256k1, SecretKey};
4use bitcoin::taproot::{TapNodeHash, TapTweakHash};
5use bitcoin::{Address, Network, TapSighash, XOnlyPublicKey};
6use clementine_errors::BridgeError;
7use clementine_utils::sign::TapTweakData;
8use eyre::Context;
9use std::sync::LazyLock;
10
11pub(crate) static SECP: LazyLock<Secp256k1<All>> = LazyLock::new(Secp256k1::new);
12
13#[cfg(feature = "citrea")]
14use sha2::{Digest, Sha256};
15
16fn calc_tweaked_keypair(
17 keypair: &Keypair,
18 merkle_root: Option<TapNodeHash>,
19) -> Result<Keypair, BridgeError> {
20 Ok(keypair
21 .add_xonly_tweak(
22 &SECP,
23 &TapTweakHash::from_key_and_tweak(keypair.x_only_public_key().0, merkle_root)
24 .to_scalar(),
25 )
26 .wrap_err("Failed to add tweak to keypair")?)
27}
28
29#[derive(Clone, Debug)]
30pub(crate) struct TxSenderSigningKey {
31 keypair: Keypair,
32 xonly_public_key: XOnlyPublicKey,
33 address: Address,
34}
35
36impl TxSenderSigningKey {
37 pub(crate) fn new(secret_key: SecretKey, network: Network) -> Self {
38 let keypair = Keypair::from_secret_key(&SECP, &secret_key);
39 let (xonly, _parity) = XOnlyPublicKey::from_keypair(&keypair);
40 let address = Address::p2tr(&SECP, xonly, None, network);
41
42 Self {
43 keypair,
44 xonly_public_key: xonly,
45 address,
46 }
47 }
48
49 pub(crate) fn address(&self) -> &Address {
50 &self.address
51 }
52
53 pub(crate) fn xonly_public_key(&self) -> XOnlyPublicKey {
54 self.xonly_public_key
55 }
56
57 pub(crate) fn sign_with_tweak_data(
58 &self,
59 sighash: TapSighash,
60 tweak_data: TapTweakData,
61 ) -> Result<schnorr::Signature, BridgeError> {
62 let keypair;
63 let keypair_ref = match tweak_data {
64 TapTweakData::KeyPath(merkle_root) => {
65 keypair = calc_tweaked_keypair(&self.keypair, merkle_root)?;
66 &keypair
67 }
68 TapTweakData::ScriptPath => &self.keypair,
69 TapTweakData::Unknown => return Err(eyre::eyre!("Spend Data Unknown").into()),
70 };
71
72 Ok(SECP
73 .sign_schnorr_no_aux_rand(&Message::from_digest(sighash.to_byte_array()), keypair_ref))
74 }
75
76 #[cfg(feature = "citrea")]
80 pub(crate) fn sign_blob(&self, blob: &[u8]) -> (Vec<u8>, Vec<u8>) {
81 let secret_key = self.keypair.secret_key();
82 let message = {
83 let mut hasher = Sha256::default();
84 hasher.update(blob);
85 hasher.finalize().into()
86 };
87 let public_key = self.keypair.public_key();
88 let msg = Message::from_digest(message);
89 let sig = SECP.sign_ecdsa(&msg, &secret_key);
90 (
91 sig.serialize_compact().to_vec(),
92 public_key.serialize().to_vec(),
93 )
94 }
95}