clementine_core/
deposit.rs

1//! This module defines the data structures related to Citrea deposits in the Clementine bridge.
2//!
3//! It includes structures for representing deposit data, actors involved (verifiers, watchtowers, operators),
4//! and security council configurations. The module also provides functionality for managing different types
5//! of deposits (base and replacement) and deriving the necessary scripts these deposits must have.
6
7use std::collections::HashSet;
8use std::sync::Arc;
9
10use crate::builder::script::{
11    BaseDepositScript, Multisig, ReplacementDepositScript, SpendableScript, TimelockScript,
12};
13use crate::builder::transaction::create_move_to_vault_txhandler;
14use crate::config::protocol::ProtocolParamset;
15use crate::musig2::AggregateFromPublicKeys;
16use crate::utils::ScriptBufExt;
17use bitcoin::address::NetworkUnchecked;
18use bitcoin::secp256k1::PublicKey;
19use bitcoin::{Address, OutPoint, Txid, XOnlyPublicKey};
20use clementine_errors::BridgeError;
21use clementine_primitives::EVMAddress;
22use clementine_primitives::RoundIndex;
23use eyre::Context;
24
25/// Data structure to represent a single kickoff utxo in an operators round tx.
26#[derive(
27    Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize, Ord, PartialOrd,
28)]
29pub struct KickoffData {
30    pub operator_xonly_pk: XOnlyPublicKey,
31    pub round_idx: RoundIndex,
32    pub kickoff_idx: u32,
33}
34
35impl std::fmt::Display for KickoffData {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(
38            f,
39            "Kickoff(operator_xonly_pk: {}, round_idx: {}, kickoff_idx: {})",
40            self.operator_xonly_pk, self.round_idx, self.kickoff_idx
41        )
42    }
43}
44
45/// Data structure to represent a deposit.
46/// - nofn_xonly_pk is cached to avoid recomputing it each time.
47/// - deposit includes the actual information about the deposit.
48/// - actors includes the public keys of the actors that will participate in the deposit.
49/// - security_council includes the public keys of the security council that can unlock the deposit to create a replacement deposit in case a bug is found in the bridge.
50#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Eq)]
51pub struct DepositData {
52    /// Cached nofn xonly public key used for deposit.
53    pub nofn_xonly_pk: Option<XOnlyPublicKey>,
54    pub deposit: DepositInfo,
55    pub actors: Actors,
56    pub security_council: SecurityCouncil,
57}
58
59impl PartialEq for DepositData {
60    fn eq(&self, other: &Self) -> bool {
61        // nofn_xonly_pk only depends on verifiers pk's so it can be ignored as verifiers are already compared
62        // for security council, order of keys matter as it will change the m of n multisig script,
63        // thus change the scriptpubkey of move to vault tx
64        self.security_council == other.security_council
65            && self.deposit.deposit_outpoint == other.deposit.deposit_outpoint
66            // for watchtowers/verifiers/operators, order doesn't matter, we compare sorted lists
67            // get() functions already return sorted lists
68            && self.get_operators() == other.get_operators()
69            && self.get_verifiers() == other.get_verifiers()
70            && self.get_watchtowers() == other.get_watchtowers()
71            && self.deposit.deposit_type == other.deposit.deposit_type
72    }
73}
74
75impl DepositData {
76    /// Returns the move to vault txid of the deposit.
77    pub fn get_move_txid(
78        &mut self,
79        paramset: &'static ProtocolParamset,
80    ) -> Result<Txid, BridgeError> {
81        Ok(*create_move_to_vault_txhandler(self, paramset)?.get_txid())
82    }
83}
84
85/// Data structure to represent the deposit outpoint and type.
86#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
87pub struct DepositInfo {
88    pub deposit_outpoint: OutPoint,
89    pub deposit_type: DepositType,
90}
91
92/// Type to represent the type of deposit, and related specific data for each type..
93#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
94pub enum DepositType {
95    BaseDeposit(BaseDepositData),
96    ReplacementDeposit(ReplacementDepositData),
97}
98
99impl DepositData {
100    /// Returns the outpoint of the deposit.
101    pub fn get_deposit_outpoint(&self) -> OutPoint {
102        self.deposit.deposit_outpoint
103    }
104    /// Returns the nofn xonly public key of the deposit. It is additionally cached to avoid recomputing it each time.
105    pub fn get_nofn_xonly_pk(&mut self) -> Result<XOnlyPublicKey, BridgeError> {
106        if let Some(pk) = self.nofn_xonly_pk {
107            return Ok(pk);
108        }
109        let verifiers = self.get_verifiers();
110        let nofn_xonly_pk = bitcoin::XOnlyPublicKey::from_musig2_pks(verifiers, None)?;
111        self.nofn_xonly_pk = Some(nofn_xonly_pk);
112        Ok(nofn_xonly_pk)
113    }
114    /// Returns the number of verifiers in the deposit.
115    pub fn get_num_verifiers(&self) -> usize {
116        self.actors.verifiers.len()
117    }
118    /// Returns the number of watchtowers in the deposit.
119    pub fn get_num_watchtowers(&self) -> usize {
120        self.get_num_verifiers() + self.actors.watchtowers.len()
121    }
122    /// Returns the index of a verifier in the deposit, in the sorted order of verifiers pk.
123    pub fn get_verifier_index(&self, public_key: &PublicKey) -> Result<usize, eyre::Report> {
124        self.get_verifiers()
125            .iter()
126            .position(|pk| pk == public_key)
127            .ok_or_else(|| eyre::eyre!("Verifier with public key {} not found", public_key))
128    }
129    /// Returns the index of a watchtower in the deposit, in the sorted order of watchtowers pk.
130    pub fn get_watchtower_index(&self, xonly_pk: &XOnlyPublicKey) -> Result<usize, eyre::Report> {
131        self.get_watchtowers()
132            .iter()
133            .position(|pk| pk == xonly_pk)
134            .ok_or_else(|| eyre::eyre!("Watchtower with xonly key {} not found", xonly_pk))
135    }
136    /// Returns the index of an operator in the deposit, in the sorted order of operators pk.
137    pub fn get_operator_index(&self, xonly_pk: XOnlyPublicKey) -> Result<usize, eyre::Report> {
138        self.get_operators()
139            .iter()
140            .position(|pk| pk == &xonly_pk)
141            .ok_or_else(|| eyre::eyre!("Operator with xonly key {} not found", xonly_pk))
142    }
143    /// Returns sorted verifiers, they are sorted so that their order is deterministic.
144    pub fn get_verifiers(&self) -> Vec<PublicKey> {
145        let mut verifiers = self.actors.verifiers.clone();
146        verifiers.sort();
147
148        verifiers
149    }
150    /// Returns sorted watchtowers, they are sorted so that their order is deterministic.
151    /// It is very important for watchtowers to be sorted, as this is the order the watchtower challenge utxo's will be
152    /// in the kickoff tx. So any change in order will change the kickoff txid's.
153    pub fn get_watchtowers(&self) -> Vec<XOnlyPublicKey> {
154        let mut watchtowers = self
155            .actors
156            .verifiers
157            .iter()
158            .map(|pk| pk.x_only_public_key().0)
159            .collect::<Vec<_>>();
160        watchtowers.extend(self.actors.watchtowers.iter());
161        watchtowers.sort();
162        watchtowers
163    }
164    /// Returns sorted operators, they are sorted so that their order is deterministic.
165    pub fn get_operators(&self) -> Vec<XOnlyPublicKey> {
166        let mut operators = self.actors.operators.clone();
167        operators.sort();
168        operators
169    }
170    /// Returns the number of operators in the deposit.
171    pub fn get_num_operators(&self) -> usize {
172        self.actors.operators.len()
173    }
174    /// Returns the scripts a taproot address of a deposit_outpoint must have to spend the deposit.
175    /// Deposits not having these scripts and corresponding taproot address should be rejected.
176    pub fn get_deposit_scripts(
177        &mut self,
178        paramset: &'static ProtocolParamset,
179    ) -> Result<Vec<Arc<dyn SpendableScript>>, BridgeError> {
180        let nofn_xonly_pk = self.get_nofn_xonly_pk()?;
181
182        match &mut self.deposit.deposit_type {
183            DepositType::BaseDeposit(original_deposit_data) => {
184                let deposit_script = Arc::new(BaseDepositScript::new(
185                    nofn_xonly_pk,
186                    original_deposit_data.evm_address,
187                ));
188
189                let recovery_script_pubkey = original_deposit_data
190                    .recovery_taproot_address
191                    .clone()
192                    .assume_checked()
193                    .script_pubkey();
194
195                let recovery_extracted_xonly_pk = recovery_script_pubkey
196                    .try_get_taproot_pk()
197                    .wrap_err("Recovery taproot address is not a valid taproot address")?;
198
199                let script_timelock = Arc::new(TimelockScript::new(
200                    Some(recovery_extracted_xonly_pk),
201                    paramset.user_takes_after,
202                ));
203
204                Ok(vec![deposit_script, script_timelock])
205            }
206            DepositType::ReplacementDeposit(replacement_deposit_data) => {
207                let deposit_script: Arc<dyn SpendableScript> =
208                    Arc::new(ReplacementDepositScript::new(
209                        nofn_xonly_pk,
210                        replacement_deposit_data.old_move_txid,
211                    ));
212                let security_council_script: Arc<dyn SpendableScript> = Arc::new(
213                    Multisig::from_security_council(self.security_council.clone()),
214                );
215
216                Ok(vec![deposit_script, security_council_script])
217            }
218        }
219    }
220
221    /// Checks if all verifiers are unique.
222    pub fn are_all_verifiers_unique(&self) -> bool {
223        let set: HashSet<_> = self.actors.verifiers.iter().collect();
224        set.len() == self.actors.verifiers.len()
225    }
226
227    /// Checks if all watchtowers are unique.
228    pub fn are_all_watchtowers_unique(&self) -> bool {
229        let set: HashSet<_> = self.get_watchtowers().into_iter().collect();
230        set.len() == self.get_num_watchtowers()
231    }
232
233    /// Checks if all operators are unique.
234    pub fn are_all_operators_unique(&self) -> bool {
235        let set: HashSet<_> = self.actors.operators.iter().collect();
236        set.len() == self.actors.operators.len()
237    }
238}
239
240/// Data structure to represent the actors public keys that participate in the deposit.
241#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
242pub struct Actors {
243    /// Public keys of verifiers that will participate in the deposit.
244    pub verifiers: Vec<PublicKey>,
245    /// X-only public keys of watchtowers that will participate in the deposit.
246    /// NOTE: verifiers are automatically considered watchtowers. This field is only for additional watchtowers.
247    pub watchtowers: Vec<XOnlyPublicKey>,
248    /// X-only public keys of operators that will participate in the deposit.
249    pub operators: Vec<XOnlyPublicKey>,
250}
251
252/// Data structure to represent the security council that can unlock the deposit using an m-of-n multisig to create a replacement deposit.
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub struct SecurityCouncil {
255    pub pks: Vec<XOnlyPublicKey>,
256    pub threshold: u32,
257}
258
259impl std::str::FromStr for SecurityCouncil {
260    type Err = eyre::Report;
261
262    fn from_str(s: &str) -> Result<Self, Self::Err> {
263        let mut parts = s.split(':');
264        let threshold_str = parts
265            .next()
266            .ok_or_else(|| eyre::eyre!("Missing threshold"))?;
267        let pks_str = parts
268            .next()
269            .ok_or_else(|| eyre::eyre!("Missing public keys"))?;
270
271        if parts.next().is_some() {
272            return Err(eyre::eyre!("Too many parts in security council string"));
273        }
274
275        let threshold = threshold_str
276            .parse::<u32>()
277            .map_err(|e| eyre::eyre!("Invalid threshold: {}", e))?;
278
279        let pks: Result<Vec<XOnlyPublicKey>, _> = pks_str
280            .split(',')
281            .map(|pk_str| {
282                let bytes = hex::decode(pk_str)
283                    .map_err(|e| eyre::eyre!("Invalid hex in public key: {}", e))?;
284                XOnlyPublicKey::from_slice(&bytes)
285                    .map_err(|e| eyre::eyre!("Invalid public key: {}", e))
286            })
287            .collect();
288
289        let pks = pks?;
290
291        if pks.is_empty() {
292            return Err(eyre::eyre!("No public keys provided"));
293        }
294
295        if threshold > pks.len() as u32 {
296            return Err(eyre::eyre!(
297                "Threshold cannot be greater than number of public keys"
298            ));
299        }
300
301        Ok(SecurityCouncil { pks, threshold })
302    }
303}
304
305impl serde::Serialize for SecurityCouncil {
306    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
307    where
308        S: serde::Serializer,
309    {
310        serializer.serialize_str(&self.to_string())
311    }
312}
313
314impl<'de> serde::Deserialize<'de> for SecurityCouncil {
315    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
316    where
317        D: serde::Deserializer<'de>,
318    {
319        let s = String::deserialize(deserializer)?;
320        s.parse().map_err(serde::de::Error::custom)
321    }
322}
323
324impl std::fmt::Display for SecurityCouncil {
325    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326        write!(f, "{}:", self.threshold)?;
327        let pks_str = self
328            .pks
329            .iter()
330            .map(|pk| hex::encode(pk.serialize()))
331            .collect::<Vec<_>>()
332            .join(",");
333        write!(f, "{pks_str}")
334    }
335}
336
337/// Data structure to represent the data for a base deposit. These kinds of deposits are created by users.
338#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
339pub struct BaseDepositData {
340    /// User's EVM address.
341    pub evm_address: EVMAddress,
342    /// User's recovery taproot address.
343    pub recovery_taproot_address: bitcoin::Address<NetworkUnchecked>,
344}
345
346/// Data structure to represent the data for a replacement deposit. These kinds of deposits are created by the bridge, using
347/// security council to unlock the previous deposit and move the funds to create a new deposit. Verifiers will sign the new deposit again.
348#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
349pub struct ReplacementDepositData {
350    /// old move_to_vault txid that was replaced
351    pub old_move_txid: Txid,
352}
353
354/// Data structure to represent the data for an operator. These data is used in the tx creation so any deviation will change the tx's
355/// created by the bridge.
356#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
357pub struct OperatorData {
358    pub xonly_pk: XOnlyPublicKey,
359    pub reimburse_addr: Address,
360    pub collateral_funding_outpoint: OutPoint,
361}
362
363impl<'de> serde::Deserialize<'de> for OperatorData {
364    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
365    where
366        D: serde::Deserializer<'de>,
367    {
368        #[derive(serde::Deserialize)]
369        struct OperatorDataHelper {
370            xonly_pk: XOnlyPublicKey,
371            reimburse_addr: Address<NetworkUnchecked>,
372            collateral_funding_outpoint: OutPoint,
373        }
374
375        let helper = OperatorDataHelper::deserialize(deserializer)?;
376
377        Ok(OperatorData {
378            xonly_pk: helper.xonly_pk,
379            reimburse_addr: helper.reimburse_addr.assume_checked(),
380            collateral_funding_outpoint: helper.collateral_funding_outpoint,
381        })
382    }
383}