clementine_core/
actor.rs

1use std::collections::hash_map::Entry;
2use std::collections::HashMap;
3
4use crate::bitvm_client::{self, ClementineBitVMPublicKeys, SECP};
5use crate::builder::script::SpendPath;
6#[cfg(test)]
7use crate::builder::transaction::deposit_signature_owner::DepositSigKeyOwner;
8use crate::builder::transaction::input::SpentTxIn;
9use crate::builder::transaction::{SighashCalculator, TxHandler};
10use crate::config::protocol::ProtocolParamset;
11use crate::rpc::clementine::tagged_signature::SignatureId;
12use crate::rpc::clementine::TaggedSignature;
13use alloy::signers::k256;
14use alloy::signers::local::PrivateKeySigner;
15use bitcoin::hashes::hash160;
16use bitcoin::secp256k1::PublicKey;
17use bitcoin::taproot::{self, LeafVersion, TaprootSpendInfo};
18use bitcoin::{
19    hashes::Hash,
20    secp256k1::{schnorr, Keypair, Message, SecretKey, XOnlyPublicKey},
21    Address, ScriptBuf, TapSighash, TapTweakHash,
22};
23use bitcoin::{Network, OutPoint, TapNodeHash, TapSighashType, Witness};
24use bitvm::signatures::winternitz;
25#[cfg(test)]
26use bitvm::signatures::winternitz::{BinarysearchVerifier, ToBytesConverter, Winternitz};
27use clementine_errors::BridgeError;
28use clementine_errors::TxError;
29use clementine_primitives::EVMAddress;
30use clementine_primitives::{PublicHash, RoundIndex};
31use clementine_utils::sign::TapTweakData;
32use eyre::{Context, OptionExt};
33use hkdf::Hkdf;
34use sha2::Sha256;
35
36pub use clementine_errors::VerificationError;
37
38#[derive(Debug, Clone)]
39pub enum WinternitzDerivationPath {
40    /// round_idx, kickoff_idx
41    /// Message length is fixed KICKOFF_BLOCKHASH_COMMIT_LENGTH
42    Kickoff(RoundIndex, u32, &'static ProtocolParamset),
43    /// message_length, pk_type_idx, pk_idx, deposit_outpoint
44    BitvmAssert(u32, u32, u32, OutPoint, &'static ProtocolParamset),
45    /// watchtower_idx, deposit_outpoint
46    /// message length is fixed to 1 (because its for one hash)
47    ChallengeAckHash(u32, OutPoint, &'static ProtocolParamset),
48}
49
50impl WinternitzDerivationPath {
51    fn get_type_id(&self) -> u8 {
52        match self {
53            WinternitzDerivationPath::Kickoff(..) => 0u8,
54            WinternitzDerivationPath::BitvmAssert(..) => 1u8,
55            WinternitzDerivationPath::ChallengeAckHash(..) => 2u8,
56        }
57    }
58
59    fn get_network_prefix(&self) -> u8 {
60        let paramset = match self {
61            WinternitzDerivationPath::Kickoff(.., paramset) => paramset,
62            WinternitzDerivationPath::BitvmAssert(.., paramset) => paramset,
63            WinternitzDerivationPath::ChallengeAckHash(.., paramset) => paramset,
64        };
65        match paramset.network {
66            Network::Regtest => 0u8,
67            Network::Testnet4 => 1u8,
68            Network::Signet => 2u8,
69            Network::Bitcoin => 3u8,
70            // currently only testnet is unsupported
71            _ => panic!("Unsupported network {:?}", paramset.network),
72        }
73    }
74
75    fn to_bytes(&self) -> Vec<u8> {
76        let type_id = self.get_type_id();
77        let mut bytes = vec![type_id, self.get_network_prefix()];
78
79        match self {
80            WinternitzDerivationPath::Kickoff(round_idx, kickoff_idx, _) => {
81                bytes.extend_from_slice(&round_idx.to_index().to_be_bytes());
82                bytes.extend_from_slice(&kickoff_idx.to_be_bytes());
83            }
84            WinternitzDerivationPath::BitvmAssert(
85                message_length,
86                pk_type_idx,
87                pk_idx,
88                deposit_outpoint,
89                _,
90            ) => {
91                bytes.extend_from_slice(&message_length.to_be_bytes());
92                bytes.extend_from_slice(&pk_type_idx.to_be_bytes());
93                bytes.extend_from_slice(&pk_idx.to_be_bytes());
94                bytes.extend_from_slice(&deposit_outpoint.txid.to_byte_array());
95                bytes.extend_from_slice(&deposit_outpoint.vout.to_be_bytes());
96            }
97            WinternitzDerivationPath::ChallengeAckHash(watchtower_idx, deposit_outpoint, _) => {
98                bytes.extend_from_slice(&watchtower_idx.to_be_bytes());
99                bytes.extend_from_slice(&deposit_outpoint.txid.to_byte_array());
100                bytes.extend_from_slice(&deposit_outpoint.vout.to_be_bytes());
101            }
102        }
103
104        bytes
105    }
106
107    /// Returns the parameters for the Winternitz signature.
108    pub fn get_params(&self) -> winternitz::Parameters {
109        match self {
110            WinternitzDerivationPath::Kickoff(_, _, paramset) => winternitz::Parameters::new(
111                paramset.kickoff_blockhash_commit_length,
112                paramset.winternitz_log_d,
113            ),
114            WinternitzDerivationPath::BitvmAssert(message_length, _, _, _, paramset) => {
115                winternitz::Parameters::new(*message_length, paramset.winternitz_log_d)
116            }
117            WinternitzDerivationPath::ChallengeAckHash(_, _, paramset) => {
118                winternitz::Parameters::new(1, paramset.winternitz_log_d)
119            }
120        }
121    }
122}
123
124fn calc_tweaked_keypair(
125    keypair: &Keypair,
126    merkle_root: Option<TapNodeHash>,
127) -> Result<Keypair, BridgeError> {
128    Ok(keypair
129        .add_xonly_tweak(
130            &SECP,
131            &TapTweakHash::from_key_and_tweak(keypair.x_only_public_key().0, merkle_root)
132                .to_scalar(),
133        )
134        .wrap_err("Failed to add tweak to keypair")?)
135}
136
137fn calc_tweaked_xonly_pk(
138    pubkey: XOnlyPublicKey,
139    merkle_root: Option<TapNodeHash>,
140) -> Result<XOnlyPublicKey, BridgeError> {
141    Ok(pubkey
142        .add_tweak(
143            &SECP,
144            &TapTweakHash::from_key_and_tweak(pubkey, merkle_root).to_scalar(),
145        )
146        .wrap_err("Failed to add tweak to xonly_pk")?
147        .0)
148}
149
150#[derive(Debug, Clone, Default)]
151// A cache that holds tweaked keys so that we do not need to repeatedly calculate them.
152// This cache will hold data for only one deposit generally because we need to clone the holder of Actor(owner or verifier)
153// to spawned threads during deposit and jn general is immutable.
154// (Because all grpc functions have &self, we also need to clone Actor to a mutable instance
155// to modify the caches)
156pub struct TweakCache {
157    tweaked_key_cache: HashMap<(XOnlyPublicKey, Option<TapNodeHash>), XOnlyPublicKey>,
158    // A cache to hold actors own tweaked keys.
159    tweaked_keypair_cache: HashMap<(XOnlyPublicKey, Option<TapNodeHash>), Keypair>,
160}
161
162impl TweakCache {
163    fn get_tweaked_keypair(
164        &mut self,
165        keypair: &Keypair,
166        merkle_root: Option<TapNodeHash>,
167    ) -> Result<&Keypair, BridgeError> {
168        match self
169            .tweaked_keypair_cache
170            .entry((keypair.x_only_public_key().0, merkle_root))
171        {
172            Entry::Occupied(entry) => Ok(entry.into_mut()),
173            Entry::Vacant(entry) => Ok(entry.insert(calc_tweaked_keypair(keypair, merkle_root)?)),
174        }
175    }
176
177    fn get_tweaked_xonly_key(
178        &mut self,
179        pubkey: XOnlyPublicKey,
180        merkle_root: Option<TapNodeHash>,
181    ) -> Result<XOnlyPublicKey, BridgeError> {
182        match self.tweaked_key_cache.entry((pubkey, merkle_root)) {
183            Entry::Occupied(entry) => Ok(*entry.get()),
184            Entry::Vacant(entry) => Ok(*entry.insert(calc_tweaked_xonly_pk(pubkey, merkle_root)?)),
185        }
186    }
187}
188
189pub fn verify_schnorr(
190    signature: &schnorr::Signature,
191    sighash: &Message,
192    pubkey: XOnlyPublicKey,
193    tweak_data: TapTweakData,
194    tweak_cache: Option<&mut TweakCache>,
195) -> Result<(), BridgeError> {
196    let pubkey = match tweak_data {
197        TapTweakData::KeyPath(merkle_root) => match tweak_cache {
198            Some(cache) => cache.get_tweaked_xonly_key(pubkey, merkle_root)?,
199            None => calc_tweaked_xonly_pk(pubkey, merkle_root)?,
200        },
201        TapTweakData::ScriptPath => pubkey,
202        TapTweakData::Unknown => return Err(eyre::eyre!("Spend Path Unknown").into()),
203    };
204    SECP.verify_schnorr(signature, sighash, &pubkey)
205        .map_err(|_| eyre::eyre!("Failed to verify Schnorr signature").into())
206}
207
208#[derive(Debug, Clone)]
209pub struct Actor {
210    pub keypair: Keypair,
211    pub xonly_public_key: XOnlyPublicKey,
212    pub public_key: PublicKey,
213    pub address: Address,
214    #[cfg(test)]
215    pub annex: Option<Vec<u8>>,
216}
217
218impl clementine_tx_sender::TxSenderSigner for Actor {
219    fn address(&self) -> &Address {
220        &self.address
221    }
222
223    fn xonly_public_key(&self) -> XOnlyPublicKey {
224        self.xonly_public_key
225    }
226
227    fn sign_with_tweak_data(
228        &self,
229        sighash: bitcoin::TapSighash,
230        tweak_data: TapTweakData,
231    ) -> Result<schnorr::Signature, clementine_errors::BridgeError> {
232        self.sign_with_tweak_data(sighash, tweak_data, None)
233    }
234}
235
236impl Actor {
237    #[tracing::instrument(ret(level = tracing::Level::TRACE))]
238    pub fn new(sk: SecretKey, network: bitcoin::Network) -> Self {
239        let keypair = Keypair::from_secret_key(&SECP, &sk);
240        let (xonly, _parity) = XOnlyPublicKey::from_keypair(&keypair);
241        let address = Address::p2tr(&SECP, xonly, None, network);
242
243        Actor {
244            keypair,
245            xonly_public_key: xonly,
246            public_key: keypair.public_key(),
247            address,
248            #[cfg(test)]
249            annex: None,
250        }
251    }
252
253    #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))]
254    fn sign_with_tweak(
255        &self,
256        sighash: TapSighash,
257        merkle_root: Option<TapNodeHash>,
258        tweak_cache: Option<&mut TweakCache>,
259    ) -> Result<schnorr::Signature, BridgeError> {
260        let keypair;
261        let keypair_ref = match tweak_cache {
262            Some(cache) => cache.get_tweaked_keypair(&self.keypair, merkle_root)?,
263            None => {
264                keypair = calc_tweaked_keypair(&self.keypair, merkle_root)?;
265                &keypair
266            }
267        };
268
269        Ok(bitvm_client::SECP
270            .sign_schnorr(&Message::from_digest(*sighash.as_byte_array()), keypair_ref))
271    }
272
273    #[tracing::instrument(skip(self), ret(level = tracing::Level::TRACE))]
274    fn sign(&self, sighash: TapSighash) -> schnorr::Signature {
275        bitvm_client::SECP.sign_schnorr(
276            &Message::from_digest(*sighash.as_byte_array()),
277            &self.keypair,
278        )
279    }
280
281    pub fn sign_with_tweak_data(
282        &self,
283        sighash: TapSighash,
284        tweak_data: TapTweakData,
285        tweak_cache: Option<&mut TweakCache>,
286    ) -> Result<schnorr::Signature, BridgeError> {
287        match tweak_data {
288            TapTweakData::KeyPath(merkle_root) => {
289                self.sign_with_tweak(sighash, merkle_root, tweak_cache)
290            }
291            TapTweakData::ScriptPath => Ok(self.sign(sighash)),
292            TapTweakData::Unknown => Err(eyre::eyre!("Spend Data Unknown").into()),
293        }
294    }
295
296    pub fn get_evm_address(&self) -> Result<EVMAddress, BridgeError> {
297        let x =
298            k256::ecdsa::SigningKey::from_bytes(&self.keypair.secret_key().secret_bytes().into())
299                .wrap_err("Failed to convert secret key to signing key")?;
300        let key: PrivateKeySigner = x.into();
301        let wallet_address = key.address();
302
303        Ok(EVMAddress(wallet_address.into_array()))
304    }
305
306    /// Returns derivied Winternitz secret key from given path.
307    pub fn get_derived_winternitz_sk(
308        &self,
309        path: WinternitzDerivationPath,
310    ) -> Result<winternitz::SecretKey, BridgeError> {
311        let hk = Hkdf::<Sha256>::new(None, self.keypair.secret_key().as_ref());
312        let path_bytes = path.to_bytes();
313        let mut derived_key = vec![0u8; 32];
314        hk.expand(&path_bytes, &mut derived_key)
315            .map_err(|e| eyre::eyre!("Key derivation failed: {:?}", e))?;
316
317        Ok(derived_key)
318    }
319
320    /// Generates a Winternitz public key for the given path.
321    pub fn derive_winternitz_pk(
322        &self,
323        path: WinternitzDerivationPath,
324    ) -> Result<winternitz::PublicKey, BridgeError> {
325        let winternitz_params = path.get_params();
326
327        let altered_secret_key = self.get_derived_winternitz_sk(path)?;
328        let public_key = winternitz::generate_public_key(&winternitz_params, &altered_secret_key);
329
330        Ok(public_key)
331    }
332
333    /// Signs given data with Winternitz signature.
334    #[cfg(test)]
335    pub fn sign_winternitz_signature(
336        &self,
337        path: WinternitzDerivationPath,
338        data: Vec<u8>,
339    ) -> Result<Witness, BridgeError> {
340        let winternitz = Winternitz::<BinarysearchVerifier, ToBytesConverter>::new();
341
342        let winternitz_params = path.get_params();
343
344        let altered_secret_key = self.get_derived_winternitz_sk(path)?;
345
346        let witness = winternitz.sign(&winternitz_params, &altered_secret_key, &data);
347
348        Ok(witness)
349    }
350
351    pub fn generate_preimage_from_path(
352        &self,
353        path: WinternitzDerivationPath,
354    ) -> Result<PublicHash, BridgeError> {
355        let first_preimage = self.get_derived_winternitz_sk(path)?;
356        let second_preimage = hash160::Hash::hash(&first_preimage);
357        Ok(second_preimage.to_byte_array())
358    }
359
360    /// Generates the hashes from the preimages. Preimages are constructed using
361    /// the Winternitz derivation path and the secret key.
362    pub fn generate_public_hash_from_path(
363        &self,
364        path: WinternitzDerivationPath,
365    ) -> Result<PublicHash, BridgeError> {
366        let preimage = self.generate_preimage_from_path(path)?;
367        let hash = hash160::Hash::hash(&preimage);
368        Ok(hash.to_byte_array())
369    }
370
371    pub fn generate_bitvm_pks_for_deposit(
372        &self,
373        deposit_outpoint: OutPoint,
374        paramset: &'static ProtocolParamset,
375    ) -> Result<ClementineBitVMPublicKeys, BridgeError> {
376        let mut pks = ClementineBitVMPublicKeys::create_replacable();
377        let pk_vec = self.derive_winternitz_pk(
378            ClementineBitVMPublicKeys::get_latest_blockhash_derivation(deposit_outpoint, paramset),
379        )?;
380        pks.latest_blockhash_pk = ClementineBitVMPublicKeys::vec_to_array::<43>(&pk_vec);
381        let pk_vec = self.derive_winternitz_pk(
382            ClementineBitVMPublicKeys::get_challenge_sending_watchtowers_derivation(
383                deposit_outpoint,
384                paramset,
385            ),
386        )?;
387        pks.challenge_sending_watchtowers_pk =
388            ClementineBitVMPublicKeys::vec_to_array::<43>(&pk_vec);
389        for i in 0..pks.bitvm_pks.0.len() {
390            let pk_vec = self.derive_winternitz_pk(WinternitzDerivationPath::BitvmAssert(
391                64,
392                3,
393                i as u32,
394                deposit_outpoint,
395                paramset,
396            ))?;
397            pks.bitvm_pks.0[i] = ClementineBitVMPublicKeys::vec_to_array::<67>(&pk_vec);
398        }
399        for i in 0..pks.bitvm_pks.1.len() {
400            let pk_vec = self.derive_winternitz_pk(WinternitzDerivationPath::BitvmAssert(
401                64,
402                4,
403                i as u32,
404                deposit_outpoint,
405                paramset,
406            ))?;
407            pks.bitvm_pks.1[i] = ClementineBitVMPublicKeys::vec_to_array::<67>(&pk_vec);
408        }
409        for i in 0..pks.bitvm_pks.2.len() {
410            let pk_vec = self.derive_winternitz_pk(WinternitzDerivationPath::BitvmAssert(
411                32,
412                5,
413                i as u32,
414                deposit_outpoint,
415                paramset,
416            ))?;
417            pks.bitvm_pks.2[i] = ClementineBitVMPublicKeys::vec_to_array::<35>(&pk_vec);
418        }
419
420        Ok(pks)
421    }
422
423    fn get_saved_signature(
424        signature_id: SignatureId,
425        signatures: &[TaggedSignature],
426    ) -> Option<schnorr::Signature> {
427        signatures
428            .iter()
429            .find(|sig| {
430                sig.signature_id
431                    .map(|id| id == signature_id)
432                    .unwrap_or(false)
433            })
434            .and_then(|sig| schnorr::Signature::from_slice(sig.signature.as_ref()).ok())
435    }
436
437    pub fn add_script_path_to_witness(
438        witness: &mut Witness,
439        script: &ScriptBuf,
440        spend_info: &TaprootSpendInfo,
441    ) -> Result<(), BridgeError> {
442        let spend_control_block = spend_info
443            .control_block(&(script.clone(), LeafVersion::TapScript))
444            .ok_or_eyre("Failed to find control block for script")?;
445        witness.push(script.clone());
446        witness.push(spend_control_block.serialize());
447        Ok(())
448    }
449
450    pub fn tx_sign_preimage(
451        &self,
452        txhandler: &mut TxHandler,
453        data: impl AsRef<[u8]>,
454    ) -> Result<(), BridgeError> {
455        #[cfg(test)]
456        {
457            txhandler.set_test_annex(self.annex.clone());
458        }
459
460        let mut signed_preimage = false;
461
462        let data = data.as_ref();
463        let signer = move |_: usize,
464                           spt: &SpentTxIn,
465                           calc_sighash: SighashCalculator<'_>|
466              -> Result<Option<Witness>, BridgeError> {
467            let spendinfo = spt
468                .get_spendable()
469                .get_spend_info()
470                .as_ref()
471                .ok_or(TxError::MissingSpendInfo)?;
472            let signature_id = spt.get_signature_id();
473            match spt.get_spend_path() {
474                SpendPath::ScriptSpend(script_idx) => {
475                    let script = spt
476                        .get_spendable()
477                        .get_scripts()
478                        .get(script_idx)
479                        .ok_or(TxError::NoScriptAtIndex(script_idx))?;
480                    let sighash_type = signature_id
481                        .get_deposit_sig_owner()
482                        .map(|s| s.sighash_type())?
483                        .unwrap_or(TapSighashType::Default);
484
485                    use crate::builder::script::ScriptKind as Kind;
486
487                    let mut witness = match script.kind() {
488                        Kind::PreimageRevealScript(script) => {
489                            if script.0 != self.xonly_public_key {
490                                return Err(TxError::NotOwnedScriptPath.into());
491                            }
492                            let signature = self.sign(calc_sighash(sighash_type)?);
493                            script.generate_script_inputs(
494                                data,
495                                &taproot::Signature {
496                                    signature,
497                                    sighash_type,
498                                },
499                            )
500                        }
501                        Kind::WinternitzCommit(_)
502                        | Kind::CheckSig(_)
503                        | Kind::Other(_)
504                        | Kind::BaseDepositScript(_)
505                        | Kind::ReplacementDepositScript(_)
506                        | Kind::TimelockScript(_)
507                        | Kind::ManualSpend(_) => return Ok(None),
508                    };
509
510                    if signed_preimage {
511                        return Err(eyre::eyre!("Encountered multiple preimage reveal scripts when attempting to commit to only one.").into());
512                    }
513
514                    signed_preimage = true;
515
516                    Self::add_script_path_to_witness(
517                        &mut witness,
518                        &script.to_script_buf(),
519                        spendinfo,
520                    )?;
521
522                    // BIP341 annex must be the last witness element.
523                    #[cfg(test)]
524                    {
525                        if let Some(ref annex) = self.annex {
526                            if matches!(
527                                signature_id.get_deposit_sig_owner()?,
528                                DepositSigKeyOwner::Own(_)
529                            ) {
530                                witness.push(annex);
531                            }
532                        }
533                    }
534
535                    Ok(Some(witness))
536                }
537                SpendPath::KeySpend => Ok(None),
538                SpendPath::Unknown => Err(TxError::SpendPathNotSpecified.into()),
539            }
540        };
541
542        txhandler.sign_txins(signer)?;
543        Ok(())
544    }
545    pub fn tx_sign_winternitz(
546        &self,
547        txhandler: &mut TxHandler,
548        data: &[(Vec<u8>, WinternitzDerivationPath)],
549    ) -> Result<(), BridgeError> {
550        #[cfg(test)]
551        {
552            txhandler.set_test_annex(self.annex.clone());
553        }
554
555        let mut signed_winternitz = false;
556
557        let signer = move |_: usize,
558                           spt: &SpentTxIn,
559                           calc_sighash: SighashCalculator<'_>|
560              -> Result<Option<Witness>, BridgeError> {
561            let spendinfo = spt
562                .get_spendable()
563                .get_spend_info()
564                .as_ref()
565                .ok_or(TxError::MissingSpendInfo)?;
566            let signature_id = spt.get_signature_id();
567            match spt.get_spend_path() {
568                SpendPath::ScriptSpend(script_idx) => {
569                    let script = spt
570                        .get_spendable()
571                        .get_scripts()
572                        .get(script_idx)
573                        .ok_or(TxError::NoScriptAtIndex(script_idx))?;
574                    let sighash_type = signature_id
575                        .get_deposit_sig_owner()
576                        .map(|s| s.sighash_type())?
577                        .unwrap_or(TapSighashType::Default);
578
579                    use crate::builder::script::ScriptKind as Kind;
580
581                    let mut witness = match script.kind() {
582                        Kind::WinternitzCommit(script) => {
583                            if script.checksig_pubkey != self.xonly_public_key {
584                                return Err(TxError::NotOwnedScriptPath.into());
585                            }
586
587                            let mut script_data = Vec::with_capacity(data.len());
588                            for (data, path) in data {
589                                let secret_key = self.get_derived_winternitz_sk(path.clone())?;
590                                script_data.push((data.clone(), secret_key));
591                            }
592                            script.generate_script_inputs(
593                                &script_data,
594                                &taproot::Signature {
595                                    signature: self.sign(calc_sighash(sighash_type)?),
596                                    sighash_type,
597                                },
598                            )
599                        }
600                        Kind::PreimageRevealScript(_)
601                        | Kind::CheckSig(_)
602                        | Kind::Other(_)
603                        | Kind::BaseDepositScript(_)
604                        | Kind::ReplacementDepositScript(_)
605                        | Kind::TimelockScript(_)
606                        | Kind::ManualSpend(_) => return Ok(None),
607                    };
608
609                    if signed_winternitz {
610                        return Err(eyre::eyre!("Encountered multiple winternitz scripts when attempting to commit to only one.").into());
611                    }
612
613                    signed_winternitz = true;
614
615                    Self::add_script_path_to_witness(
616                        &mut witness,
617                        &script.to_script_buf(),
618                        spendinfo,
619                    )?;
620
621                    // BIP341 annex must be the last witness element.
622                    #[cfg(test)]
623                    {
624                        if let Some(ref annex) = self.annex {
625                            if matches!(
626                                signature_id.get_deposit_sig_owner()?,
627                                DepositSigKeyOwner::Own(_)
628                            ) {
629                                witness.push(annex);
630                            }
631                        }
632                    }
633
634                    Ok(Some(witness))
635                }
636                SpendPath::KeySpend => Ok(None),
637                SpendPath::Unknown => Err(TxError::SpendPathNotSpecified.into()),
638            }
639        };
640
641        txhandler.sign_txins(signer)?;
642        Ok(())
643    }
644
645    pub fn tx_sign_and_fill_sigs(
646        &self,
647        txhandler: &mut TxHandler,
648        signatures: &[TaggedSignature],
649        mut tweak_cache: Option<&mut TweakCache>,
650    ) -> Result<(), BridgeError> {
651        #[cfg(test)]
652        {
653            txhandler.set_test_annex(self.annex.clone());
654        }
655
656        let tx_type = txhandler.get_transaction_type();
657        let signer = move |_,
658                           spt: &SpentTxIn,
659                           calc_sighash: SighashCalculator<'_>|
660              -> Result<Option<Witness>, BridgeError> {
661            let spendinfo = spt
662                .get_spendable()
663                .get_spend_info()
664                .as_ref()
665                .ok_or(TxError::MissingSpendInfo)?;
666            let sighash_type = spt
667                .get_signature_id()
668                .get_deposit_sig_owner()
669                .map(|s| s.sighash_type())?
670                .unwrap_or(TapSighashType::Default);
671
672            // Common calculations across all spend paths
673            let signature_id = spt.get_signature_id();
674            let sig = Self::get_saved_signature(signature_id, signatures);
675            let sighash = calc_sighash(sighash_type)?;
676
677            match spt.get_spend_path() {
678                SpendPath::ScriptSpend(script_idx) => {
679                    let script = spt
680                        .get_spendable()
681                        .get_scripts()
682                        .get(script_idx)
683                        .ok_or(TxError::NoScriptAtIndex(script_idx))?;
684
685                    let sig = sig.map(|sig| taproot::Signature {
686                        signature: sig,
687                        sighash_type,
688                    });
689
690                    use crate::builder::script::ScriptKind as Kind;
691
692                    // Set the script inputs of the witness
693                    let mut witness: Witness = match script.kind() {
694                        Kind::BaseDepositScript(script) => {
695                            match (sig, script.0 == self.xonly_public_key) {
696                                (Some(sig), _) => {
697                                    Self::verify_signature(
698                                        &sig.signature,
699                                        sighash,
700                                        script.0,
701                                        TapTweakData::ScriptPath,
702                                        &signature_id,
703                                    )?;
704                                    script.generate_script_inputs(&sig)
705                                }
706                                (None, true) => {
707                                    script.generate_script_inputs(&taproot::Signature {
708                                        signature: self.sign(sighash),
709                                        sighash_type,
710                                    })
711                                }
712                                (None, false) => {
713                                    return Err(TxError::SignatureNotFound(tx_type).into())
714                                }
715                            }
716                        }
717                        Kind::ReplacementDepositScript(script) => {
718                            match (sig, script.0 == self.xonly_public_key) {
719                                (Some(sig), _) => {
720                                    Self::verify_signature(
721                                        &sig.signature,
722                                        sighash,
723                                        script.0,
724                                        TapTweakData::ScriptPath,
725                                        &signature_id,
726                                    )?;
727                                    script.generate_script_inputs(&sig)
728                                }
729                                (None, true) => {
730                                    script.generate_script_inputs(&taproot::Signature {
731                                        signature: self.sign(sighash),
732                                        sighash_type,
733                                    })
734                                }
735                                (None, false) => {
736                                    return Err(TxError::SignatureNotFound(tx_type).into());
737                                }
738                            }
739                        }
740                        Kind::TimelockScript(script) => match (sig, script.0) {
741                            (Some(sig), Some(xonly_pk)) => {
742                                Self::verify_signature(
743                                    &sig.signature,
744                                    sighash,
745                                    xonly_pk,
746                                    TapTweakData::ScriptPath,
747                                    &signature_id,
748                                )?;
749                                script.generate_script_inputs(Some(&sig))
750                            }
751                            (None, Some(xonly_key)) if xonly_key == self.xonly_public_key => script
752                                .generate_script_inputs(Some(&taproot::Signature {
753                                    signature: self.sign(sighash),
754                                    sighash_type,
755                                })),
756                            (None, Some(_)) => {
757                                return Err(TxError::SignatureNotFound(tx_type).into())
758                            }
759                            (_, None) => Witness::new(),
760                        },
761                        Kind::CheckSig(script) => match (sig, script.0 == self.xonly_public_key) {
762                            (Some(sig), _) => {
763                                Self::verify_signature(
764                                    &sig.signature,
765                                    sighash,
766                                    script.0,
767                                    TapTweakData::ScriptPath,
768                                    &signature_id,
769                                )?;
770                                script.generate_script_inputs(&sig)
771                            }
772
773                            (None, true) => script.generate_script_inputs(&taproot::Signature {
774                                signature: self.sign(sighash),
775                                sighash_type,
776                            }),
777                            (None, false) => return Err(TxError::SignatureNotFound(tx_type).into()),
778                        },
779                        Kind::WinternitzCommit(_)
780                        | Kind::PreimageRevealScript(_)
781                        | Kind::Other(_)
782                        | Kind::ManualSpend(_) => return Ok(None),
783                    };
784
785                    // Add P2TR elements (control block and script) to the witness
786                    Self::add_script_path_to_witness(
787                        &mut witness,
788                        &script.to_script_buf(),
789                        spendinfo,
790                    )?;
791
792                    // BIP341 annex must be the last witness element.
793                    #[cfg(test)]
794                    {
795                        if let Some(ref annex) = self.annex {
796                            if matches!(
797                                signature_id.get_deposit_sig_owner()?,
798                                DepositSigKeyOwner::Own(_)
799                            ) {
800                                witness.push(annex);
801                            }
802                        }
803                    }
804
805                    Ok(Some(witness))
806                }
807                SpendPath::KeySpend => {
808                    let xonly_public_key = spendinfo.internal_key();
809                    let sig = match sig {
810                        Some(sig) => {
811                            Self::verify_signature(
812                                &sig,
813                                sighash,
814                                xonly_public_key,
815                                TapTweakData::KeyPath(spendinfo.merkle_root()),
816                                &signature_id,
817                            )?;
818                            taproot::Signature {
819                                signature: sig,
820                                sighash_type,
821                            }
822                        }
823                        None => {
824                            if xonly_public_key == self.xonly_public_key {
825                                taproot::Signature {
826                                    signature: self.sign_with_tweak(
827                                        sighash,
828                                        spendinfo.merkle_root(),
829                                        tweak_cache.as_deref_mut(),
830                                    )?,
831                                    sighash_type,
832                                }
833                            } else {
834                                return Err(TxError::NotOwnKeyPath.into());
835                            }
836                        }
837                    };
838                    #[cfg(test)]
839                    {
840                        let mut witness = Witness::from_slice(&[&sig.serialize()]);
841                        if let Some(ref annex) = self.annex {
842                            if matches!(
843                                signature_id.get_deposit_sig_owner()?,
844                                DepositSigKeyOwner::Own(_)
845                            ) {
846                                witness.push(annex);
847                            }
848                        }
849                        Ok(Some(witness))
850                    }
851
852                    #[cfg(not(test))]
853                    {
854                        Ok(Some(Witness::from_slice(&[&sig.serialize()])))
855                    }
856                }
857                SpendPath::Unknown => Err(TxError::SpendPathNotSpecified.into()),
858            }
859        };
860
861        txhandler.sign_txins(signer)?;
862        Ok(())
863    }
864
865    /// Verifies a schnorr signature with the given parameters and wraps errors with signature ID context.
866    fn verify_signature(
867        sig: &schnorr::Signature,
868        sighash: TapSighash,
869        xonly_public_key: XOnlyPublicKey,
870        tweak_data: TapTweakData,
871        signature_id: &SignatureId,
872    ) -> Result<(), BridgeError> {
873        verify_schnorr(
874            sig,
875            &Message::from(sighash),
876            xonly_public_key,
877            tweak_data,
878            None,
879        )
880        .wrap_err(format!(
881            "Failed to verify signature from DB for signature {signature_id:?} for signer xonly pk {xonly_public_key}"
882        ))
883        .map_err(Into::into)
884    }
885}
886
887#[cfg(test)]
888mod tests {
889    use super::Actor;
890    use crate::builder::address::create_taproot_address;
891    use crate::config::protocol::ProtocolParamsetName;
892
893    use super::*;
894    use crate::builder::script::{CheckSig, SpendPath, SpendableScript};
895    use crate::builder::transaction::input::SpendableTxIn;
896    use crate::builder::transaction::output::UnspentTxOut;
897    use crate::builder::transaction::{TxHandler, TxHandlerBuilder};
898    use clementine_errors::TransactionType;
899
900    use crate::bitvm_client::SECP;
901    use crate::rpc::clementine::NormalSignatureKind;
902    use crate::{actor::WinternitzDerivationPath, test::common::*};
903    use bitcoin::secp256k1::{schnorr, Message, SecretKey};
904
905    use bitcoin::sighash::TapSighashType;
906    use bitcoin::transaction::Transaction;
907
908    use bitcoin::secp256k1::rand;
909    use bitcoin::{Amount, Network, OutPoint, Txid};
910    use bitcoincore_rpc::RpcApi;
911    use bitvm::{
912        execute_script,
913        signatures::winternitz::{self, BinarysearchVerifier, ToBytesConverter, Winternitz},
914        treepp::script,
915    };
916    use rand::thread_rng;
917    use std::str::FromStr;
918    use std::sync::Arc;
919
920    // Helper: create a TxHandler with a single key spend input.
921    fn create_key_spend_tx_handler(actor: &Actor) -> (bitcoin::TxOut, TxHandler) {
922        let (tap_addr, spend_info) =
923            create_taproot_address(&[], Some(actor.xonly_public_key), Network::Regtest);
924        // Build a transaction with one input that expects a key spend signature.
925        let prevtxo = bitcoin::TxOut {
926            value: Amount::from_sat(1000),
927            script_pubkey: tap_addr.script_pubkey(),
928        };
929        let builder = TxHandlerBuilder::new(TransactionType::Dummy).add_input(
930            NormalSignatureKind::Reimburse2,
931            SpendableTxIn::new(
932                OutPoint::default(),
933                prevtxo.clone(),
934                vec![],
935                Some(spend_info),
936            ),
937            SpendPath::KeySpend,
938            bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
939        );
940
941        (
942            prevtxo,
943            builder
944                .add_output(UnspentTxOut::new(
945                    bitcoin::TxOut {
946                        value: Amount::from_sat(999),
947                        script_pubkey: actor.address.script_pubkey(),
948                    },
949                    vec![],
950                    None,
951                ))
952                .finalize(),
953        )
954    }
955
956    // Helper: create a dummy CheckSig script for script spend.
957    fn create_dummy_checksig_script(actor: &Actor) -> CheckSig {
958        // Use a trivial script that is expected to be spent via a signature.
959        // In production this would be a proper P2TR script.
960        CheckSig(actor.xonly_public_key)
961    }
962
963    // Helper: create a TxHandler with a single script spend input using CheckSig.
964    fn create_script_spend_tx_handler(actor: &Actor) -> (bitcoin::TxOut, TxHandler) {
965        // Create a dummy spendable input that carries a script.
966        // Here we simulate that the spendable has one script: a CheckSig script.
967        let script = create_dummy_checksig_script(actor);
968
969        let (tap_addr, spend_info) = create_taproot_address(
970            &[script.to_script_buf()],
971            Some(actor.xonly_public_key),
972            Network::Regtest,
973        );
974
975        let prevutxo = bitcoin::TxOut {
976            value: Amount::from_sat(1000),
977            script_pubkey: tap_addr.script_pubkey(),
978        };
979        let spendable_input = SpendableTxIn::new(
980            OutPoint::default(),
981            prevutxo.clone(),
982            vec![Arc::new(script)],
983            Some(spend_info),
984        );
985
986        let builder = TxHandlerBuilder::new(TransactionType::Dummy).add_input(
987            NormalSignatureKind::KickoffNotFinalized1,
988            spendable_input,
989            SpendPath::ScriptSpend(0),
990            bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
991        );
992
993        (
994            prevutxo,
995            builder
996                .add_output(UnspentTxOut::new(
997                    bitcoin::TxOut {
998                        value: Amount::from_sat(999),
999                        script_pubkey: actor.address.script_pubkey(),
1000                    },
1001                    vec![],
1002                    None,
1003                ))
1004                .finalize(),
1005        )
1006    }
1007
1008    #[test]
1009    fn test_actor_key_spend_verification() {
1010        let sk = SecretKey::new(&mut thread_rng());
1011        let actor = Actor::new(sk, Network::Regtest);
1012        let (utxo, mut txhandler) = create_key_spend_tx_handler(&actor);
1013
1014        // Actor signs the key spend input.
1015        actor
1016            .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1017            .expect("Key spend signature should succeed");
1018
1019        // Retrieve the cached transaction from the txhandler.
1020        let tx: &Transaction = txhandler.get_cached_tx();
1021
1022        tx.verify(|_| Some(utxo.clone()))
1023            .expect("Expected valid signature for key spend");
1024    }
1025
1026    #[test]
1027    fn test_actor_script_spend_tx_valid() {
1028        let sk = SecretKey::new(&mut thread_rng());
1029        let actor = Actor::new(sk, Network::Regtest);
1030        let (prevutxo, mut txhandler) = create_script_spend_tx_handler(&actor);
1031
1032        // Actor performs a partial sign for script spend.
1033        // Using an empty signature slice since our dummy CheckSig uses actor signature.
1034        let signatures: Vec<_> = vec![];
1035        actor
1036            .tx_sign_and_fill_sigs(&mut txhandler, &signatures, None)
1037            .expect("Script spend partial sign should succeed");
1038
1039        // Retrieve the cached transaction.
1040        let tx: &Transaction = txhandler.get_cached_tx();
1041
1042        tx.verify(|_| Some(prevutxo.clone()))
1043            .expect("Invalid transaction");
1044    }
1045
1046    #[test]
1047    fn test_actor_script_spend_sig_valid() {
1048        let sk = SecretKey::new(&mut thread_rng());
1049        let actor = Actor::new(sk, Network::Regtest);
1050        let (_, mut txhandler) = create_script_spend_tx_handler(&actor);
1051
1052        // Actor performs a partial sign for script spend.
1053        // Using an empty signature slice since our dummy CheckSig uses actor signature.
1054        let signatures: Vec<_> = vec![];
1055        actor
1056            .tx_sign_and_fill_sigs(&mut txhandler, &signatures, None)
1057            .expect("Script spend partial sign should succeed");
1058
1059        // Retrieve the cached transaction.
1060        let tx: &Transaction = txhandler.get_cached_tx();
1061
1062        // For script spend, we extract the witness from the corresponding input.
1063        // Our dummy witness is expected to contain the signature.
1064        let witness = &tx.input[0].witness;
1065        assert!(!witness.is_empty(), "Witness should not be empty");
1066        let sig = schnorr::Signature::from_slice(&witness[0])
1067            .expect("Failed to parse Schnorr signature from witness");
1068
1069        // Compute the sighash expected for a pubkey spend (similar to key spend).
1070        let sighash = txhandler
1071            .calculate_script_spend_sighash_indexed(0, 0, TapSighashType::Default)
1072            .expect("Sighash computed");
1073
1074        let message = Message::from_digest(*sighash.as_byte_array());
1075        SECP.verify_schnorr(&sig, &message, &actor.xonly_public_key)
1076            .expect("Script spend signature verification failed");
1077    }
1078
1079    #[test]
1080    fn actor_new() {
1081        let sk = SecretKey::new(&mut rand::thread_rng());
1082        let network = Network::Regtest;
1083
1084        let actor = Actor::new(sk, network);
1085
1086        assert_eq!(sk.public_key(&SECP), actor.public_key);
1087        assert_eq!(sk.x_only_public_key(&SECP).0, actor.xonly_public_key);
1088    }
1089
1090    #[test]
1091    fn sign_taproot_pubkey_spend() {
1092        let sk = SecretKey::new(&mut rand::thread_rng());
1093        let network = Network::Regtest;
1094        let actor = Actor::new(sk, network);
1095
1096        // This transaction is matching with prevouts. Therefore signing will
1097        // be successful.
1098        let tx_handler = create_key_spend_tx_handler(&actor).1;
1099        let sighash = tx_handler
1100            .calculate_pubkey_spend_sighash(0, bitcoin::TapSighashType::Default)
1101            .expect("calculating pubkey spend sighash");
1102
1103        let signature = actor.sign(sighash);
1104
1105        let message = Message::from_digest(*sighash.as_byte_array());
1106        SECP.verify_schnorr(&signature, &message, &actor.xonly_public_key)
1107            .expect("invalid signature");
1108    }
1109
1110    #[test]
1111    fn sign_taproot_pubkey_spend_tx_with_sighash() {
1112        let sk = SecretKey::new(&mut rand::thread_rng());
1113        let network = Network::Regtest;
1114        let actor = Actor::new(sk, network);
1115
1116        // This transaction is matching with prevouts. Therefore signing will
1117        // be successful.
1118        let tx_handler = create_key_spend_tx_handler(&actor).1;
1119        let x = tx_handler
1120            .calculate_pubkey_spend_sighash(0, TapSighashType::Default)
1121            .unwrap();
1122        actor.sign_with_tweak(x, None, None).unwrap();
1123    }
1124
1125    #[tokio::test]
1126    async fn derive_winternitz_pk_uniqueness() {
1127        let paramset: &'static ProtocolParamset = ProtocolParamsetName::Regtest.into();
1128        let config = create_test_config_with_thread_name().await;
1129        let actor = Actor::new(config.secret_key, Network::Regtest);
1130
1131        let mut params = WinternitzDerivationPath::Kickoff(RoundIndex::Round(0), 0, paramset);
1132        let pk0 = actor.derive_winternitz_pk(params.clone()).unwrap();
1133        let pk1 = actor.derive_winternitz_pk(params).unwrap();
1134        assert_eq!(pk0, pk1);
1135
1136        params = WinternitzDerivationPath::Kickoff(RoundIndex::Round(0), 1, paramset);
1137        let pk2 = actor.derive_winternitz_pk(params).unwrap();
1138        assert_ne!(pk0, pk2);
1139    }
1140
1141    impl TweakCache {
1142        fn get_tweaked_xonly_key_cache_size(&self) -> usize {
1143            self.tweaked_key_cache.len()
1144        }
1145        fn get_tweaked_keypair_cache_size(&self) -> usize {
1146            self.tweaked_keypair_cache.len()
1147        }
1148    }
1149
1150    #[tokio::test]
1151    async fn test_tweak_cache() {
1152        let mut tweak_cache = TweakCache::default();
1153        let sk = SecretKey::new(&mut rand::thread_rng());
1154        let keypair = Keypair::from_secret_key(&SECP, &sk);
1155        let sk2 = SecretKey::new(&mut rand::thread_rng());
1156        let keypair2 = Keypair::from_secret_key(&SECP, &sk2);
1157        let sk3 = SecretKey::new(&mut rand::thread_rng());
1158        let keypair3 = Keypair::from_secret_key(&SECP, &sk3);
1159
1160        tweak_cache.get_tweaked_keypair(&keypair, None).unwrap();
1161        assert!(tweak_cache.get_tweaked_keypair_cache_size() == 1);
1162        tweak_cache
1163            .get_tweaked_keypair(&keypair, Some(TapNodeHash::assume_hidden([0x55; 32])))
1164            .unwrap();
1165        assert!(tweak_cache.get_tweaked_keypair_cache_size() == 2);
1166        tweak_cache
1167            .get_tweaked_keypair(&keypair, Some(TapNodeHash::assume_hidden([0x56; 32])))
1168            .unwrap();
1169        assert!(tweak_cache.get_tweaked_keypair_cache_size() == 3);
1170        tweak_cache
1171            .get_tweaked_keypair(&keypair, Some(TapNodeHash::assume_hidden([0x57; 32])))
1172            .unwrap();
1173        assert!(tweak_cache.get_tweaked_keypair_cache_size() == 4);
1174        tweak_cache
1175            .get_tweaked_keypair(&keypair, Some(TapNodeHash::assume_hidden([0x55; 32])))
1176            .unwrap();
1177        tweak_cache.get_tweaked_keypair(&keypair, None).unwrap();
1178        assert!(tweak_cache.get_tweaked_keypair_cache_size() == 4);
1179        tweak_cache.get_tweaked_keypair(&keypair2, None).unwrap();
1180        assert!(tweak_cache.get_tweaked_keypair_cache_size() == 5);
1181        let xonly_pk1 = keypair.x_only_public_key();
1182        let xonly_pk2 = keypair2.x_only_public_key();
1183        let xonly_pk3 = keypair3.x_only_public_key();
1184
1185        // Test for get_tweaked_xonly_key
1186        tweak_cache
1187            .get_tweaked_xonly_key(xonly_pk1.0, None)
1188            .unwrap();
1189        assert!(tweak_cache.get_tweaked_xonly_key_cache_size() == 1);
1190        tweak_cache
1191            .get_tweaked_xonly_key(xonly_pk1.0, Some(TapNodeHash::assume_hidden([0x55; 32])))
1192            .unwrap();
1193        assert!(tweak_cache.get_tweaked_xonly_key_cache_size() == 2);
1194        tweak_cache
1195            .get_tweaked_xonly_key(xonly_pk2.0, Some(TapNodeHash::assume_hidden([0x55; 32])))
1196            .unwrap();
1197        assert!(tweak_cache.get_tweaked_xonly_key_cache_size() == 3);
1198        tweak_cache
1199            .get_tweaked_xonly_key(xonly_pk3.0, Some(TapNodeHash::assume_hidden([0x55; 32])))
1200            .unwrap();
1201        assert!(tweak_cache.get_tweaked_xonly_key_cache_size() == 4);
1202        tweak_cache
1203            .get_tweaked_xonly_key(xonly_pk1.0, None)
1204            .unwrap();
1205        tweak_cache
1206            .get_tweaked_xonly_key(xonly_pk3.0, Some(TapNodeHash::assume_hidden([0x55; 32])))
1207            .unwrap();
1208        assert!(tweak_cache.get_tweaked_xonly_key_cache_size() == 4);
1209    }
1210
1211    #[tokio::test]
1212    async fn derive_winternitz_pk_fixed_pk() {
1213        let paramset: &'static ProtocolParamset = ProtocolParamsetName::Regtest.into();
1214        let actor = Actor::new(
1215            SecretKey::from_str("451F451F451F451F451F451F451F451F451F451F451F451F451F451F451F451F")
1216                .unwrap(),
1217            Network::Regtest,
1218        );
1219        // Test so that same path always returns the same public key (to not change it accidentally)
1220        // check only first digit
1221        let params = WinternitzDerivationPath::Kickoff(RoundIndex::Round(0), 1, paramset);
1222        let expected_pk = vec![
1223            101, 197, 179, 64, 250, 67, 109, 29, 241, 138, 5, 24, 94, 33, 175, 150, 152, 91, 168,
1224            177,
1225        ];
1226        assert_eq!(
1227            actor.derive_winternitz_pk(params).unwrap()[0].to_vec(),
1228            expected_pk
1229        );
1230
1231        let deposit_outpoint = OutPoint {
1232            txid: Txid::all_zeros(),
1233            vout: 1,
1234        };
1235
1236        let params = WinternitzDerivationPath::BitvmAssert(3, 0, 0, deposit_outpoint, paramset);
1237        let expected_pk = vec![
1238            175, 225, 87, 0, 121, 25, 91, 88, 22, 210, 26, 117, 146, 84, 228, 150, 199, 181, 186,
1239            33,
1240        ];
1241        assert_eq!(
1242            actor.derive_winternitz_pk(params).unwrap()[0].to_vec(),
1243            expected_pk
1244        );
1245
1246        let params = WinternitzDerivationPath::ChallengeAckHash(0, deposit_outpoint, paramset);
1247        let expected_pk = vec![
1248            247, 46, 220, 228, 70, 245, 147, 30, 64, 207, 189, 137, 222, 217, 244, 96, 68, 114,
1249            243, 13,
1250        ];
1251        assert_eq!(
1252            actor.derive_winternitz_pk(params).unwrap()[0].to_vec(),
1253            expected_pk
1254        );
1255    }
1256
1257    #[tokio::test]
1258    async fn sign_winternitz_signature() {
1259        let config = create_test_config_with_thread_name().await;
1260        let actor = Actor::new(config.secret_key, Network::Regtest);
1261
1262        let data = "iwantporscheasagiftpls".as_bytes().to_vec();
1263        let message_len = data.len() as u32 * 2;
1264        let paramset: &'static ProtocolParamset = ProtocolParamsetName::Regtest.into();
1265
1266        let deposit_outpoint = OutPoint {
1267            txid: Txid::all_zeros(),
1268            vout: 1,
1269        };
1270
1271        let path =
1272            WinternitzDerivationPath::BitvmAssert(message_len, 0, 0, deposit_outpoint, paramset);
1273        let params = winternitz::Parameters::new(message_len, paramset.winternitz_log_d);
1274
1275        let witness = actor
1276            .sign_winternitz_signature(path.clone(), data.clone())
1277            .unwrap();
1278        let pk = actor.derive_winternitz_pk(path.clone()).unwrap();
1279
1280        let winternitz = Winternitz::<BinarysearchVerifier, ToBytesConverter>::new();
1281        let check_sig_script = winternitz.checksig_verify(&params, &pk);
1282
1283        let message_checker = script! {
1284            for i in 0..message_len / 2 {
1285                {data[i as usize]}
1286                if i == message_len / 2 - 1 {
1287                    OP_EQUAL
1288                } else {
1289                    OP_EQUALVERIFY
1290                }
1291            }
1292        };
1293
1294        let script = script!({witness} {check_sig_script} {message_checker});
1295        let ret = execute_script(script);
1296        assert!(ret.success);
1297    }
1298
1299    #[tokio::test]
1300    async fn test_key_spend_signing() {
1301        // Setup test node and actor
1302        let mut config = create_test_config_with_thread_name().await;
1303        let regtest = create_regtest_rpc(&mut config).await;
1304        let rpc = regtest.rpc();
1305        let sk = SecretKey::new(&mut thread_rng());
1306        let actor = Actor::new(sk, Network::Regtest);
1307
1308        // Create a UTXO controlled by the actor's key spend path
1309        let (tap_addr, spend_info) =
1310            create_taproot_address(&[], Some(actor.xonly_public_key), Network::Regtest);
1311        let prevtxo = bitcoin::TxOut {
1312            value: Amount::from_sat(50000), // Use a reasonable amount
1313            script_pubkey: tap_addr.script_pubkey(),
1314        };
1315
1316        // Fund the address (required for testmempoolaccept)
1317        let outpoint = rpc
1318            .send_to_address(&tap_addr, Amount::from_sat(50000))
1319            .await
1320            .unwrap();
1321
1322        rpc.mine_blocks(1).await.unwrap(); // Confirm the funding transaction
1323
1324        // Build a transaction spending the UTXO with TapSighashType::SinglePlusAnyoneCanPay
1325        let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
1326            // Use Challenge which maps to NofnSharedDeposit(TapSighashType::SinglePlusAnyoneCanPay)
1327            .add_input(
1328                NormalSignatureKind::Challenge,
1329                SpendableTxIn::new(outpoint, prevtxo.clone(), vec![], Some(spend_info.clone())),
1330                SpendPath::KeySpend,
1331                bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
1332            );
1333
1334        // Add a dummy output
1335        builder = builder.add_output(UnspentTxOut::new(
1336            bitcoin::TxOut {
1337                value: Amount::from_sat(49000), // Account for fee
1338                script_pubkey: actor.address.script_pubkey(),
1339            },
1340            vec![],
1341            None,
1342        ));
1343
1344        let mut txhandler = builder.finalize();
1345
1346        // Actor signs the key spend input using the non-default sighash type
1347        actor
1348            .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1349            .expect("Key spend signature with SighashNone should succeed");
1350
1351        // Retrieve the signed transaction
1352        let tx: &Transaction = txhandler.get_cached_tx();
1353
1354        // Use testmempoolaccept to verify the transaction is valid by consensus rules
1355        let mempool_accept_result = rpc.test_mempool_accept(&[tx]).await.unwrap();
1356
1357        assert!(
1358            mempool_accept_result[0].allowed.unwrap(),
1359            "Transaction should be allowed in mempool. Rejection reason: {:?}",
1360            mempool_accept_result[0].reject_reason.as_ref().unwrap()
1361        );
1362
1363        // Build a transaction spending the UTXO with TapSighashType::Default
1364        let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
1365            // Use Reimburse2 which maps to NofnSharedDeposit(TapSighashType::Default)
1366            .add_input(
1367                NormalSignatureKind::Reimburse2,
1368                SpendableTxIn::new(outpoint, prevtxo.clone(), vec![], Some(spend_info.clone())),
1369                SpendPath::KeySpend,
1370                bitcoin::Sequence::ENABLE_RBF_NO_LOCKTIME,
1371            );
1372
1373        // Add a dummy output
1374        builder = builder.add_output(UnspentTxOut::new(
1375            bitcoin::TxOut {
1376                value: Amount::from_sat(39000), // Account for fee
1377                script_pubkey: actor.address.script_pubkey(),
1378            },
1379            vec![],
1380            None,
1381        ));
1382
1383        let mut txhandler = builder.finalize();
1384
1385        // Actor signs the key spend input using the non-default sighash type
1386        actor
1387            .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1388            .expect("Key spend signature with SighashDefault should succeed");
1389
1390        // Retrieve the signed transaction
1391        let tx: &Transaction = txhandler.get_cached_tx();
1392
1393        // Use testmempoolaccept to verify the transaction is valid by consensus rules
1394        let mempool_accept_result = rpc.test_mempool_accept(&[tx]).await.unwrap();
1395
1396        assert!(
1397            mempool_accept_result[0].allowed.unwrap(),
1398            "Transaction should be allowed in mempool. Rejection reason: {:?}",
1399            mempool_accept_result[0].reject_reason.as_ref().unwrap()
1400        );
1401    }
1402}