1use super::create_move_to_vault_txhandler;
13use super::input::SpendableTxIn;
14use super::input::UtxoVout;
15use super::op_return_txout;
16use super::txhandler::DEFAULT_SEQUENCE;
17use super::HiddenNode;
18use super::Signed;
19use crate::builder;
20use crate::builder::script::{CheckSig, SpendableScript, TimelockScript};
21use crate::builder::script::{PreimageRevealScript, SpendPath};
22use crate::builder::transaction::anchor_output;
23use crate::builder::transaction::output::UnspentTxOut;
24use crate::builder::transaction::txhandler::{TxHandler, TxHandlerBuilder};
25use crate::config::protocol::ProtocolParamset;
26use crate::constants::NON_EPHEMERAL_ANCHOR_AMOUNT;
27use crate::constants::NON_STANDARD_V3;
28use crate::deposit::{DepositData, KickoffData};
29use crate::rpc::clementine::NormalSignatureKind;
30use bitcoin::hashes::Hash;
31use bitcoin::script::PushBytesBuf;
32use bitcoin::ScriptBuf;
33use bitcoin::XOnlyPublicKey;
34use bitcoin::{taproot, TxOut, Txid};
35use clementine_errors::BridgeError;
36use clementine_errors::{TransactionType, TxError};
37use clementine_primitives::UTXO;
38use std::sync::Arc;
39
40#[derive(Debug, Clone)]
41pub enum AssertScripts<'a> {
42 AssertScriptTapNodeHash(&'a [[u8; 32]]),
43 AssertSpendableScript(Vec<Arc<dyn SpendableScript>>),
44}
45
46#[derive(Debug, Clone)]
47pub enum DisprovePath<'a> {
48 Scripts(Vec<ScriptBuf>),
49 HiddenNode(HiddenNode<'a>),
50}
51
52#[allow(clippy::too_many_arguments)]
88pub fn create_kickoff_txhandler(
89 kickoff_data: KickoffData,
90 round_txhandler: &TxHandler,
91 move_txhandler: &TxHandler,
92 deposit_data: &mut DepositData,
93 operator_xonly_pk: XOnlyPublicKey,
94 assert_scripts: AssertScripts,
95 disprove_path: DisprovePath,
96 additional_disprove_script: Vec<u8>,
97 latest_blockhash_script: AssertScripts,
98 operator_unlock_hashes: &[[u8; 20]],
99 paramset: &'static ProtocolParamset,
100) -> Result<TxHandler, BridgeError> {
101 let kickoff_idx = kickoff_data.kickoff_idx as usize;
102 let move_txid: Txid = *move_txhandler.get_txid();
103 let mut builder = TxHandlerBuilder::new(TransactionType::Kickoff).with_version(NON_STANDARD_V3);
104 builder = builder.add_input(
105 NormalSignatureKind::OperatorSighashDefault,
106 round_txhandler.get_spendable_output(UtxoVout::Kickoff(kickoff_idx))?,
107 builder::script::SpendPath::ScriptSpend(0),
108 DEFAULT_SEQUENCE,
109 );
110
111 let nofn_script = Arc::new(CheckSig::new(deposit_data.get_nofn_xonly_pk()?));
112 let operator_script = Arc::new(CheckSig::new(operator_xonly_pk));
113
114 let operator_1week = Arc::new(TimelockScript::new(
115 Some(operator_xonly_pk),
116 paramset.operator_challenge_timeout_timelock,
117 ));
118
119 builder = builder
120 .add_output(UnspentTxOut::from_scripts(
122 paramset.default_utxo_amount(),
123 vec![operator_script, operator_1week],
124 None,
125 paramset.network,
126 ))
127 .add_output(UnspentTxOut::from_scripts(
129 paramset.default_utxo_amount(),
130 vec![nofn_script.clone()],
131 None,
132 paramset.network,
133 ))
134 .add_output(UnspentTxOut::from_scripts(
136 paramset.default_utxo_amount(),
137 vec![nofn_script.clone()],
138 None,
139 paramset.network,
140 ));
141
142 let operator_5week = Arc::new(TimelockScript::new(
145 Some(operator_xonly_pk),
146 paramset.disprove_timeout_timelock,
147 ));
148
149 let additional_disprove_script = ScriptBuf::from_bytes(additional_disprove_script);
150
151 builder = builder.add_output(super::create_disprove_taproot_output(
153 operator_5week,
154 additional_disprove_script.clone(),
155 disprove_path,
156 paramset.default_utxo_amount(),
157 paramset.network,
158 ));
159
160 let nofn_latest_blockhash = Arc::new(TimelockScript::new(
161 Some(deposit_data.get_nofn_xonly_pk()?),
162 paramset.latest_blockhash_timeout_timelock,
163 ));
164
165 match latest_blockhash_script {
166 AssertScripts::AssertScriptTapNodeHash(latest_blockhash_root_hash) => {
167 if latest_blockhash_root_hash.len() != 1 {
168 return Err(TxError::LatestBlockhashScriptNumber.into());
169 }
170 let latest_blockhash_root_hash = latest_blockhash_root_hash[0];
171 builder = builder.add_output(super::create_taproot_output_with_hidden_node(
173 nofn_latest_blockhash,
174 &latest_blockhash_root_hash,
175 paramset.default_utxo_amount(),
176 paramset.network,
177 ));
178 }
179 AssertScripts::AssertSpendableScript(latest_blockhash_script) => {
180 if latest_blockhash_script.len() != 1 {
181 return Err(TxError::LatestBlockhashScriptNumber.into());
182 }
183 let latest_blockhash_script = latest_blockhash_script[0].clone();
184 builder = builder.add_output(UnspentTxOut::from_scripts(
185 paramset.default_utxo_amount(),
186 vec![nofn_latest_blockhash, latest_blockhash_script],
187 None,
188 paramset.network,
189 ));
190 }
191 }
192
193 let nofn_4week = Arc::new(TimelockScript::new(
195 Some(deposit_data.get_nofn_xonly_pk()?),
196 paramset.assert_timeout_timelock,
197 ));
198
199 match assert_scripts {
200 AssertScripts::AssertScriptTapNodeHash(assert_script_hashes) => {
201 for script_hash in assert_script_hashes.iter() {
202 builder = builder.add_output(super::create_taproot_output_with_hidden_node(
204 nofn_4week.clone(),
205 script_hash,
206 paramset.default_utxo_amount(),
207 paramset.network,
208 ));
209 }
210 }
211 AssertScripts::AssertSpendableScript(assert_scripts) => {
212 for script in assert_scripts {
213 builder = builder.add_output(UnspentTxOut::from_scripts(
214 paramset.default_utxo_amount(),
215 vec![nofn_4week.clone(), script],
216 None,
217 paramset.network,
218 ));
219 }
220 }
221 }
222
223 let watchtower_xonly_pks = deposit_data.get_watchtowers();
224
225 for (watchtower_idx, watchtower_xonly_pk) in watchtower_xonly_pks.iter().enumerate() {
226 let nofn_2week = Arc::new(TimelockScript::new(
227 Some(deposit_data.get_nofn_xonly_pk()?),
228 paramset.watchtower_challenge_timeout_timelock,
229 ));
230 builder = builder.add_output(UnspentTxOut::from_scripts(
232 paramset.default_utxo_amount() * 2 + paramset.anchor_amount(), vec![nofn_2week.clone()],
234 Some(*watchtower_xonly_pk), paramset.network,
236 ));
237
238 let nofn_3week = Arc::new(TimelockScript::new(
240 Some(deposit_data.get_nofn_xonly_pk()?),
241 paramset.operator_challenge_nack_timelock,
242 ));
243 let operator_with_preimage = Arc::new(PreimageRevealScript::new(
244 operator_xonly_pk,
245 operator_unlock_hashes[watchtower_idx],
246 ));
247 builder = builder.add_output(UnspentTxOut::from_scripts(
248 paramset.default_utxo_amount(),
249 vec![
250 nofn_3week.clone(),
251 nofn_2week.clone(),
252 operator_with_preimage,
253 ],
254 None,
255 paramset.network,
256 ));
257 }
258
259 let mut op_return_script = move_txid.to_byte_array().to_vec();
260 op_return_script.extend(kickoff_data.operator_xonly_pk.serialize());
261
262 let push_bytes = PushBytesBuf::try_from(op_return_script)
263 .expect("Can't fail since the script is shorter than 4294967296 bytes");
264
265 let op_return_txout = builder::transaction::op_return_txout(push_bytes);
266
267 Ok(builder
268 .add_output(UnspentTxOut::from_partial(op_return_txout))
269 .add_output(UnspentTxOut::from_partial(
270 builder::transaction::anchor_output(paramset.anchor_amount()),
271 ))
272 .finalize())
273}
274
275pub fn create_kickoff_not_finalized_txhandler(
293 kickoff_txhandler: &TxHandler,
294 ready_to_reimburse_txhandler: &TxHandler,
295 paramset: &'static ProtocolParamset,
296) -> Result<TxHandler, BridgeError> {
297 Ok(TxHandlerBuilder::new(TransactionType::KickoffNotFinalized)
298 .with_version(NON_STANDARD_V3)
299 .add_input(
300 NormalSignatureKind::KickoffNotFinalized1,
301 kickoff_txhandler.get_spendable_output(UtxoVout::KickoffFinalizer)?,
302 builder::script::SpendPath::ScriptSpend(0),
303 DEFAULT_SEQUENCE,
304 )
305 .add_input(
306 NormalSignatureKind::KickoffNotFinalized2,
307 ready_to_reimburse_txhandler
308 .get_spendable_output(UtxoVout::CollateralInReadyToReimburse)?,
309 builder::script::SpendPath::KeySpend,
310 DEFAULT_SEQUENCE,
311 )
312 .add_output(UnspentTxOut::from_partial(
313 builder::transaction::anchor_output(paramset.anchor_amount()),
314 ))
315 .finalize())
316}
317
318pub fn create_reimburse_txhandler(
342 move_txhandler: &TxHandler,
343 round_txhandler: &TxHandler,
344 kickoff_txhandler: &TxHandler,
345 kickoff_idx: usize,
346 paramset: &'static ProtocolParamset,
347 operator_reimbursement_address: &bitcoin::Address,
348) -> Result<TxHandler, BridgeError> {
349 let builder = TxHandlerBuilder::new(TransactionType::Reimburse)
350 .with_version(NON_STANDARD_V3)
351 .add_input(
352 NormalSignatureKind::Reimburse1,
353 move_txhandler.get_spendable_output(UtxoVout::DepositInMove)?,
354 builder::script::SpendPath::ScriptSpend(0),
355 DEFAULT_SEQUENCE,
356 )
357 .add_input(
358 NormalSignatureKind::Reimburse2,
359 kickoff_txhandler.get_spendable_output(UtxoVout::ReimburseInKickoff)?,
360 builder::script::SpendPath::ScriptSpend(0),
361 DEFAULT_SEQUENCE,
362 )
363 .add_input(
364 NormalSignatureKind::OperatorSighashDefault,
365 round_txhandler.get_spendable_output(UtxoVout::ReimburseInRound(
366 kickoff_idx,
367 paramset.num_kickoffs_per_round,
368 ))?,
369 builder::script::SpendPath::KeySpend,
370 DEFAULT_SEQUENCE,
371 );
372
373 Ok(builder
374 .add_output(UnspentTxOut::from_partial(TxOut {
375 value: move_txhandler
376 .get_spendable_output(UtxoVout::DepositInMove)?
377 .get_prevout()
378 .value,
379 script_pubkey: operator_reimbursement_address.script_pubkey(),
380 }))
381 .add_output(UnspentTxOut::from_partial(
382 builder::transaction::anchor_output(paramset.anchor_amount()),
383 ))
384 .finalize())
385}
386
387pub fn create_payout_txhandler(
408 input_utxo: UTXO,
409 output_txout: TxOut,
410 operator_xonly_pk: XOnlyPublicKey,
411 user_sig: taproot::Signature,
412 _network: bitcoin::Network,
413) -> Result<TxHandler<Signed>, BridgeError> {
414 let txin = SpendableTxIn::new_partial(input_utxo.outpoint, input_utxo.txout);
415
416 let output_txout = UnspentTxOut::from_partial(output_txout.clone());
417
418 let op_return_txout = op_return_txout(PushBytesBuf::from(operator_xonly_pk.serialize()));
419
420 let mut txhandler = TxHandlerBuilder::new(TransactionType::Payout)
421 .with_version(NON_STANDARD_V3)
422 .add_input(
423 NormalSignatureKind::NotStored,
424 txin,
425 SpendPath::KeySpend,
426 DEFAULT_SEQUENCE,
427 )
428 .add_output(output_txout)
429 .add_output(UnspentTxOut::from_partial(anchor_output(
430 NON_EPHEMERAL_ANCHOR_AMOUNT,
431 )))
432 .add_output(UnspentTxOut::from_partial(op_return_txout))
433 .finalize();
434 txhandler.set_p2tr_key_spend_witness(&user_sig, 0)?;
435 txhandler.promote()
436}
437
438pub fn create_optimistic_payout_txhandler(
460 deposit_data: &mut DepositData,
461 input_utxo: UTXO,
462 output_txout: TxOut,
463 user_sig: taproot::Signature,
464 paramset: &'static ProtocolParamset,
465) -> Result<TxHandler, BridgeError> {
466 let move_txhandler: TxHandler = create_move_to_vault_txhandler(deposit_data, paramset)?;
467 let txin = SpendableTxIn::new_partial(input_utxo.outpoint, input_utxo.txout);
468
469 let output_txout = UnspentTxOut::from_partial(output_txout.clone());
470
471 let mut txhandler = TxHandlerBuilder::new(TransactionType::Payout)
472 .with_version(NON_STANDARD_V3)
473 .add_input(
474 NormalSignatureKind::NotStored,
475 txin,
476 SpendPath::KeySpend,
477 DEFAULT_SEQUENCE,
478 )
479 .add_input(
480 NormalSignatureKind::NotStored,
481 move_txhandler.get_spendable_output(UtxoVout::DepositInMove)?,
482 SpendPath::ScriptSpend(0),
483 DEFAULT_SEQUENCE,
484 )
485 .add_output(output_txout)
486 .add_output(UnspentTxOut::from_partial(
487 builder::transaction::non_ephemeral_anchor_output(),
488 ))
489 .finalize();
490 txhandler.set_p2tr_key_spend_witness(&user_sig, 0)?;
491 Ok(txhandler)
492}