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