1use super::script::{
8 BaseDepositScript, Multisig, ReplacementDepositScript, SpendableScript, TimelockScript,
9};
10use crate::deposit::SecurityCouncil;
11use crate::utils::ScriptBufExt;
12use bitcoin::address::NetworkUnchecked;
13use bitcoin::{secp256k1::XOnlyPublicKey, taproot::TaprootSpendInfo, Address};
14use clementine_errors::BridgeError;
15use clementine_primitives::EVMAddress;
16
17use eyre::Context;
18
19pub use clementine_utils::address::{
20 calculate_taproot_leaf_depths, create_taproot_address, taproot_builder_with_scripts,
21};
22
23pub fn generate_deposit_address(
46 nofn_xonly_pk: XOnlyPublicKey,
47 recovery_taproot_address: &Address<NetworkUnchecked>,
48 user_evm_address: EVMAddress,
49 network: bitcoin::Network,
50 user_takes_after: u16,
51) -> Result<(Address, TaprootSpendInfo), BridgeError> {
52 let deposit_script = BaseDepositScript::new(nofn_xonly_pk, user_evm_address).to_script_buf();
53
54 let recovery_script_pubkey = recovery_taproot_address
55 .clone()
56 .assume_checked()
57 .script_pubkey();
58
59 let recovery_extracted_xonly_pk = recovery_script_pubkey
60 .try_get_taproot_pk()
61 .wrap_err("Recovery taproot address is not a valid taproot address")?;
62
63 let script_timelock =
64 TimelockScript::new(Some(recovery_extracted_xonly_pk), user_takes_after).to_script_buf();
65
66 let (addr, spend) = create_taproot_address(&[deposit_script, script_timelock], None, network);
67 Ok((addr, spend))
68}
69
70pub fn generate_replacement_deposit_address(
90 old_move_txid: bitcoin::Txid,
91 nofn_xonly_pk: XOnlyPublicKey,
92 network: bitcoin::Network,
93 security_council: SecurityCouncil,
94) -> Result<(Address, TaprootSpendInfo), BridgeError> {
95 let deposit_script =
96 ReplacementDepositScript::new(nofn_xonly_pk, old_move_txid).to_script_buf();
97
98 let security_council_script = Multisig::from_security_council(security_council).to_script_buf();
99
100 let (addr, spend) =
101 create_taproot_address(&[deposit_script, security_council_script], None, network);
102 Ok((addr, spend))
103}
104
105#[cfg(test)]
106mod tests {
107 use crate::{
108 bitvm_client::{self, SECP},
109 builder::{self, address::calculate_taproot_leaf_depths},
110 };
111 use bitcoin::secp256k1::rand;
112 use bitcoin::{
113 key::{Keypair, TapTweak},
114 secp256k1::SecretKey,
115 AddressType, ScriptBuf, XOnlyPublicKey,
116 };
117
118 #[test]
119 fn create_taproot_address() {
120 let secret_key = SecretKey::new(&mut rand::thread_rng());
121 let internal_key =
122 XOnlyPublicKey::from_keypair(&Keypair::from_secret_key(&SECP, &secret_key)).0;
123
124 let (address, spend_info) =
126 builder::address::create_taproot_address(&[], None, bitcoin::Network::Regtest);
127 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
128 assert!(address.is_related_to_xonly_pubkey(
129 &bitvm_client::UNSPENDABLE_XONLY_PUBKEY
130 .tap_tweak(&SECP, spend_info.merkle_root())
131 .0
132 .to_x_only_public_key()
133 ));
134 assert_eq!(
135 spend_info.internal_key(),
136 *bitvm_client::UNSPENDABLE_XONLY_PUBKEY
137 );
138 assert!(spend_info.merkle_root().is_none());
139
140 let (address, spend_info) = builder::address::create_taproot_address(
142 &[],
143 Some(internal_key),
144 bitcoin::Network::Regtest,
145 );
146 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
147 assert!(address.is_related_to_xonly_pubkey(
148 &internal_key
149 .tap_tweak(&SECP, spend_info.merkle_root())
150 .0
151 .to_x_only_public_key()
152 ));
153 assert_eq!(spend_info.internal_key(), internal_key);
154 assert!(spend_info.merkle_root().is_none());
155
156 let scripts = [ScriptBuf::new()];
157 let (address, spend_info) = builder::address::create_taproot_address(
158 &scripts,
159 Some(internal_key),
160 bitcoin::Network::Regtest,
161 );
162 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
163 assert!(address.is_related_to_xonly_pubkey(
164 &internal_key
165 .tap_tweak(&SECP, spend_info.merkle_root())
166 .0
167 .to_x_only_public_key()
168 ));
169 assert_eq!(spend_info.internal_key(), internal_key);
170 assert!(spend_info.merkle_root().is_some());
171
172 let scripts = [ScriptBuf::new(), ScriptBuf::new()];
173 let (address, spend_info) = builder::address::create_taproot_address(
174 &scripts,
175 Some(internal_key),
176 bitcoin::Network::Regtest,
177 );
178 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
179 assert!(address.is_related_to_xonly_pubkey(
180 &internal_key
181 .tap_tweak(&SECP, spend_info.merkle_root())
182 .0
183 .to_x_only_public_key()
184 ));
185 assert_eq!(spend_info.internal_key(), internal_key);
186 assert!(spend_info.merkle_root().is_some());
187 }
188
189 #[test]
190 pub fn test_taproot_builder_with_scripts() {
191 for i in [0, 1, 10, 50, 100, 1000].into_iter() {
192 let scripts = (0..i)
193 .map(|k| ScriptBuf::builder().push_int(k).into_script())
194 .collect::<Vec<_>>();
195 let builder = super::taproot_builder_with_scripts(scripts);
196 let tree_info = builder
197 .finalize(&SECP, *bitvm_client::UNSPENDABLE_XONLY_PUBKEY)
198 .unwrap();
199
200 assert_eq!(tree_info.script_map().len(), i as usize);
201 }
202 }
203
204 #[test]
205 fn test_calculate_taproot_leaf_depths() {
206 let expected: Vec<u8> = vec![];
208 assert_eq!(calculate_taproot_leaf_depths(0), expected);
209
210 assert_eq!(calculate_taproot_leaf_depths(1), vec![0]);
212
213 assert_eq!(calculate_taproot_leaf_depths(2), vec![1, 1]);
215
216 assert_eq!(calculate_taproot_leaf_depths(3), vec![2, 2, 1]);
219
220 assert_eq!(calculate_taproot_leaf_depths(4), vec![2, 2, 2, 2]);
222
223 assert_eq!(calculate_taproot_leaf_depths(5), vec![3, 3, 2, 2, 2]);
230
231 assert_eq!(
233 calculate_taproot_leaf_depths(8),
234 vec![3, 3, 3, 3, 3, 3, 3, 3]
235 );
236 }
237}