clementine_core/builder/transaction/
operator_reimburse.rs1use 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 super::TransactionType;
20use super::TxError;
21use crate::builder::script::{CheckSig, SpendableScript, TimelockScript};
22use crate::builder::script::{PreimageRevealScript, SpendPath};
23use crate::builder::transaction::anchor_output;
24use crate::builder::transaction::output::UnspentTxOut;
25use crate::builder::transaction::txhandler::{TxHandler, TxHandlerBuilder};
26use crate::config::protocol::ProtocolParamset;
27use crate::constants::NON_EPHEMERAL_ANCHOR_AMOUNT;
28use crate::constants::NON_STANDARD_V3;
29use crate::deposit::{DepositData, KickoffData};
30use crate::errors::BridgeError;
31use crate::rpc::clementine::NormalSignatureKind;
32use crate::{builder, UTXO};
33use bitcoin::hashes::Hash;
34use bitcoin::script::PushBytesBuf;
35use bitcoin::ScriptBuf;
36use bitcoin::XOnlyPublicKey;
37use bitcoin::{taproot, TxOut, Txid};
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
366 .get_spendable_output(UtxoVout::ReimburseInRound(kickoff_idx, paramset))?,
367 builder::script::SpendPath::KeySpend,
368 DEFAULT_SEQUENCE,
369 );
370
371 Ok(builder
372 .add_output(UnspentTxOut::from_partial(TxOut {
373 value: move_txhandler
374 .get_spendable_output(UtxoVout::DepositInMove)?
375 .get_prevout()
376 .value,
377 script_pubkey: operator_reimbursement_address.script_pubkey(),
378 }))
379 .add_output(UnspentTxOut::from_partial(
380 builder::transaction::anchor_output(paramset.anchor_amount()),
381 ))
382 .finalize())
383}
384
385pub fn create_payout_txhandler(
406 input_utxo: UTXO,
407 output_txout: TxOut,
408 operator_xonly_pk: XOnlyPublicKey,
409 user_sig: taproot::Signature,
410 _network: bitcoin::Network,
411) -> Result<TxHandler<Signed>, BridgeError> {
412 let txin = SpendableTxIn::new_partial(input_utxo.outpoint, input_utxo.txout);
413
414 let output_txout = UnspentTxOut::from_partial(output_txout.clone());
415
416 let op_return_txout = op_return_txout(PushBytesBuf::from(operator_xonly_pk.serialize()));
417
418 let mut txhandler = TxHandlerBuilder::new(TransactionType::Payout)
419 .with_version(NON_STANDARD_V3)
420 .add_input(
421 NormalSignatureKind::NotStored,
422 txin,
423 SpendPath::KeySpend,
424 DEFAULT_SEQUENCE,
425 )
426 .add_output(output_txout)
427 .add_output(UnspentTxOut::from_partial(anchor_output(
428 NON_EPHEMERAL_ANCHOR_AMOUNT,
429 )))
430 .add_output(UnspentTxOut::from_partial(op_return_txout))
431 .finalize();
432 txhandler.set_p2tr_key_spend_witness(&user_sig, 0)?;
433 txhandler.promote()
434}
435
436pub fn create_optimistic_payout_txhandler(
458 deposit_data: &mut DepositData,
459 input_utxo: UTXO,
460 output_txout: TxOut,
461 user_sig: taproot::Signature,
462 paramset: &'static ProtocolParamset,
463) -> Result<TxHandler, BridgeError> {
464 let move_txhandler: TxHandler = create_move_to_vault_txhandler(deposit_data, paramset)?;
465 let txin = SpendableTxIn::new_partial(input_utxo.outpoint, input_utxo.txout);
466
467 let output_txout = UnspentTxOut::from_partial(output_txout.clone());
468
469 let mut txhandler = TxHandlerBuilder::new(TransactionType::Payout)
470 .with_version(NON_STANDARD_V3)
471 .add_input(
472 NormalSignatureKind::NotStored,
473 txin,
474 SpendPath::KeySpend,
475 DEFAULT_SEQUENCE,
476 )
477 .add_input(
478 NormalSignatureKind::NotStored,
479 move_txhandler.get_spendable_output(UtxoVout::DepositInMove)?,
480 SpendPath::ScriptSpend(0),
481 DEFAULT_SEQUENCE,
482 )
483 .add_output(output_txout)
484 .add_output(UnspentTxOut::from_partial(
485 builder::transaction::non_ephemeral_anchor_output(),
486 ))
487 .finalize();
488 txhandler.set_p2tr_key_spend_witness(&user_sig, 0)?;
489 Ok(txhandler)
490}