1use bitcoin::opcodes::all::*;
2use bitcoin::taproot::TaprootSpendInfo;
3use bitcoin::ScriptBuf;
4use bitcoin::XOnlyPublicKey;
5use std::fmt;
6
7pub fn multisig_script(pk_0: XOnlyPublicKey, pk_1: XOnlyPublicKey) -> ScriptBuf {
9 ScriptBuf::builder()
10 .push_x_only_key(&pk_0)
11 .push_opcode(OP_CHECKSIGVERIFY)
12 .push_x_only_key(&pk_1)
13 .push_opcode(OP_CHECKSIG)
14 .into_script()
15}
16
17pub fn csv_sig_script(locktime: bitcoin::Sequence, pk: XOnlyPublicKey) -> ScriptBuf {
20 ScriptBuf::builder()
21 .push_int(locktime.to_consensus_u32() as i64)
22 .push_opcode(OP_CSV)
23 .push_opcode(OP_DROP)
24 .push_x_only_key(&pk)
25 .push_opcode(OP_CHECKSIG)
26 .into_script()
27}
28
29pub fn tr_script_pubkey(spend_info: &TaprootSpendInfo) -> ScriptBuf {
31 let output_key = spend_info.output_key();
32 let builder = bitcoin::blockdata::script::Builder::new();
33 builder
34 .push_opcode(OP_PUSHNUM_1)
35 .push_slice(output_key.serialize())
36 .into_script()
37}
38
39pub fn extract_sequence_from_csv_sig_script(
40 script: &ScriptBuf,
41) -> Result<bitcoin::Sequence, InvalidCsvSigScriptError> {
42 let csv_index = script
43 .to_bytes()
44 .windows(2)
45 .position(|window| *window == [OP_CSV.to_u8(), OP_DROP.to_u8()])
46 .ok_or(InvalidCsvSigScriptError)?;
47
48 let before_csv = &script.to_bytes()[..csv_index];
49
50 let sequence = if before_csv.len() > 1 {
53 &before_csv[1..]
54 } else {
55 before_csv
56 };
57
58 let mut sequence = sequence.to_vec();
59 sequence.reverse();
60
61 let mut buffer = [0u8; 4];
62 let input_len = sequence.len();
63 let start_index = 4 - input_len; buffer[start_index..].copy_from_slice(&sequence);
66
67 let sequence = u32::from_be_bytes(buffer);
68
69 let sequence = bitcoin::Sequence::from_consensus(sequence);
70
71 Ok(sequence)
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct InvalidCsvSigScriptError;
76
77impl fmt::Display for InvalidCsvSigScriptError {
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 f.write_str("invalid CSV-Sig script")
80 }
81}
82
83impl std::error::Error for InvalidCsvSigScriptError {}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use bitcoin::locktime;
89 use bitcoin::XOnlyPublicKey;
90 use std::str::FromStr;
91
92 #[test]
93 fn test_extract_sequence_from_csv_sig_script() {
94 let locktime_seconds = 1024;
96 let sequence = bitcoin::Sequence::from_seconds_ceil(locktime_seconds).unwrap();
97
98 let pk = XOnlyPublicKey::from_str(
99 "18845781f631c48f1c9709e23092067d06837f30aa0cd0544ac887fe91ddd166",
100 )
101 .unwrap();
102
103 let script = csv_sig_script(sequence, pk);
104
105 let parsed = extract_sequence_from_csv_sig_script(&script).unwrap();
106 let parsed = parsed.to_relative_lock_time();
107
108 assert_eq!(
109 parsed,
110 locktime::relative::LockTime::from_512_second_intervals(2).into()
111 );
112 }
113}