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