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