clementine_core/builder/transaction/challenge.rs
1//! # Challenge Transaction Logic
2//!
3//! This module provides functions for constructing and challenge related transactions in the protocol.
4//! The transactions are: Challenge, ChallengeTimeout, OperatorChallengeNack, OperatorChallengeAck, Disprove.
5
6use crate::builder;
7use crate::builder::script::SpendPath;
8use crate::builder::transaction::output::UnspentTxOut;
9use crate::builder::transaction::txhandler::{TxHandler, DEFAULT_SEQUENCE};
10use crate::builder::transaction::*;
11use crate::config::protocol::ProtocolParamset;
12use crate::constants::NON_STANDARD_V3;
13use crate::rpc::clementine::{NormalSignatureKind, NumberedSignatureKind};
14use bitcoin::script::PushBytesBuf;
15use bitcoin::{Sequence, TxOut};
16use clementine_errors::{BridgeError, TransactionType, TxError};
17use clementine_primitives::EVMAddress;
18use eyre::Context;
19
20use self::input::UtxoVout;
21
22/// Creates a [`TxHandler`] for the `watchtower_challenge_tx`.
23///
24/// This transaction is sent by a watchtower to submit a challenge proof (e.g., a Groth16 proof with public inputs).
25/// The proof data is encoded in a single OP_RETURN output.
26///
27/// # Inputs
28/// 1. KickoffTx: WatchtowerChallenge utxo (for the given watchtower)
29///
30/// # Outputs
31/// 1. OP_RETURN output containing the challenge data.
32/// 2. Anchor output for CPFP.
33///
34/// # Arguments
35///
36/// * `kickoff_txhandler` - The kickoff transaction handler the watchtower challenge belongs to.
37/// * `watchtower_idx` - The index of the watchtower in the deposit submitting the challenge.
38/// * `commit_data` - The challenge proof data to be included in the transaction.
39/// * `paramset` - Protocol parameter set.
40///
41/// # Returns
42///
43/// A [`TxHandler`] for the watchtower challenge transaction, or a [`BridgeError`] if construction fails.
44pub fn create_watchtower_challenge_txhandler(
45 kickoff_txhandler: &TxHandler,
46 watchtower_idx: usize,
47 commit_data: &[u8],
48 paramset: &'static ProtocolParamset,
49 #[cfg(test)] test_params: &crate::config::TestParams,
50) -> Result<TxHandler, BridgeError> {
51 if commit_data.len() != paramset.watchtower_challenge_bytes {
52 return Err(TxError::IncorrectWatchtowerChallengeDataLength.into());
53 }
54 let mut builder = TxHandlerBuilder::new(TransactionType::WatchtowerChallenge(watchtower_idx))
55 .with_version(NON_STANDARD_V3)
56 .add_input(
57 (
58 NumberedSignatureKind::WatchtowerChallenge,
59 watchtower_idx as i32,
60 ),
61 kickoff_txhandler
62 .get_spendable_output(UtxoVout::WatchtowerChallenge(watchtower_idx))?,
63 SpendPath::KeySpend,
64 DEFAULT_SEQUENCE,
65 );
66
67 let push_data = PushBytesBuf::try_from(commit_data.to_vec())
68 .wrap_err("Failed to create pushbytesbuf for watchtower challenge op_return")?;
69 builder = builder
70 .add_output(UnspentTxOut::from_partial(op_return_txout(push_data)))
71 .add_output(UnspentTxOut::from_partial(anchor_output(
72 paramset.anchor_amount(),
73 )));
74
75 #[cfg(test)]
76 {
77 builder = test_params.maybe_add_large_test_outputs(builder)?;
78 }
79
80 Ok(builder.finalize())
81}
82
83/// Creates a [`TxHandler`] for the `watchtower_challenge_timeout_tx`.
84///
85/// This transaction is sent by an operator if a watchtower does not submit a challenge in time, allowing the operator to claim a timeout.
86/// This way, operators do not need to reveal their preimage, and do not need to use the watchtowers longest chain proof in their
87/// bridge proof.
88///
89/// # Inputs
90/// 1. KickoffTx: WatchtowerChallenge utxo (for the given watchtower)
91/// 2. KickoffTx: WatchtowerChallengeAck utxo (for the given watchtower)
92///
93/// # Outputs
94/// 1. Anchor output for CPFP
95///
96/// # Arguments
97///
98/// * `kickoff_txhandler` - The kickoff transaction handler the watchtower challenge timeout belongs to.
99/// * `watchtower_idx` - The index of the watchtower in the deposit submitting the challenge.
100/// * `paramset` - Protocol parameter set.
101///
102/// # Returns
103///
104/// A [`TxHandler`] for the watchtower challenge timeout transaction, or a [`BridgeError`] if construction fails.
105pub fn create_watchtower_challenge_timeout_txhandler(
106 kickoff_txhandler: &TxHandler,
107 watchtower_idx: usize,
108 paramset: &'static ProtocolParamset,
109) -> Result<TxHandler, BridgeError> {
110 let watchtower_challenge_vout = UtxoVout::WatchtowerChallenge(watchtower_idx);
111 let challenge_ack_vout = UtxoVout::WatchtowerChallengeAck(watchtower_idx);
112 Ok(
113 TxHandlerBuilder::new(TransactionType::WatchtowerChallengeTimeout(watchtower_idx))
114 .with_version(NON_STANDARD_V3)
115 .add_input(
116 (
117 NumberedSignatureKind::WatchtowerChallengeTimeout1,
118 watchtower_idx as i32,
119 ),
120 kickoff_txhandler.get_spendable_output(watchtower_challenge_vout)?,
121 SpendPath::ScriptSpend(0),
122 Sequence::from_height(paramset.watchtower_challenge_timeout_timelock),
123 )
124 .add_input(
125 (
126 NumberedSignatureKind::WatchtowerChallengeTimeout2,
127 watchtower_idx as i32,
128 ),
129 kickoff_txhandler.get_spendable_output(challenge_ack_vout)?,
130 SpendPath::ScriptSpend(1),
131 Sequence::from_height(paramset.watchtower_challenge_timeout_timelock),
132 )
133 .add_output(UnspentTxOut::from_partial(
134 builder::transaction::anchor_output(paramset.anchor_amount()),
135 ))
136 .finalize(),
137 )
138}
139
140/// Creates a [`TxHandler`] for the `OperatorChallengeNack` transaction.
141///
142/// This transaction is used to force an operator to reveal a preimage for a watchtower challenge. If a watchtower sends a watchtower challenge,
143/// but the operator does not reveal the preimage by sending an OperatorChallengeAck, after a specified number of time (defined in paramset),
144/// the N-of-N can spend the output, burning the operator's collateral.
145///
146/// # Inputs
147/// 1. KickoffTx: WatchtowerChallengeAck utxo (for the given watchtower)
148/// 2. KickoffTx: KickoffFinalizer utxo
149/// 3. RoundTx: BurnConnector utxo
150///
151/// # Outputs
152/// 1. Anchor output for CPFP
153///
154/// # Arguments
155///
156/// * `kickoff_txhandler` - The kickoff transaction handler the operator challenge nack belongs to.
157/// * `watchtower_idx` - The index of the watchtower in the deposit corresponding to the watchtower challenge related to the operator challenge nack.
158/// * `round_txhandler` - The round transaction handler for the current round the kickoff belongs to.
159/// * `paramset` - Protocol parameter set.
160///
161/// # Returns
162///
163/// A [`TxHandler`] for the operator challenge NACK transaction, or a [`BridgeError`] if construction fails.
164pub fn create_operator_challenge_nack_txhandler(
165 kickoff_txhandler: &TxHandler,
166 watchtower_idx: usize,
167 round_txhandler: &TxHandler,
168 paramset: &'static ProtocolParamset,
169) -> Result<TxHandler, BridgeError> {
170 Ok(
171 TxHandlerBuilder::new(TransactionType::OperatorChallengeNack(watchtower_idx))
172 .with_version(NON_STANDARD_V3)
173 .add_input(
174 (
175 NumberedSignatureKind::OperatorChallengeNack1,
176 watchtower_idx as i32,
177 ),
178 kickoff_txhandler
179 .get_spendable_output(UtxoVout::WatchtowerChallengeAck(watchtower_idx))?,
180 SpendPath::ScriptSpend(0),
181 Sequence::from_height(paramset.operator_challenge_nack_timelock),
182 )
183 .add_input(
184 (
185 NumberedSignatureKind::OperatorChallengeNack2,
186 watchtower_idx as i32,
187 ),
188 kickoff_txhandler.get_spendable_output(UtxoVout::KickoffFinalizer)?,
189 SpendPath::ScriptSpend(0),
190 DEFAULT_SEQUENCE,
191 )
192 .add_input(
193 (
194 NumberedSignatureKind::OperatorChallengeNack3,
195 watchtower_idx as i32,
196 ),
197 round_txhandler.get_spendable_output(UtxoVout::CollateralInRound)?,
198 SpendPath::KeySpend,
199 DEFAULT_SEQUENCE,
200 )
201 .add_output(UnspentTxOut::from_partial(
202 builder::transaction::anchor_output(paramset.anchor_amount()),
203 ))
204 .finalize(),
205 )
206}
207
208/// Creates a [`TxHandler`] for the OperatorChallengeAck transaction.
209///
210/// This transaction is used by an operator to acknowledge a watchtower challenge and reveal the required preimage, if a watchtower challenge is sent.
211///
212/// # Inputs
213/// 1. KickoffTx: WatchtowerChallengeAck utxo (for the given watchtower)
214///
215/// # Outputs
216/// 1. Anchor output for CPFP
217/// 2. Dummy OP_RETURN output (to pad the size of the transaction, as it is too small otherwise)
218///
219/// # Arguments
220///
221/// * `kickoff_txhandler` - The kickoff transaction handler the operator challenge ack belongs to.
222/// * `watchtower_idx` - The index of the watchtower that sent the challenge.
223/// * `paramset` - Protocol parameter set.
224///
225/// # Returns
226///
227/// A [`TxHandler`] for the operator challenge ACK transaction, or a [`BridgeError`] if construction fails.
228pub fn create_operator_challenge_ack_txhandler(
229 kickoff_txhandler: &TxHandler,
230 watchtower_idx: usize,
231 paramset: &'static ProtocolParamset,
232) -> Result<TxHandler, BridgeError> {
233 Ok(
234 TxHandlerBuilder::new(TransactionType::OperatorChallengeAck(watchtower_idx))
235 .with_version(NON_STANDARD_V3)
236 .add_input(
237 NormalSignatureKind::OperatorChallengeAck1,
238 kickoff_txhandler
239 .get_spendable_output(UtxoVout::WatchtowerChallengeAck(watchtower_idx))?,
240 SpendPath::ScriptSpend(2),
241 DEFAULT_SEQUENCE,
242 )
243 .add_output(UnspentTxOut::from_partial(
244 builder::transaction::anchor_output(paramset.anchor_amount()),
245 ))
246 .add_output(UnspentTxOut::from_partial(op_return_txout(b"PADDING")))
247 .finalize(),
248 )
249}
250
251/// Creates a [`TxHandler`] for the `disprove_tx`.
252///
253/// This transaction is sent by N-of-N to penalize a malicious operator by burning their collateral (burn connector).
254/// This is done either with the additional disprove script created by BitVM, in case the public inputs of the bridge proof the operator
255/// sent are not correct/do not match previous data, or if the Groth16 verification of the proof is incorrect using BitVM disprove scripts.
256///
257/// # Inputs
258/// 1. KickoffTx: Disprove utxo
259/// 2. RoundTx: BurnConnector utxo
260///
261/// # Outputs
262/// 1. Anchor output for CPFP
263///
264/// # Arguments
265///
266/// * `kickoff_txhandler` - The kickoff transaction handler the disprove belongs to.
267/// * `round_txhandler` - The round transaction handler to the current round the kickoff belongs to.
268///
269/// # Returns
270///
271/// A [`TxHandler`] for the disprove transaction, or a [`BridgeError`] if construction fails.
272pub fn create_disprove_txhandler(
273 kickoff_txhandler: &TxHandler,
274 round_txhandler: &TxHandler,
275) -> Result<TxHandler, BridgeError> {
276 Ok(TxHandlerBuilder::new(TransactionType::Disprove)
277 .with_version(Version::TWO)
278 .add_input(
279 NormalSignatureKind::NoSignature,
280 kickoff_txhandler.get_spendable_output(UtxoVout::Disprove)?,
281 SpendPath::Unknown,
282 DEFAULT_SEQUENCE,
283 )
284 .add_input(
285 NormalSignatureKind::Disprove2,
286 round_txhandler.get_spendable_output(UtxoVout::CollateralInRound)?,
287 SpendPath::KeySpend,
288 DEFAULT_SEQUENCE,
289 )
290 .add_output(UnspentTxOut::from_partial(
291 builder::transaction::non_ephemeral_anchor_output(), // must be non-ephemeral, because tx is v2
292 ))
293 .finalize())
294}
295
296/// Creates a [`TxHandler`] for the `challenge` transaction.
297///
298/// This transaction is used to reimburse an operator for a valid challenge, intended to cover their costs for sending asserts transactions,
299/// and potentially cover their opportunity cost as their reimbursements are delayed due to the challenge. This cost of a challenge is also
300/// used to disincentivize sending challenges for kickoffs that are correct. In case the challenge is correct and operator is proved to be
301/// malicious, the challenge cost will be reimbursed using the operator's collateral that's locked in Citrea.
302///
303/// # Inputs
304/// 1. KickoffTx: Challenge utxo
305///
306/// # Outputs
307/// 1. Operator reimbursement output
308/// 2. OP_RETURN output (containing EVM address of the challenger, for reimbursement if the challenge is correct)
309///
310/// # Arguments
311///
312/// * `kickoff_txhandler` - The kickoff transaction handler that the challenge belongs to.
313/// * `operator_reimbursement_address` - The address to reimburse the operator to cover their costs.
314/// * `challenger_evm_address` - The EVM address of the challenger, for reimbursement if the challenge is correct.
315/// * `paramset` - Protocol parameter set.
316///
317/// # Returns
318///
319/// A [`TxHandler`] for the challenge transaction, or a [`BridgeError`] if construction fails.
320pub fn create_challenge_txhandler(
321 kickoff_txhandler: &TxHandler,
322 operator_reimbursement_address: &bitcoin::Address,
323 challenger_evm_address: Option<EVMAddress>,
324 paramset: &'static ProtocolParamset,
325) -> Result<TxHandler, BridgeError> {
326 let mut builder = TxHandlerBuilder::new(TransactionType::Challenge)
327 .with_version(NON_STANDARD_V3)
328 .add_input(
329 NormalSignatureKind::Challenge,
330 kickoff_txhandler.get_spendable_output(UtxoVout::Challenge)?,
331 SpendPath::ScriptSpend(0),
332 DEFAULT_SEQUENCE,
333 )
334 .add_output(UnspentTxOut::from_partial(TxOut {
335 value: paramset.operator_challenge_amount,
336 script_pubkey: operator_reimbursement_address.script_pubkey(),
337 }));
338
339 if let Some(challenger_evm_address) = challenger_evm_address {
340 builder = builder.add_output(UnspentTxOut::from_partial(op_return_txout(
341 challenger_evm_address.0,
342 )));
343 }
344
345 Ok(builder.finalize())
346}
347
348/// Creates a [`TxHandler`] for the `challenge_timeout` transaction.
349///
350/// This transaction is used to finalize a kickoff if no challenge is submitted in time, allowing the operator to proceed faster to the next round, thus getting their reimbursement, as the next round will generate the reimbursement connectors of the current round.
351///
352/// # Inputs
353/// 1. KickoffTx: Challenge utxo
354/// 2. KickoffTx: KickoffFinalizer utxo
355///
356/// # Outputs
357/// 1. Anchor output for CPFP
358///
359/// # Arguments
360///
361/// * `kickoff_txhandler` - The kickoff transaction handler the challenge timeout belongs to.
362/// * `paramset` - Protocol parameter set.
363///
364/// # Returns
365///
366/// A [`TxHandler`] for the challenge timeout transaction, or a [`BridgeError`] if construction fails.
367pub fn create_challenge_timeout_txhandler(
368 kickoff_txhandler: &TxHandler,
369 paramset: &'static ProtocolParamset,
370) -> Result<TxHandler, BridgeError> {
371 Ok(TxHandlerBuilder::new(TransactionType::ChallengeTimeout)
372 .with_version(NON_STANDARD_V3)
373 .add_input(
374 NormalSignatureKind::OperatorSighashDefault,
375 kickoff_txhandler.get_spendable_output(UtxoVout::Challenge)?,
376 SpendPath::ScriptSpend(1),
377 Sequence::from_height(paramset.operator_challenge_timeout_timelock),
378 )
379 .add_input(
380 NormalSignatureKind::ChallengeTimeout2,
381 kickoff_txhandler.get_spendable_output(UtxoVout::KickoffFinalizer)?,
382 SpendPath::ScriptSpend(0),
383 DEFAULT_SEQUENCE,
384 )
385 .add_output(UnspentTxOut::from_partial(
386 builder::transaction::anchor_output(paramset.anchor_amount()),
387 ))
388 .finalize())
389}