1#![allow(dead_code)]
21
22use crate::actor::WinternitzDerivationPath;
23use crate::config::protocol::ProtocolParamset;
24use crate::deposit::SecurityCouncil;
25use crate::EVMAddress;
26use bitcoin::hashes::Hash;
27use bitcoin::opcodes::OP_TRUE;
28use bitcoin::{
29 opcodes::{all::*, OP_FALSE},
30 script::Builder,
31 ScriptBuf, XOnlyPublicKey,
32};
33use bitcoin::{taproot, Txid, Witness};
34use bitvm::signatures::winternitz::{Parameters, PublicKey, SecretKey};
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 crate::operator::RoundIndex;
580 use bitcoin::hashes::Hash;
581 use bitcoin::secp256k1::rand::{self, Rng};
582 use bitcoin::secp256k1::{PublicKey, SecretKey};
583 use bitcoincore_rpc::RpcApi;
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::{TransactionType, TxHandlerBuilder, DEFAULT_SEQUENCE};
727 use bitcoin::{Amount, OutPoint, Sequence, TxOut, Txid};
728
729 async fn create_taproot_test_tx(
730 rpc: &ExtendedBitcoinRpc,
731 scripts: Vec<Arc<dyn SpendableScript>>,
732 spend_path: SpendPath,
733 amount: Amount,
734 ) -> (TxHandlerBuilder, bitcoin::Address) {
735 let (address, taproot_spend_info) = builder::address::create_taproot_address(
736 &scripts
737 .iter()
738 .map(|s| s.to_script_buf())
739 .collect::<Vec<_>>(),
740 None,
741 bitcoin::Network::Regtest,
742 );
743
744 let outpoint = rpc.send_to_address(&address, amount).await.unwrap();
745 let sequence = if let SpendPath::ScriptSpend(idx) = spend_path {
746 if let Some(script) = scripts.get(idx) {
747 match script.kind() {
748 ScriptKind::TimelockScript(&TimelockScript(_, seq)) => {
749 Sequence::from_height(seq)
750 }
751 _ => DEFAULT_SEQUENCE,
752 }
753 } else {
754 DEFAULT_SEQUENCE
755 }
756 } else {
757 DEFAULT_SEQUENCE
758 };
759 let mut builder = TxHandlerBuilder::new(TransactionType::Dummy);
760 builder = builder.add_input(
761 crate::rpc::clementine::NormalSignatureKind::OperatorSighashDefault,
762 SpendableTxIn::new(
763 outpoint,
764 TxOut {
765 value: amount,
766 script_pubkey: address.script_pubkey(),
767 },
768 scripts.clone(),
769 Some(taproot_spend_info.clone()),
770 ),
771 spend_path,
772 sequence,
773 );
774
775 builder = builder.add_output(UnspentTxOut::new(
776 TxOut {
777 value: amount - Amount::from_sat(5000), script_pubkey: address.script_pubkey(),
779 },
780 scripts,
781 Some(taproot_spend_info),
782 ));
783
784 (builder, address)
785 }
786
787 use crate::test::common::*;
788
789 #[tokio::test]
790
791 async fn test_checksig_spendable() {
792 let mut config = create_test_config_with_thread_name().await;
793 let regtest = create_regtest_rpc(&mut config).await;
794 let rpc = regtest.rpc().clone();
795
796 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
797 let xonly_pk = kp.public_key().x_only_public_key().0;
798
799 let scripts: Vec<Arc<dyn SpendableScript>> = vec![Arc::new(CheckSig::new(xonly_pk))];
800 let (builder, _) = create_taproot_test_tx(
801 &rpc,
802 scripts,
803 SpendPath::ScriptSpend(0),
804 Amount::from_sat(10_000),
805 )
806 .await;
807 let mut tx = builder.finalize();
808
809 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
811
812 signer
813 .tx_sign_and_fill_sigs(&mut tx, &[], None)
814 .expect("should be able to sign checksig");
815 let tx = tx
816 .promote()
817 .expect("the transaction should be fully signed");
818
819 rpc.send_raw_transaction(tx.get_cached_tx())
820 .await
821 .expect("bitcoin RPC did not accept transaction");
822 }
823
824 #[tokio::test]
825 async fn test_winternitz_commit_spendable() {
826 let mut config = create_test_config_with_thread_name().await;
827 let regtest = create_regtest_rpc(&mut config).await;
828 let rpc = regtest.rpc();
829
830 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
831 let xonly_pk = kp.public_key().x_only_public_key().0;
832
833 let deposit_outpoint = OutPoint {
834 txid: Txid::all_zeros(),
835 vout: 0,
836 };
837
838 let derivation = WinternitzDerivationPath::BitvmAssert(
839 64,
840 3,
841 0,
842 deposit_outpoint,
843 ProtocolParamsetName::Regtest.into(),
844 );
845
846 let derivation2 = WinternitzDerivationPath::BitvmAssert(
847 64,
848 2,
849 0,
850 deposit_outpoint,
851 ProtocolParamsetName::Regtest.into(),
852 );
853
854 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
855
856 let script: Arc<dyn SpendableScript> = Arc::new(WinternitzCommit::new(
857 vec![
858 (
859 signer
860 .derive_winternitz_pk(derivation.clone())
861 .expect("failed to derive Winternitz public key"),
862 64,
863 ),
864 (
865 signer
866 .derive_winternitz_pk(derivation2.clone())
867 .expect("failed to derive Winternitz public key"),
868 64,
869 ),
870 ],
871 xonly_pk,
872 4,
873 ));
874
875 let scripts = vec![script];
876 let (builder, _) = create_taproot_test_tx(
877 rpc,
878 scripts,
879 SpendPath::ScriptSpend(0),
880 Amount::from_sat(10_000),
881 )
882 .await;
883 let mut tx = builder.finalize();
884
885 signer
886 .tx_sign_winternitz(
887 &mut tx,
888 &[
889 (vec![0; 32], derivation.clone()),
890 (vec![0; 32], derivation2.clone()),
891 ],
892 )
893 .expect("failed to partially sign commitments");
894
895 let tx = tx
896 .promote()
897 .expect("the transaction should be fully signed");
898
899 rpc.send_raw_transaction(tx.get_cached_tx())
900 .await
901 .expect("bitcoin RPC did not accept transaction");
902 }
903
904 #[tokio::test]
905 async fn test_timelock_script_spendable() {
906 let mut config = create_test_config_with_thread_name().await;
907 let regtest = create_regtest_rpc(&mut config).await;
908 let rpc = regtest.rpc();
909
910 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
911 let xonly_pk = kp.public_key().x_only_public_key().0;
912
913 let scripts: Vec<Arc<dyn SpendableScript>> =
914 vec![Arc::new(TimelockScript::new(Some(xonly_pk), 15))];
915 let (builder, _) = create_taproot_test_tx(
916 rpc,
917 scripts,
918 SpendPath::ScriptSpend(0),
919 Amount::from_sat(10_000),
920 )
921 .await;
922
923 let mut tx = builder.finalize();
924
925 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
926
927 signer
928 .tx_sign_and_fill_sigs(&mut tx, &[], None)
929 .expect("should be able to sign timelock");
930
931 rpc.send_raw_transaction(tx.get_cached_tx())
932 .await
933 .expect_err("should not pass without 15 blocks");
934
935 rpc.mine_blocks(15).await.expect("failed to mine blocks");
936
937 rpc.send_raw_transaction(tx.get_cached_tx())
938 .await
939 .expect("should pass after 15 blocks");
940 }
941
942 #[tokio::test]
943 async fn test_preimage_reveal_script_spendable() {
944 let mut config = create_test_config_with_thread_name().await;
945 let regtest = create_regtest_rpc(&mut config).await;
946 let rpc = regtest.rpc().clone();
947 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
948 let xonly_pk = kp.public_key().x_only_public_key().0;
949
950 let preimage = [1; 20];
951 let hash = bitcoin::hashes::hash160::Hash::hash(&preimage);
952 let script: Arc<dyn SpendableScript> =
953 Arc::new(PreimageRevealScript::new(xonly_pk, hash.to_byte_array()));
954 let scripts = vec![script];
955 let (builder, _) = create_taproot_test_tx(
956 &rpc,
957 scripts,
958 SpendPath::ScriptSpend(0),
959 Amount::from_sat(10_000),
960 )
961 .await;
962 let mut tx = builder.finalize();
963
964 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
965
966 signer
967 .tx_sign_preimage(&mut tx, preimage)
968 .expect("failed to sign preimage reveal");
969
970 let final_tx = tx
971 .promote()
972 .expect("the transaction should be fully signed");
973
974 rpc.send_raw_transaction(final_tx.get_cached_tx())
975 .await
976 .expect("bitcoin RPC did not accept transaction");
977
978 let preimage = [1; 21];
981 let hash = bitcoin::hashes::hash160::Hash::hash(&preimage);
982 let script: Arc<dyn SpendableScript> =
983 Arc::new(PreimageRevealScript::new(xonly_pk, hash.to_byte_array()));
984 let scripts = vec![script];
985 let (builder, _) = create_taproot_test_tx(
986 &rpc,
987 scripts,
988 SpendPath::ScriptSpend(0),
989 Amount::from_sat(10_000),
990 )
991 .await;
992 let mut tx = builder.finalize();
993
994 signer
995 .tx_sign_preimage(&mut tx, preimage)
996 .expect("failed to sign preimage reveal");
997
998 let final_tx = tx
999 .promote()
1000 .expect("the transaction should be fully signed");
1001
1002 assert!(rpc
1003 .send_raw_transaction(final_tx.get_cached_tx())
1004 .await
1005 .is_err());
1006 }
1007
1008 #[tokio::test]
1009 async fn test_base_deposit_script_spendable() {
1010 let mut config = create_test_config_with_thread_name().await;
1011 let regtest = create_regtest_rpc(&mut config).await;
1012 let rpc = regtest.rpc().clone();
1013
1014 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
1015 let xonly_pk = kp.public_key().x_only_public_key().0;
1016
1017 let script: Arc<dyn SpendableScript> =
1018 Arc::new(BaseDepositScript::new(xonly_pk, EVMAddress([2; 20])));
1019 let scripts = vec![script];
1020 let (builder, _) = create_taproot_test_tx(
1021 &rpc,
1022 scripts,
1023 SpendPath::ScriptSpend(0),
1024 Amount::from_sat(10_000),
1025 )
1026 .await;
1027 let mut tx = builder.finalize();
1028
1029 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
1030
1031 signer
1032 .tx_sign_and_fill_sigs(&mut tx, &[], None)
1033 .expect("should be able to sign base deposit");
1034
1035 rpc.send_raw_transaction(tx.get_cached_tx())
1036 .await
1037 .expect("bitcoin RPC did not accept transaction");
1038 }
1039
1040 #[tokio::test]
1041 async fn test_replacement_deposit_script_spendable() {
1042 let mut config = create_test_config_with_thread_name().await;
1043 let regtest = create_regtest_rpc(&mut config).await;
1044 let rpc = regtest.rpc().clone();
1045
1046 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
1047 let xonly_pk = kp.public_key().x_only_public_key().0;
1048
1049 let script: Arc<dyn SpendableScript> =
1050 Arc::new(ReplacementDepositScript::new(xonly_pk, Txid::all_zeros()));
1051 let scripts = vec![script];
1052 let (builder, _) = create_taproot_test_tx(
1053 &rpc,
1054 scripts,
1055 SpendPath::ScriptSpend(0),
1056 Amount::from_sat(10_000),
1057 )
1058 .await;
1059 let mut tx = builder.finalize();
1060
1061 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
1062
1063 signer
1064 .tx_sign_and_fill_sigs(&mut tx, &[], None)
1065 .expect("should be able to sign replacement deposit");
1066
1067 rpc.send_raw_transaction(tx.get_cached_tx())
1068 .await
1069 .expect("bitcoin RPC did not accept transaction");
1070 }
1071
1072 #[tokio::test]
1073 async fn test_extract_commit_data() {
1074 let config = create_test_config_with_thread_name().await;
1075 let kp = bitcoin::secp256k1::Keypair::new(&SECP, &mut rand::thread_rng());
1076
1077 let signer = Actor::new(kp.secret_key(), bitcoin::Network::Regtest);
1078
1079 let kickoff =
1080 WinternitzDerivationPath::Kickoff(RoundIndex::Round(0), 0, config.protocol_paramset());
1081 let bitvm_assert = WinternitzDerivationPath::BitvmAssert(
1082 64,
1083 3,
1084 0,
1085 OutPoint {
1086 txid: Txid::all_zeros(),
1087 vout: 0,
1088 },
1089 config.protocol_paramset(),
1090 );
1091 let commit_script = WinternitzCommit::new(
1092 vec![
1093 (
1094 signer
1095 .derive_winternitz_pk(kickoff.clone())
1096 .expect("failed to derive Winternitz public key"),
1097 40,
1098 ),
1099 (
1100 signer
1101 .derive_winternitz_pk(bitvm_assert.clone())
1102 .expect("failed to derive Winternitz public key"),
1103 64,
1104 ),
1105 ],
1106 signer.xonly_public_key,
1107 4,
1108 );
1109 let signature = taproot::Signature::from_slice(&[1u8; 64]).expect("valid signature");
1110 let kickoff_blockhash: Vec<u8> = (0..20u8).collect();
1111 let assert_commit_data: Vec<u8> = (0..32u8).collect();
1112 let witness = commit_script.generate_script_inputs(
1113 &[
1114 (
1115 kickoff_blockhash.clone(),
1116 signer.get_derived_winternitz_sk(kickoff.clone()).unwrap(),
1117 ),
1118 (
1119 assert_commit_data.clone(),
1120 signer
1121 .get_derived_winternitz_sk(bitvm_assert.clone())
1122 .unwrap(),
1123 ),
1124 ],
1125 &signature,
1126 );
1127 let extracted = extract_winternitz_commits(
1128 witness,
1129 &[kickoff, bitvm_assert],
1130 config.protocol_paramset(),
1131 )
1132 .unwrap();
1133 tracing::info!("{:?}", extracted);
1134 assert_eq!(extracted[0], kickoff_blockhash);
1135 assert_eq!(extracted[1], assert_commit_data);
1136 }
1137
1138 #[tokio::test]
1139 async fn test_multisig_matches_descriptor() {
1140 let mut config = create_test_config_with_thread_name().await;
1141 let regtest = create_regtest_rpc(&mut config).await;
1142 let rpc = regtest.rpc().clone();
1143
1144 let num_pks = rand::thread_rng().gen_range(1..=10);
1146 let threshold = rand::thread_rng().gen_range(1..=num_pks);
1147
1148 let mut pks = Vec::new();
1149 for _ in 0..num_pks {
1150 let secret_key = SecretKey::new(&mut rand::thread_rng());
1151 let kp = bitcoin::secp256k1::Keypair::from_secret_key(&*SECP, &secret_key);
1152 pks.push(kp.public_key().x_only_public_key().0);
1153 }
1154
1155 let unspendable_xonly_pk_str = (*UNSPENDABLE_XONLY_PUBKEY).to_string();
1156 let descriptor = format!(
1157 "tr({},multi_a({},{}))",
1158 unspendable_xonly_pk_str,
1159 threshold,
1160 pks.iter()
1161 .map(|pk| pk.to_string())
1162 .collect::<Vec<String>>()
1163 .join(",")
1164 );
1165
1166 let descriptor_info = rpc.get_descriptor_info(&descriptor).await.expect("");
1167
1168 let descriptor = descriptor_info.descriptor;
1169
1170 let addresses = rpc.derive_addresses(&descriptor, None).await.expect("");
1171
1172 tracing::info!("{:?}", addresses);
1173
1174 let multisig_address = addresses[0].clone().assume_checked();
1175
1176 let multisig = Multisig::new(pks, threshold);
1177
1178 let (addr, _) = create_taproot_address(
1179 &[multisig.to_script_buf()],
1180 None,
1181 config.protocol_paramset().network,
1182 );
1183
1184 assert_eq!(addr, multisig_address);
1188 }
1189}