1#![allow(dead_code)]
21
22use crate::actor::WinternitzDerivationPath;
23use crate::config::protocol::ProtocolParamset;
24use crate::deposit::SecurityCouncil;
25use bitcoin::hashes::Hash;
26use bitcoin::opcodes::OP_TRUE;
27use bitcoin::{
28 opcodes::{all::*, OP_FALSE},
29 script::Builder,
30 ScriptBuf, XOnlyPublicKey,
31};
32use bitcoin::{taproot, Txid, Witness};
33use bitvm::signatures::winternitz::{Parameters, PublicKey, SecretKey};
34use clementine_primitives::EVMAddress;
35use eyre::{Context, Result};
36use std::any::Any;
37use std::fmt::Debug;
38
39#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
40pub enum SpendPath {
41 ScriptSpend(usize),
42 KeySpend,
43 Unknown,
44}
45
46fn from_minimal_to_u32_le_bytes(minimal: &[u8]) -> Result<[u8; 4]> {
48 if minimal.len() > 4 {
49 return Err(eyre::eyre!("u32 bytes length is greater than 4"));
50 }
51 let mut bytes = [0u8; 4];
52 bytes[..minimal.len()].copy_from_slice(minimal);
53 Ok(bytes)
54}
55
56pub fn extract_winternitz_commits(
59 witness: Witness,
60 wt_derive_paths: &[WinternitzDerivationPath],
61 paramset: &'static ProtocolParamset,
62) -> Result<Vec<Vec<u8>>> {
63 if paramset.winternitz_log_d != 4 {
64 return Err(eyre::eyre!("Only winternitz_log_d = 4 is supported"));
65 }
66 let mut commits: Vec<Vec<u8>> = Vec::new();
67 let mut cur_witness_iter = witness.into_iter().skip(1);
68
69 for wt_path in wt_derive_paths.iter().rev() {
70 let wt_params = wt_path.get_params();
71 let message_digits =
72 (wt_params.message_byte_len() * 8).div_ceil(paramset.winternitz_log_d) as usize;
73 let checksum_digits = wt_params.total_digit_len() as usize - message_digits;
74
75 let mut elements: Vec<&[u8]> = cur_witness_iter
76 .by_ref()
77 .skip(1)
78 .step_by(2)
79 .take(message_digits)
80 .collect();
81 elements.reverse();
82
83 cur_witness_iter.by_ref().nth(checksum_digits * 2 - 1);
85
86 commits.push(
87 elements
88 .chunks_exact(2)
89 .map(|digits| {
90 let first_digit = u32::from_le_bytes(from_minimal_to_u32_le_bytes(digits[0])?);
91 let second_digit = u32::from_le_bytes(from_minimal_to_u32_le_bytes(digits[1])?);
92
93 let first_u8 = u8::try_from(first_digit)
94 .wrap_err("Failed to convert first digit to u8")?;
95 let second_u8 = u8::try_from(second_digit)
96 .wrap_err("Failed to convert second digit to u8")?;
97
98 Ok(second_u8 * (1 << paramset.winternitz_log_d) + first_u8)
99 })
100 .collect::<Result<Vec<_>>>()?,
101 );
102 }
103 commits.reverse();
104 Ok(commits)
105}
106
107pub fn extract_winternitz_commits_with_sigs(
110 witness: Witness,
111 wt_derive_paths: &[WinternitzDerivationPath],
112 paramset: &'static ProtocolParamset,
113) -> Result<Vec<Vec<Vec<u8>>>> {
114 if paramset.winternitz_log_d != 4 {
115 return Err(eyre::eyre!("Only winternitz_log_d = 4 is supported"));
116 }
117 let mut commits_with_sig: Vec<Vec<Vec<u8>>> = Vec::new();
122 let mut cur_witness_iter = witness.into_iter().skip(1);
123
124 for wt_path in wt_derive_paths.iter().rev() {
125 let wt_params = wt_path.get_params();
126 let message_digits =
127 (wt_params.message_byte_len() * 8).div_ceil(paramset.winternitz_log_d) as usize;
128 let checksum_digits = wt_params.total_digit_len() as usize - message_digits;
129
130 let elements: Vec<Vec<u8>> = cur_witness_iter
131 .by_ref()
132 .take((message_digits + checksum_digits) * 2)
133 .map(|x| x.to_vec())
134 .collect();
135
136 commits_with_sig.push(elements);
137 }
138
139 Ok(commits_with_sig)
140}
141
142pub trait SpendableScript: Send + Sync + 'static + std::any::Any {
151 fn as_any(&self) -> &dyn Any;
152
153 fn kind(&self) -> ScriptKind;
154
155 fn to_script_buf(&self) -> ScriptBuf;
156}
157
158impl Debug for dyn SpendableScript {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 write!(f, "SpendableScript")
161 }
162}
163
164#[derive(Debug, Clone)]
166pub struct OtherSpendable(ScriptBuf);
167
168impl From<ScriptBuf> for OtherSpendable {
169 fn from(script: ScriptBuf) -> Self {
170 Self(script)
171 }
172}
173
174impl SpendableScript for OtherSpendable {
175 fn as_any(&self) -> &dyn Any {
176 self
177 }
178
179 fn kind(&self) -> ScriptKind {
180 ScriptKind::Other(self)
181 }
182
183 fn to_script_buf(&self) -> ScriptBuf {
184 self.0.clone()
185 }
186}
187
188impl OtherSpendable {
189 fn as_script(&self) -> &ScriptBuf {
190 &self.0
191 }
192
193 fn generate_script_inputs(&self, witness: Witness) -> Witness {
194 witness
195 }
196
197 pub fn new(script: ScriptBuf) -> Self {
198 Self(script)
199 }
200}
201
202#[derive(Debug, Clone)]
204pub struct CheckSig(pub(crate) XOnlyPublicKey);
205impl SpendableScript for CheckSig {
206 fn as_any(&self) -> &dyn Any {
207 self
208 }
209
210 fn kind(&self) -> ScriptKind {
211 ScriptKind::CheckSig(self)
212 }
213
214 fn to_script_buf(&self) -> ScriptBuf {
215 Builder::new()
216 .push_x_only_key(&self.0)
217 .push_opcode(OP_CHECKSIG)
218 .into_script()
219 }
220}
221
222impl CheckSig {
223 pub fn generate_script_inputs(&self, signature: &taproot::Signature) -> Witness {
224 Witness::from_slice(&[signature.serialize()])
225 }
226
227 pub fn new(xonly_pk: XOnlyPublicKey) -> Self {
228 Self(xonly_pk)
229 }
230}
231
232#[derive(Clone)]
233pub struct Multisig {
234 pubkeys: Vec<XOnlyPublicKey>,
235 threshold: u32,
236}
237
238impl SpendableScript for Multisig {
239 fn as_any(&self) -> &dyn Any {
240 self
241 }
242
243 fn kind(&self) -> ScriptKind {
244 ScriptKind::ManualSpend(self)
245 }
246
247 fn to_script_buf(&self) -> ScriptBuf {
248 let mut script_builder = Builder::new()
249 .push_x_only_key(&self.pubkeys[0])
250 .push_opcode(OP_CHECKSIG);
251 for pubkey in self.pubkeys.iter().skip(1) {
252 script_builder = script_builder.push_x_only_key(pubkey);
253 script_builder = script_builder.push_opcode(OP_CHECKSIGADD);
254 }
255 script_builder = script_builder.push_int(self.threshold as i64);
256 script_builder = script_builder.push_opcode(OP_NUMEQUAL);
257 script_builder.into_script()
258 }
259}
260
261impl Multisig {
262 pub fn new(pubkeys: Vec<XOnlyPublicKey>, threshold: u32) -> Self {
263 Self { pubkeys, threshold }
264 }
265
266 pub fn from_security_council(security_council: SecurityCouncil) -> Self {
267 Self {
268 pubkeys: security_council.pks,
269 threshold: security_council.threshold,
270 }
271 }
272
273 pub fn generate_script_inputs(
274 &self,
275 signatures: &[Option<taproot::Signature>],
276 ) -> eyre::Result<Witness> {
277 let mut witness = Witness::new();
278
279 for signature in signatures.iter().rev() {
280 match signature {
281 Some(sig) => witness.push(sig.serialize()),
282 None => witness.push([]),
283 }
284 }
285 Ok(witness)
286 }
287}
288
289#[derive(Clone)]
293pub struct WinternitzCommit {
294 commitments: Vec<(PublicKey, u32)>,
295 pub(crate) checksig_pubkey: XOnlyPublicKey,
296 log_d: u32,
297}
298
299impl SpendableScript for WinternitzCommit {
300 fn as_any(&self) -> &dyn Any {
301 self
302 }
303
304 fn kind(&self) -> ScriptKind {
305 ScriptKind::WinternitzCommit(self)
306 }
307
308 fn to_script_buf(&self) -> ScriptBuf {
309 let mut total_script = ScriptBuf::new();
310 for (index, (pubkey, _size)) in self.commitments.iter().enumerate() {
311 let params = self.get_params(index);
312 let a = bitvm::signatures::signing_winternitz::WINTERNITZ_MESSAGE_VERIFIER
313 .checksig_verify_and_clear_stack(¶ms, pubkey);
314 total_script.extend(a.compile().instructions().map(|x| x.expect("just created")));
315 }
316
317 total_script.push_slice(self.checksig_pubkey.serialize());
318 total_script.push_opcode(OP_CHECKSIG);
319 total_script
320 }
321}
322
323impl WinternitzCommit {
324 pub fn get_params(&self, index: usize) -> Parameters {
325 Parameters::new(self.commitments[index].1, self.log_d)
326 }
327
328 pub fn generate_script_inputs(
329 &self,
330 commit_data: &[(Vec<u8>, SecretKey)],
331 signature: &taproot::Signature,
332 ) -> Witness {
333 let mut witness = Witness::new();
334 witness.push(signature.serialize());
335 for (index, (data, secret_key)) in commit_data.iter().enumerate().rev() {
336 #[cfg(debug_assertions)]
337 {
338 let pk = bitvm::signatures::winternitz::generate_public_key(
339 &self.get_params(index),
340 secret_key,
341 );
342 if pk != self.commitments[index].0 {
343 tracing::error!(
344 "Winternitz public key mismatch len: {}",
345 self.commitments[index].1
346 );
347 }
348 }
349 bitvm::signatures::signing_winternitz::WINTERNITZ_MESSAGE_VERIFIER
350 .sign(&self.get_params(index), secret_key, data)
351 .into_iter()
352 .for_each(|x| witness.push(x));
353 }
354 witness
355 }
356
357 pub fn new(
359 commitments: Vec<(PublicKey, u32)>,
360 checksig_pubkey: XOnlyPublicKey,
361 log_d: u32,
362 ) -> Self {
363 Self {
364 commitments,
365 checksig_pubkey,
366 log_d,
367 }
368 }
369}
370
371#[derive(Debug, Clone)]
383pub struct TimelockScript(pub(crate) Option<XOnlyPublicKey>, u16);
384
385impl SpendableScript for TimelockScript {
386 fn as_any(&self) -> &dyn Any {
387 self
388 }
389
390 fn kind(&self) -> ScriptKind {
391 ScriptKind::TimelockScript(self)
392 }
393
394 fn to_script_buf(&self) -> ScriptBuf {
395 let script_builder = Builder::new()
396 .push_int(self.1 as i64)
397 .push_opcode(OP_CSV)
398 .push_opcode(OP_DROP);
399
400 if let Some(xonly_pk) = self.0 {
401 script_builder
402 .push_x_only_key(&xonly_pk)
403 .push_opcode(OP_CHECKSIG)
404 } else {
405 script_builder.push_opcode(OP_TRUE)
406 }
407 .into_script()
408 }
409}
410
411impl TimelockScript {
412 pub fn generate_script_inputs(&self, signature: Option<&taproot::Signature>) -> Witness {
413 match signature {
414 Some(sig) => Witness::from_slice(&[sig.serialize()]),
415 None => Witness::default(),
416 }
417 }
418
419 pub fn new(xonly_pk: Option<XOnlyPublicKey>, block_count: u16) -> Self {
420 Self(xonly_pk, block_count)
421 }
422}
423
424pub struct PreimageRevealScript(pub(crate) XOnlyPublicKey, [u8; 20]);
426
427impl SpendableScript for PreimageRevealScript {
428 fn as_any(&self) -> &dyn Any {
429 self
430 }
431
432 fn kind(&self) -> ScriptKind {
433 ScriptKind::PreimageRevealScript(self)
434 }
435
436 fn to_script_buf(&self) -> ScriptBuf {
437 Builder::new()
438 .push_opcode(OP_SIZE)
439 .push_int(20)
440 .push_opcode(OP_EQUALVERIFY)
441 .push_opcode(OP_HASH160)
442 .push_slice(self.1)
443 .push_opcode(OP_EQUALVERIFY)
444 .push_x_only_key(&self.0)
445 .push_opcode(OP_CHECKSIG)
446 .into_script()
447 }
448}
449
450impl PreimageRevealScript {
451 pub fn generate_script_inputs(
452 &self,
453 preimage: impl AsRef<[u8]>,
454 signature: &taproot::Signature,
455 ) -> Witness {
456 let mut witness = Witness::new();
457 #[cfg(debug_assertions)]
458 assert_eq!(
459 bitcoin::hashes::hash160::Hash::hash(preimage.as_ref()),
460 bitcoin::hashes::hash160::Hash::from_byte_array(self.1),
461 "Preimage does not match"
462 );
463
464 witness.push(signature.serialize());
465 witness.push(preimage.as_ref());
466 witness
467 }
468
469 pub fn new(xonly_pk: XOnlyPublicKey, hash: [u8; 20]) -> Self {
470 Self(xonly_pk, hash)
471 }
472}
473
474#[derive(Debug, Clone)]
476pub struct BaseDepositScript(pub(crate) XOnlyPublicKey, EVMAddress);
477
478impl SpendableScript for BaseDepositScript {
479 fn as_any(&self) -> &dyn Any {
480 self
481 }
482
483 fn kind(&self) -> ScriptKind {
484 ScriptKind::BaseDepositScript(self)
485 }
486
487 fn to_script_buf(&self) -> ScriptBuf {
488 let citrea: [u8; 6] = "citrea".as_bytes().try_into().expect("length == 6");
489
490 Builder::new()
491 .push_x_only_key(&self.0)
492 .push_opcode(OP_CHECKSIG)
493 .push_opcode(OP_FALSE)
494 .push_opcode(OP_IF)
495 .push_slice(citrea)
496 .push_slice(self.1 .0)
497 .push_opcode(OP_ENDIF)
498 .into_script()
499 }
500}
501
502impl BaseDepositScript {
503 pub fn generate_script_inputs(&self, signature: &taproot::Signature) -> Witness {
504 Witness::from_slice(&[signature.serialize()])
505 }
506
507 pub fn new(nofn_xonly_pk: XOnlyPublicKey, evm_address: EVMAddress) -> Self {
508 Self(nofn_xonly_pk, evm_address)
509 }
510}
511
512#[derive(Debug, Clone)]
515pub struct ReplacementDepositScript(pub(crate) XOnlyPublicKey, Txid);
516
517impl SpendableScript for ReplacementDepositScript {
518 fn as_any(&self) -> &dyn Any {
519 self
520 }
521
522 fn kind(&self) -> ScriptKind {
523 ScriptKind::ReplacementDepositScript(self)
524 }
525
526 fn to_script_buf(&self) -> ScriptBuf {
527 let citrea_replace: [u8; 13] = "citreaReplace".as_bytes().try_into().expect("length == 13");
528
529 Builder::new()
530 .push_x_only_key(&self.0)
531 .push_opcode(OP_CHECKSIG)
532 .push_opcode(OP_FALSE)
533 .push_opcode(OP_IF)
534 .push_slice(citrea_replace)
535 .push_slice(self.1.as_byte_array())
536 .push_opcode(OP_ENDIF)
537 .into_script()
538 }
539}
540
541impl ReplacementDepositScript {
542 pub fn generate_script_inputs(&self, signature: &taproot::Signature) -> Witness {
543 Witness::from_slice(&[signature.serialize()])
544 }
545
546 pub fn new(nofn_xonly_pk: XOnlyPublicKey, old_move_txid: Txid) -> Self {
547 Self(nofn_xonly_pk, old_move_txid)
548 }
549}
550
551#[derive(Clone)]
552pub enum ScriptKind<'a> {
553 CheckSig(&'a CheckSig),
554 WinternitzCommit(&'a WinternitzCommit),
555 TimelockScript(&'a TimelockScript),
556 PreimageRevealScript(&'a PreimageRevealScript),
557 BaseDepositScript(&'a BaseDepositScript),
558 ReplacementDepositScript(&'a ReplacementDepositScript),
559 Other(&'a OtherSpendable),
560 ManualSpend(&'a Multisig),
561}
562
563#[cfg(test)]
564fn get_script_from_arr<T: SpendableScript>(
565 arr: &Vec<Box<dyn SpendableScript>>,
566) -> Option<(usize, &T)> {
567 arr.iter()
568 .enumerate()
569 .find_map(|(i, x)| x.as_any().downcast_ref::<T>().map(|x| (i, x)))
570}
571#[cfg(test)]
572mod tests {
573 use super::*;
574 use crate::actor::{Actor, WinternitzDerivationPath};
575 use crate::bitvm_client::{self, UNSPENDABLE_XONLY_PUBKEY};
576 use crate::builder::address::create_taproot_address;
577 use crate::config::protocol::ProtocolParamsetName;
578 use crate::extended_bitcoin_rpc::ExtendedBitcoinRpc;
579 use bitcoin::hashes::Hash;
580 use bitcoin::secp256k1::rand::{self, Rng};
581 use bitcoin::secp256k1::{PublicKey, SecretKey};
582 use bitcoincore_rpc::RpcApi;
583 use clementine_primitives::RoundIndex;
584 use std::sync::Arc;
585
586 fn dummy_xonly() -> XOnlyPublicKey {
589 *bitvm_client::UNSPENDABLE_XONLY_PUBKEY
591 }
592
593 fn dummy_scriptbuf() -> ScriptBuf {
594 ScriptBuf::from_hex("51").expect("valid hex")
595 }
596
597 fn dummy_pubkey() -> PublicKey {
598 bitvm_client::UNSPENDABLE_XONLY_PUBKEY.public_key(bitcoin::secp256k1::Parity::Even)
599 }
600
601 fn dummy_params() -> Parameters {
602 Parameters::new(32, 4)
603 }
604
605 fn dummy_evm_address() -> EVMAddress {
606 EVMAddress([0u8; 20])
608 }
609
610 #[test]
611 fn test_dynamic_casting_extended() {
612 let scripts: Vec<Box<dyn SpendableScript>> = vec![
614 Box::new(OtherSpendable::new(dummy_scriptbuf())),
615 Box::new(CheckSig::new(dummy_xonly())),
616 Box::new(WinternitzCommit::new(
617 vec![(vec![[0u8; 20]; 32], 32)],
618 dummy_xonly(),
619 4,
620 )),
621 Box::new(TimelockScript::new(Some(dummy_xonly()), 10)),
622 Box::new(PreimageRevealScript::new(dummy_xonly(), [0; 20])),
623 Box::new(BaseDepositScript::new(dummy_xonly(), dummy_evm_address())),
624 ];
625
626 let checksig = get_script_from_arr::<CheckSig>(&scripts);
628 let winternitz = get_script_from_arr::<WinternitzCommit>(&scripts);
629 let timelock = get_script_from_arr::<TimelockScript>(&scripts);
630 let preimage = get_script_from_arr::<PreimageRevealScript>(&scripts);
631 let deposit = get_script_from_arr::<BaseDepositScript>(&scripts);
632 let others = get_script_from_arr::<OtherSpendable>(&scripts);
633
634 assert!(checksig.is_some(), "CheckSig not found");
635 assert!(winternitz.is_some(), "WinternitzCommit not found");
636 assert!(timelock.is_some(), "TimelockScript not found");
637 assert!(preimage.is_some(), "PreimageRevealScript not found");
638 assert!(deposit.is_some(), "DepositScript not found");
639 assert!(others.is_some(), "OtherSpendable not found");
640
641 let checksig_val = checksig.unwrap().1;
643 println!("CheckSig: {checksig_val:?}");
644 let timelock_val = timelock.unwrap().1;
646 println!("TimelockScript: {timelock_val:?}");
647 let others_val = others.unwrap().1;
650 println!("OtherSpendable: {others_val:?}");
651 }
652
653 #[test]
654 fn test_dynamic_casting() {
655 use crate::bitvm_client;
656 let scripts: Vec<Box<dyn SpendableScript>> = vec![
657 Box::new(OtherSpendable(ScriptBuf::from_hex("51").expect(""))),
658 Box::new(CheckSig(*bitvm_client::UNSPENDABLE_XONLY_PUBKEY)),
659 ];
660
661 let otherspendable = scripts
662 .first()
663 .expect("")
664 .as_any()
665 .downcast_ref::<OtherSpendable>()
666 .expect("");
667
668 let checksig = get_script_from_arr::<CheckSig>(&scripts).expect("");
669 println!("{otherspendable:?}");
670 println!("{checksig:?}");
671 }
672
673 #[test]
674 fn test_scriptkind_completeness() {
675 let script_variants: Vec<(&str, Arc<dyn SpendableScript>)> = vec![
676 ("CheckSig", Arc::new(CheckSig::new(dummy_xonly()))),
677 (
678 "WinternitzCommit",
679 Arc::new(WinternitzCommit::new(
680 vec![(vec![[0u8; 20]; 32], 32)],
681 dummy_xonly(),
682 4,
683 )),
684 ),
685 (
686 "TimelockScript",
687 Arc::new(TimelockScript::new(Some(dummy_xonly()), 15)),
688 ),
689 (
690 "PreimageRevealScript",
691 Arc::new(PreimageRevealScript::new(dummy_xonly(), [1; 20])),
692 ),
693 (
694 "BaseDepositScript",
695 Arc::new(BaseDepositScript::new(dummy_xonly(), dummy_evm_address())),
696 ),
697 (
698 "ReplacementDepositScript",
699 Arc::new(ReplacementDepositScript::new(
700 dummy_xonly(),
701 Txid::all_zeros(),
702 )),
703 ),
704 ("Other", Arc::new(OtherSpendable::new(dummy_scriptbuf()))),
705 ];
706
707 for (expected, script) in script_variants {
708 let kind = script.kind();
709 match (expected, kind) {
710 ("CheckSig", ScriptKind::CheckSig(_)) => (),
711 ("WinternitzCommit", ScriptKind::WinternitzCommit(_)) => (),
712 ("TimelockScript", ScriptKind::TimelockScript(_)) => (),
713 ("PreimageRevealScript", ScriptKind::PreimageRevealScript(_)) => (),
714 ("BaseDepositScript", ScriptKind::BaseDepositScript(_)) => (),
715 ("ReplacementDepositScript", ScriptKind::ReplacementDepositScript(_)) => (),
716 ("Other", ScriptKind::Other(_)) => (),
717 (s, _) => panic!("ScriptKind conversion not comprehensive for variant: {s}"),
718 }
719 }
720 }
721 use crate::bitvm_client::SECP;
723 use crate::builder;
724 use crate::builder::transaction::input::SpendableTxIn;
725 use crate::builder::transaction::output::UnspentTxOut;
726 use crate::builder::transaction::{TxHandlerBuilder, DEFAULT_SEQUENCE};
727 use bitcoin::{Amount, OutPoint, Sequence, TxOut, Txid};
728 use clementine_primitives::TransactionType;
729
730 async fn create_taproot_test_tx(
731 rpc: &ExtendedBitcoinRpc,
732 scripts: Vec<Arc<dyn SpendableScript>>,
733 spend_path: SpendPath,
734 amount: Amount,
735 ) -> (TxHandlerBuilder, bitcoin::Address) {
736 let (address, taproot_spend_info) = builder::address::create_taproot_address(
737 &scripts
738 .iter()
739 .map(|s| s.to_script_buf())
740 .collect::<Vec<_>>(),
741 None,
742 bitcoin::Network::Regtest,
743 );
744
745 let outpoint = rpc.send_to_address(&address, amount).await.unwrap();
746 let sequence = if let SpendPath::ScriptSpend(idx) = spend_path {
747 if let Some(script) = scripts.get(idx) {
748 match script.kind() {
749 ScriptKind::TimelockScript(&TimelockScript(_, seq)) => {
750 Sequence::from_height(seq)
751 }
752 _ => DEFAULT_SEQUENCE,
753 }
754 } else {
755 DEFAULT_SEQUENCE
756 }
757 } else {
758 DEFAULT_SEQUENCE
759 };
760 let mut builder = TxHandlerBuilder::new(TransactionType::Dummy);
761 builder = builder.add_input(
762 crate::rpc::clementine::NormalSignatureKind::OperatorSighashDefault,
763 SpendableTxIn::new(
764 outpoint,
765 TxOut {
766 value: amount,
767 script_pubkey: address.script_pubkey(),
768 },
769 scripts.clone(),
770 Some(taproot_spend_info.clone()),
771 ),
772 spend_path,
773 sequence,
774 );
775
776 builder = builder.add_output(UnspentTxOut::new(
777 TxOut {
778 value: amount - Amount::from_sat(5000), script_pubkey: address.script_pubkey(),
780 },
781 scripts,
782 Some(taproot_spend_info),
783 ));
784
785 (builder, address)
786 }
787
788 use crate::test::common::*;
789
790 #[tokio::test]
791
792 async fn test_checksig_spendable() {
793 let mut config = create_test_config_with_thread_name().await;
794 let regtest = create_regtest_rpc(&mut config).await;
795 let rpc = regtest.rpc().clone();
796
797 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
798 let xonly_pk = kp.public_key().x_only_public_key().0;
799
800 let scripts: Vec<Arc<dyn SpendableScript>> = vec![Arc::new(CheckSig::new(xonly_pk))];
801 let (builder, _) = create_taproot_test_tx(
802 &rpc,
803 scripts,
804 SpendPath::ScriptSpend(0),
805 Amount::from_sat(10_000),
806 )
807 .await;
808 let mut tx = builder.finalize();
809
810 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
812
813 signer
814 .tx_sign_and_fill_sigs(&mut tx, &[], None)
815 .expect("should be able to sign checksig");
816 let tx = tx
817 .promote()
818 .expect("the transaction should be fully signed");
819
820 rpc.send_raw_transaction(tx.get_cached_tx())
821 .await
822 .expect("bitcoin RPC did not accept transaction");
823 }
824
825 #[tokio::test]
826 async fn test_winternitz_commit_spendable() {
827 let mut config = create_test_config_with_thread_name().await;
828 let regtest = create_regtest_rpc(&mut config).await;
829 let rpc = regtest.rpc();
830
831 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
832 let xonly_pk = kp.public_key().x_only_public_key().0;
833
834 let deposit_outpoint = OutPoint {
835 txid: Txid::all_zeros(),
836 vout: 0,
837 };
838
839 let derivation = WinternitzDerivationPath::BitvmAssert(
840 64,
841 3,
842 0,
843 deposit_outpoint,
844 ProtocolParamsetName::Regtest.into(),
845 );
846
847 let derivation2 = WinternitzDerivationPath::BitvmAssert(
848 64,
849 2,
850 0,
851 deposit_outpoint,
852 ProtocolParamsetName::Regtest.into(),
853 );
854
855 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
856
857 let script: Arc<dyn SpendableScript> = Arc::new(WinternitzCommit::new(
858 vec![
859 (
860 signer
861 .derive_winternitz_pk(derivation.clone())
862 .expect("failed to derive Winternitz public key"),
863 64,
864 ),
865 (
866 signer
867 .derive_winternitz_pk(derivation2.clone())
868 .expect("failed to derive Winternitz public key"),
869 64,
870 ),
871 ],
872 xonly_pk,
873 4,
874 ));
875
876 let scripts = vec![script];
877 let (builder, _) = create_taproot_test_tx(
878 rpc,
879 scripts,
880 SpendPath::ScriptSpend(0),
881 Amount::from_sat(10_000),
882 )
883 .await;
884 let mut tx = builder.finalize();
885
886 signer
887 .tx_sign_winternitz(
888 &mut tx,
889 &[
890 (vec![0; 32], derivation.clone()),
891 (vec![0; 32], derivation2.clone()),
892 ],
893 )
894 .expect("failed to partially sign commitments");
895
896 let tx = tx
897 .promote()
898 .expect("the transaction should be fully signed");
899
900 rpc.send_raw_transaction(tx.get_cached_tx())
901 .await
902 .expect("bitcoin RPC did not accept transaction");
903 }
904
905 #[tokio::test]
906 async fn test_timelock_script_spendable() {
907 let mut config = create_test_config_with_thread_name().await;
908 let regtest = create_regtest_rpc(&mut config).await;
909 let rpc = regtest.rpc();
910
911 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
912 let xonly_pk = kp.public_key().x_only_public_key().0;
913
914 let scripts: Vec<Arc<dyn SpendableScript>> =
915 vec![Arc::new(TimelockScript::new(Some(xonly_pk), 15))];
916 let (builder, _) = create_taproot_test_tx(
917 rpc,
918 scripts,
919 SpendPath::ScriptSpend(0),
920 Amount::from_sat(10_000),
921 )
922 .await;
923
924 let mut tx = builder.finalize();
925
926 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
927
928 signer
929 .tx_sign_and_fill_sigs(&mut tx, &[], None)
930 .expect("should be able to sign timelock");
931
932 rpc.send_raw_transaction(tx.get_cached_tx())
933 .await
934 .expect_err("should not pass without 15 blocks");
935
936 rpc.mine_blocks(15).await.expect("failed to mine blocks");
937
938 rpc.send_raw_transaction(tx.get_cached_tx())
939 .await
940 .expect("should pass after 15 blocks");
941 }
942
943 #[tokio::test]
944 async fn test_preimage_reveal_script_spendable() {
945 let mut config = create_test_config_with_thread_name().await;
946 let regtest = create_regtest_rpc(&mut config).await;
947 let rpc = regtest.rpc().clone();
948 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
949 let xonly_pk = kp.public_key().x_only_public_key().0;
950
951 let preimage = [1; 20];
952 let hash = bitcoin::hashes::hash160::Hash::hash(&preimage);
953 let script: Arc<dyn SpendableScript> =
954 Arc::new(PreimageRevealScript::new(xonly_pk, hash.to_byte_array()));
955 let scripts = vec![script];
956 let (builder, _) = create_taproot_test_tx(
957 &rpc,
958 scripts,
959 SpendPath::ScriptSpend(0),
960 Amount::from_sat(10_000),
961 )
962 .await;
963 let mut tx = builder.finalize();
964
965 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
966
967 signer
968 .tx_sign_preimage(&mut tx, preimage)
969 .expect("failed to sign preimage reveal");
970
971 let final_tx = tx
972 .promote()
973 .expect("the transaction should be fully signed");
974
975 rpc.send_raw_transaction(final_tx.get_cached_tx())
976 .await
977 .expect("bitcoin RPC did not accept transaction");
978
979 let preimage = [1; 21];
982 let hash = bitcoin::hashes::hash160::Hash::hash(&preimage);
983 let script: Arc<dyn SpendableScript> =
984 Arc::new(PreimageRevealScript::new(xonly_pk, hash.to_byte_array()));
985 let scripts = vec![script];
986 let (builder, _) = create_taproot_test_tx(
987 &rpc,
988 scripts,
989 SpendPath::ScriptSpend(0),
990 Amount::from_sat(10_000),
991 )
992 .await;
993 let mut tx = builder.finalize();
994
995 signer
996 .tx_sign_preimage(&mut tx, preimage)
997 .expect("failed to sign preimage reveal");
998
999 let final_tx = tx
1000 .promote()
1001 .expect("the transaction should be fully signed");
1002
1003 assert!(rpc
1004 .send_raw_transaction(final_tx.get_cached_tx())
1005 .await
1006 .is_err());
1007 }
1008
1009 #[tokio::test]
1010 async fn test_base_deposit_script_spendable() {
1011 let mut config = create_test_config_with_thread_name().await;
1012 let regtest = create_regtest_rpc(&mut config).await;
1013 let rpc = regtest.rpc().clone();
1014
1015 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
1016 let xonly_pk = kp.public_key().x_only_public_key().0;
1017
1018 let script: Arc<dyn SpendableScript> =
1019 Arc::new(BaseDepositScript::new(xonly_pk, EVMAddress([2; 20])));
1020 let scripts = vec![script];
1021 let (builder, _) = create_taproot_test_tx(
1022 &rpc,
1023 scripts,
1024 SpendPath::ScriptSpend(0),
1025 Amount::from_sat(10_000),
1026 )
1027 .await;
1028 let mut tx = builder.finalize();
1029
1030 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
1031
1032 signer
1033 .tx_sign_and_fill_sigs(&mut tx, &[], None)
1034 .expect("should be able to sign base deposit");
1035
1036 rpc.send_raw_transaction(tx.get_cached_tx())
1037 .await
1038 .expect("bitcoin RPC did not accept transaction");
1039 }
1040
1041 #[tokio::test]
1042 async fn test_replacement_deposit_script_spendable() {
1043 let mut config = create_test_config_with_thread_name().await;
1044 let regtest = create_regtest_rpc(&mut config).await;
1045 let rpc = regtest.rpc().clone();
1046
1047 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
1048 let xonly_pk = kp.public_key().x_only_public_key().0;
1049
1050 let script: Arc<dyn SpendableScript> =
1051 Arc::new(ReplacementDepositScript::new(xonly_pk, Txid::all_zeros()));
1052 let scripts = vec![script];
1053 let (builder, _) = create_taproot_test_tx(
1054 &rpc,
1055 scripts,
1056 SpendPath::ScriptSpend(0),
1057 Amount::from_sat(10_000),
1058 )
1059 .await;
1060 let mut tx = builder.finalize();
1061
1062 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
1063
1064 signer
1065 .tx_sign_and_fill_sigs(&mut tx, &[], None)
1066 .expect("should be able to sign replacement deposit");
1067
1068 rpc.send_raw_transaction(tx.get_cached_tx())
1069 .await
1070 .expect("bitcoin RPC did not accept transaction");
1071 }
1072
1073 #[tokio::test]
1074 async fn test_extract_commit_data() {
1075 let config = create_test_config_with_thread_name().await;
1076 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
1077
1078 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
1079
1080 let kickoff =
1081 WinternitzDerivationPath::Kickoff(RoundIndex::Round(0), 0, config.protocol_paramset());
1082 let bitvm_assert = WinternitzDerivationPath::BitvmAssert(
1083 64,
1084 3,
1085 0,
1086 OutPoint {
1087 txid: Txid::all_zeros(),
1088 vout: 0,
1089 },
1090 config.protocol_paramset(),
1091 );
1092 let commit_script = WinternitzCommit::new(
1093 vec![
1094 (
1095 signer
1096 .derive_winternitz_pk(kickoff.clone())
1097 .expect("failed to derive Winternitz public key"),
1098 40,
1099 ),
1100 (
1101 signer
1102 .derive_winternitz_pk(bitvm_assert.clone())
1103 .expect("failed to derive Winternitz public key"),
1104 64,
1105 ),
1106 ],
1107 signer.xonly_public_key,
1108 4,
1109 );
1110 let signature = taproot::Signature::from_slice(&[1u8; 64]).expect("valid signature");
1111 let kickoff_blockhash: Vec<u8> = (0..20u8).collect();
1112 let assert_commit_data: Vec<u8> = (0..32u8).collect();
1113 let witness = commit_script.generate_script_inputs(
1114 &[
1115 (
1116 kickoff_blockhash.clone(),
1117 signer.get_derived_winternitz_sk(kickoff.clone()).unwrap(),
1118 ),
1119 (
1120 assert_commit_data.clone(),
1121 signer
1122 .get_derived_winternitz_sk(bitvm_assert.clone())
1123 .unwrap(),
1124 ),
1125 ],
1126 &signature,
1127 );
1128 let extracted = extract_winternitz_commits(
1129 witness,
1130 &[kickoff, bitvm_assert],
1131 config.protocol_paramset(),
1132 )
1133 .unwrap();
1134 tracing::info!("{:?}", extracted);
1135 assert_eq!(extracted[0], kickoff_blockhash);
1136 assert_eq!(extracted[1], assert_commit_data);
1137 }
1138
1139 #[tokio::test]
1140 async fn test_multisig_matches_descriptor() {
1141 let mut config = create_test_config_with_thread_name().await;
1142 let regtest = create_regtest_rpc(&mut config).await;
1143 let rpc = regtest.rpc().clone();
1144
1145 let num_pks = rand::thread_rng().gen_range(1..=10);
1147 let threshold = rand::thread_rng().gen_range(1..=num_pks);
1148
1149 let mut pks = Vec::new();
1150 for _ in 0..num_pks {
1151 let secret_key = SecretKey::new(&mut rand::thread_rng());
1152 let kp = bitcoin::secp256k1::Keypair::from_secret_key(&*SECP, &secret_key);
1153 pks.push(kp.public_key().x_only_public_key().0);
1154 }
1155
1156 let unspendable_xonly_pk_str = (*UNSPENDABLE_XONLY_PUBKEY).to_string();
1157 let descriptor = format!(
1158 "tr({},multi_a({},{}))",
1159 unspendable_xonly_pk_str,
1160 threshold,
1161 pks.iter()
1162 .map(|pk| pk.to_string())
1163 .collect::<Vec<String>>()
1164 .join(",")
1165 );
1166
1167 let descriptor_info = rpc.get_descriptor_info(&descriptor).await.expect("");
1168
1169 let descriptor = descriptor_info.descriptor;
1170
1171 let addresses = rpc.derive_addresses(&descriptor, None).await.expect("");
1172
1173 tracing::info!("{:?}", addresses);
1174
1175 let multisig_address = addresses[0].clone().assume_checked();
1176
1177 let multisig = Multisig::new(pks, threshold);
1178
1179 let (addr, _) = create_taproot_address(
1180 &[multisig.to_script_buf()],
1181 None,
1182 config.protocol_paramset().network,
1183 );
1184
1185 assert_eq!(addr, multisig_address);
1189 }
1190}