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 Kickoff(RoundIndex, u32, &'static ProtocolParamset),
46 BitvmAssert(u32, u32, u32, OutPoint, &'static ProtocolParamset),
48 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 _ => 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 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)]
154pub struct TweakCache {
160 tweaked_key_cache: HashMap<(XOnlyPublicKey, Option<TapNodeHash>), XOnlyPublicKey>,
161 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 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 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 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 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 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 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 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 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 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 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 fn create_dummy_checksig_script(actor: &Actor) -> CheckSig {
865 CheckSig(actor.xonly_public_key)
868 }
869
870 fn create_script_spend_tx_handler(actor: &Actor) -> (bitcoin::TxOut, TxHandler) {
872 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
923 .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
924 .expect("Key spend signature should succeed");
925
926 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 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 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 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 let tx: &Transaction = txhandler.get_cached_tx();
968
969 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 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 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 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 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 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(¶ms, &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 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 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), script_pubkey: tap_addr.script_pubkey(),
1221 };
1222
1223 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(); let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
1233 .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 builder = builder.add_output(UnspentTxOut::new(
1243 bitcoin::TxOut {
1244 value: Amount::from_sat(49000), script_pubkey: actor.address.script_pubkey(),
1246 },
1247 vec![],
1248 None,
1249 ));
1250
1251 let mut txhandler = builder.finalize();
1252
1253 actor
1255 .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1256 .expect("Key spend signature with SighashNone should succeed");
1257
1258 let tx: &Transaction = txhandler.get_cached_tx();
1260
1261 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 let mut builder = TxHandlerBuilder::new(TransactionType::Dummy)
1272 .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 builder = builder.add_output(UnspentTxOut::new(
1282 bitcoin::TxOut {
1283 value: Amount::from_sat(39000), script_pubkey: actor.address.script_pubkey(),
1285 },
1286 vec![],
1287 None,
1288 ));
1289
1290 let mut txhandler = builder.finalize();
1291
1292 actor
1294 .tx_sign_and_fill_sigs(&mut txhandler, &[], None)
1295 .expect("Key spend signature with SighashDefault should succeed");
1296
1297 let tx: &Transaction = txhandler.get_cached_tx();
1299
1300 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}