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 Kickoff(RoundIndex, u32, &'static ProtocolParamset),
41 BitvmAssert(u32, u32, u32, OutPoint, &'static ProtocolParamset),
43 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 _ => 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 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)]
149pub struct TweakCache {
155 tweaked_key_cache: HashMap<(XOnlyPublicKey, Option<TapNodeHash>), XOnlyPublicKey>,
156 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 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 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 #[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 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 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 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 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 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 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 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 fn create_dummy_checksig_script(actor: &Actor) -> CheckSig {
880 CheckSig(actor.xonly_public_key)
883 }
884
885 fn create_script_spend_tx_handler(actor: &Actor) -> (bitcoin::TxOut, TxHandler) {
887 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
938 .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
939 .expect("Key spend signature should succeed");
940
941 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 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 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 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 let tx: &Transaction = txhandler.get_cached_tx();
983
984 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 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 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 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 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 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(¶ms, &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 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 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), script_pubkey: tap_addr.script_pubkey(),
1236 };
1237
1238 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(); let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
1248 .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 builder = builder.add_output(UnspentTxOut::new(
1258 bitcoin::TxOut {
1259 value: Amount::from_sat(49000), script_pubkey: actor.address.script_pubkey(),
1261 },
1262 vec![],
1263 None,
1264 ));
1265
1266 let mut txhandler = builder.finalize();
1267
1268 actor
1270 .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1271 .expect("Key spend signature with SighashNone should succeed");
1272
1273 let tx: &Transaction = txhandler.get_cached_tx();
1275
1276 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 let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
1287 .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 builder = builder.add_output(UnspentTxOut::new(
1297 bitcoin::TxOut {
1298 value: Amount::from_sat(39000), script_pubkey: actor.address.script_pubkey(),
1300 },
1301 vec![],
1302 None,
1303 ));
1304
1305 let mut txhandler = builder.finalize();
1306
1307 actor
1309 .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1310 .expect("Key spend signature with SighashDefault should succeed");
1311
1312 let tx: &Transaction = txhandler.get_cached_tx();
1314
1315 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}