1use super::script::{
8 BaseDepositScript, CheckSig, Multisig, ReplacementDepositScript, SpendableScript,
9 TimelockScript,
10};
11use crate::bitvm_client::SECP;
12use crate::deposit::SecurityCouncil;
13use crate::errors::BridgeError;
14use crate::utils::ScriptBufExt;
15use crate::{bitvm_client, EVMAddress};
16use bitcoin::address::NetworkUnchecked;
17use bitcoin::{
18 secp256k1::XOnlyPublicKey,
19 taproot::{TaprootBuilder, TaprootSpendInfo},
20 Address, ScriptBuf,
21};
22
23use eyre::Context;
24
25pub fn taproot_builder_with_scripts(scripts: impl Into<Vec<ScriptBuf>>) -> TaprootBuilder {
28 let mut scripts: Vec<ScriptBuf> = scripts.into();
30 let builder = TaprootBuilder::new();
31 let num_scripts = scripts.len();
32
33 match num_scripts {
35 0 => return builder,
36 1 => {
37 return builder
38 .add_leaf(0, scripts.remove(0))
39 .expect("one root leaf added on empty builder")
40 }
41 _ => {}
42 }
43
44 let deepest_layer_depth: u8 = ((num_scripts - 1).ilog2() + 1) as u8;
45
46 let num_empty_nodes_in_final_depth = 2_usize.pow(deepest_layer_depth.into()) - num_scripts;
47 let num_nodes_in_final_depth = num_scripts - num_empty_nodes_in_final_depth;
48
49 scripts
50 .into_iter()
51 .enumerate()
52 .fold(builder, |acc, (i, script)| {
53 let is_node_in_last_minus_one_depth = (i >= num_nodes_in_final_depth) as u8;
54
55 acc.add_leaf(
56 deepest_layer_depth - is_node_in_last_minus_one_depth,
57 script,
58 )
59 .expect("algorithm tested to be correct")
60 })
61}
62
63pub fn calculate_taproot_leaf_depths(num_scripts: usize) -> Vec<u8> {
66 match num_scripts {
67 0 => return vec![],
68 1 => return vec![0],
69 _ => {}
70 }
71
72 let deepest_layer_depth: u8 = ((num_scripts - 1).ilog2() + 1) as u8;
73
74 let num_empty_nodes_in_final_depth = 2_usize.pow(deepest_layer_depth.into()) - num_scripts;
75 let num_nodes_in_final_depth = num_scripts - num_empty_nodes_in_final_depth;
76
77 (0..num_scripts)
78 .map(|i| {
79 let is_node_in_last_minus_one_depth = (i >= num_nodes_in_final_depth) as u8;
80 deepest_layer_depth - is_node_in_last_minus_one_depth
81 })
82 .collect()
83}
84
85pub fn create_taproot_address(
104 scripts: &[ScriptBuf],
105 internal_key: Option<XOnlyPublicKey>,
106 network: bitcoin::Network,
107) -> (Address, TaprootSpendInfo) {
108 let taproot_builder = taproot_builder_with_scripts(scripts);
110 let tree_info = match internal_key {
112 Some(xonly_pk) => taproot_builder
113 .finalize(&SECP, xonly_pk)
114 .expect("builder return is finalizable"),
115 None => taproot_builder
116 .finalize(&SECP, *bitvm_client::UNSPENDABLE_XONLY_PUBKEY)
117 .expect("builder return is finalizable"),
118 };
119
120 let taproot_address: Address = Address::p2tr_tweaked(tree_info.output_key(), network);
122
123 (taproot_address, tree_info)
124}
125
126pub fn generate_deposit_address(
149 nofn_xonly_pk: XOnlyPublicKey,
150 recovery_taproot_address: &Address<NetworkUnchecked>,
151 user_evm_address: EVMAddress,
152 network: bitcoin::Network,
153 user_takes_after: u16,
154) -> Result<(Address, TaprootSpendInfo), BridgeError> {
155 let deposit_script = BaseDepositScript::new(nofn_xonly_pk, user_evm_address).to_script_buf();
156
157 let recovery_script_pubkey = recovery_taproot_address
158 .clone()
159 .assume_checked()
160 .script_pubkey();
161
162 let recovery_extracted_xonly_pk = recovery_script_pubkey
163 .try_get_taproot_pk()
164 .wrap_err("Recovery taproot address is not a valid taproot address")?;
165
166 let script_timelock =
167 TimelockScript::new(Some(recovery_extracted_xonly_pk), user_takes_after).to_script_buf();
168
169 let (addr, spend) = create_taproot_address(&[deposit_script, script_timelock], None, network);
170 Ok((addr, spend))
171}
172
173pub fn generate_replacement_deposit_address(
193 old_move_txid: bitcoin::Txid,
194 nofn_xonly_pk: XOnlyPublicKey,
195 network: bitcoin::Network,
196 security_council: SecurityCouncil,
197) -> Result<(Address, TaprootSpendInfo), BridgeError> {
198 let deposit_script =
199 ReplacementDepositScript::new(nofn_xonly_pk, old_move_txid).to_script_buf();
200
201 let security_council_script = Multisig::from_security_council(security_council).to_script_buf();
202
203 let (addr, spend) =
204 create_taproot_address(&[deposit_script, security_council_script], None, network);
205 Ok((addr, spend))
206}
207
208pub fn create_checksig_address(
217 xonly_pk: XOnlyPublicKey,
218 network: bitcoin::Network,
219) -> (Address, TaprootSpendInfo) {
220 let script = CheckSig::new(xonly_pk);
221 create_taproot_address(&[script.to_script_buf()], None, network)
222}
223
224#[cfg(test)]
225mod tests {
226 use crate::{
227 bitvm_client::{self, SECP},
228 builder::{self, address::calculate_taproot_leaf_depths},
229 };
230 use bitcoin::secp256k1::rand;
231 use bitcoin::{
232 key::{Keypair, TapTweak},
233 secp256k1::SecretKey,
234 AddressType, ScriptBuf, XOnlyPublicKey,
235 };
236
237 #[test]
238 fn create_taproot_address() {
239 let secret_key = SecretKey::new(&mut rand::thread_rng());
240 let internal_key =
241 XOnlyPublicKey::from_keypair(&Keypair::from_secret_key(&SECP, &secret_key)).0;
242
243 let (address, spend_info) =
245 builder::address::create_taproot_address(&[], None, bitcoin::Network::Regtest);
246 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
247 assert!(address.is_related_to_xonly_pubkey(
248 &bitvm_client::UNSPENDABLE_XONLY_PUBKEY
249 .tap_tweak(&SECP, spend_info.merkle_root())
250 .0
251 .to_x_only_public_key()
252 ));
253 assert_eq!(
254 spend_info.internal_key(),
255 *bitvm_client::UNSPENDABLE_XONLY_PUBKEY
256 );
257 assert!(spend_info.merkle_root().is_none());
258
259 let (address, spend_info) = builder::address::create_taproot_address(
261 &[],
262 Some(internal_key),
263 bitcoin::Network::Regtest,
264 );
265 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
266 assert!(address.is_related_to_xonly_pubkey(
267 &internal_key
268 .tap_tweak(&SECP, spend_info.merkle_root())
269 .0
270 .to_x_only_public_key()
271 ));
272 assert_eq!(spend_info.internal_key(), internal_key);
273 assert!(spend_info.merkle_root().is_none());
274
275 let scripts = [ScriptBuf::new()];
276 let (address, spend_info) = builder::address::create_taproot_address(
277 &scripts,
278 Some(internal_key),
279 bitcoin::Network::Regtest,
280 );
281 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
282 assert!(address.is_related_to_xonly_pubkey(
283 &internal_key
284 .tap_tweak(&SECP, spend_info.merkle_root())
285 .0
286 .to_x_only_public_key()
287 ));
288 assert_eq!(spend_info.internal_key(), internal_key);
289 assert!(spend_info.merkle_root().is_some());
290
291 let scripts = [ScriptBuf::new(), ScriptBuf::new()];
292 let (address, spend_info) = builder::address::create_taproot_address(
293 &scripts,
294 Some(internal_key),
295 bitcoin::Network::Regtest,
296 );
297 assert_eq!(address.address_type().unwrap(), AddressType::P2tr);
298 assert!(address.is_related_to_xonly_pubkey(
299 &internal_key
300 .tap_tweak(&SECP, spend_info.merkle_root())
301 .0
302 .to_x_only_public_key()
303 ));
304 assert_eq!(spend_info.internal_key(), internal_key);
305 assert!(spend_info.merkle_root().is_some());
306 }
307
308 #[test]
309 pub fn test_taproot_builder_with_scripts() {
310 for i in [0, 1, 10, 50, 100, 1000].into_iter() {
311 let scripts = (0..i)
312 .map(|k| ScriptBuf::builder().push_int(k).into_script())
313 .collect::<Vec<_>>();
314 let builder = super::taproot_builder_with_scripts(scripts);
315 let tree_info = builder
316 .finalize(&SECP, *bitvm_client::UNSPENDABLE_XONLY_PUBKEY)
317 .unwrap();
318
319 assert_eq!(tree_info.script_map().len(), i as usize);
320 }
321 }
322
323 #[test]
324 fn test_calculate_taproot_leaf_depths() {
325 let expected: Vec<u8> = vec![];
327 assert_eq!(calculate_taproot_leaf_depths(0), expected);
328
329 assert_eq!(calculate_taproot_leaf_depths(1), vec![0]);
331
332 assert_eq!(calculate_taproot_leaf_depths(2), vec![1, 1]);
334
335 assert_eq!(calculate_taproot_leaf_depths(3), vec![2, 2, 1]);
338
339 assert_eq!(calculate_taproot_leaf_depths(4), vec![2, 2, 2, 2]);
341
342 assert_eq!(calculate_taproot_leaf_depths(5), vec![3, 3, 2, 2, 2]);
349
350 assert_eq!(
352 calculate_taproot_leaf_depths(8),
353 vec![3, 3, 3, 3, 3, 3, 3, 3]
354 );
355 }
356}