1use std::convert::TryFrom;
26use std::fmt;
27use std::str::FromStr;
28use std::sync::Arc;
29
30use bitcoin::blockdata::script::PushBytes;
31use bitcoin::blockdata::{opcodes, script};
32use bitcoin::hashes::{hash160, ripemd160, sha256, Hash};
33use bitcoin::{self, hashes, ScriptBuf as BtcScript};
34use bitcoin_miniscript::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
35use bitcoin_miniscript::TranslatePk as BtcTranslatePk;
36use elements::secp256k1_zkp;
37
38use crate::descriptor::checksum::{self, verify_checksum};
39use crate::expression::{self, FromTree};
40use crate::extensions::{CovExtArgs, CovenantExt};
41use crate::policy::{semantic, Liftable};
42use crate::util::varint_len;
43use crate::{
44 hash256, tweak_key, BtcError, BtcFromTree, BtcLiftable, BtcMiniscript, BtcPolicy, BtcSatisfier,
45 BtcSegwitv0, BtcTerminal, BtcTree, Descriptor, Error, MiniscriptKey, ToPublicKey,
46};
47
48#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub enum LegacyPeginKey {
51 Functionary(bitcoin::PublicKey),
53 NonFunctionary(bitcoin::PublicKey),
55}
56
57impl LegacyPeginKey {
58 pub fn as_untweaked(&self) -> &bitcoin::PublicKey {
60 match *self {
61 LegacyPeginKey::Functionary(ref pk) => pk,
62 LegacyPeginKey::NonFunctionary(ref pk) => pk,
63 }
64 }
65}
66
67impl FromStr for LegacyPeginKey {
70 type Err = Error;
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 if s.is_empty() {
74 Err(Error::BadDescriptor("Empty Legacy pegin".to_string()))
75 } else if &s[0..1] == "f" && s.len() == 67 {
76 Ok(LegacyPeginKey::Functionary(bitcoin::PublicKey::from_str(
77 &s[1..],
78 )?))
79 } else if &s[0..1] == "u" && s.len() == 67 {
80 Ok(LegacyPeginKey::NonFunctionary(
81 bitcoin::PublicKey::from_str(&s[1..])?,
82 ))
83 } else {
84 Err(Error::BadDescriptor(
85 "Invalid Legacy Pegin descriptor".to_string(),
86 ))
87 }
88 }
89}
90
91impl fmt::Display for LegacyPeginKey {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 match *self {
94 LegacyPeginKey::Functionary(ref pk) => write!(f, "f{}", pk),
95 LegacyPeginKey::NonFunctionary(ref pk) => write!(f, "u{}", pk),
96 }
97 }
98}
99
100impl MiniscriptKey for LegacyPeginKey {
101 type Sha256 = sha256::Hash;
102 type Hash256 = hash256::Hash;
103 type Ripemd160 = ripemd160::Hash;
104 type Hash160 = hash160::Hash;
105
106 fn is_uncompressed(&self) -> bool {
107 false
108 }
109
110 fn num_der_paths(&self) -> usize {
111 0
112 }
113}
114
115#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
117pub struct LegacyPegin<Pk: MiniscriptKey> {
118 fed: bitcoin_miniscript::Threshold<LegacyPeginKey, MAX_PUBKEYS_PER_MULTISIG>,
120
121 emer: bitcoin_miniscript::Threshold<LegacyPeginKey, MAX_PUBKEYS_PER_MULTISIG>,
123
124 timelock: bitcoin_miniscript::RelLockTime,
126
127 pub desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
131 ms: BtcMiniscript<LegacyPeginKey, BtcSegwitv0>,
134}
135
136impl<Pk: MiniscriptKey> LegacyPegin<Pk> {
137 pub fn new(
139 fed: bitcoin_miniscript::Threshold<LegacyPeginKey, MAX_PUBKEYS_PER_MULTISIG>,
140 emer: bitcoin_miniscript::Threshold<LegacyPeginKey, MAX_PUBKEYS_PER_MULTISIG>,
141 timelock: bitcoin_miniscript::RelLockTime,
142 desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
143 ) -> Self {
144 let fed_ms = BtcMiniscript::from_ast(BtcTerminal::Multi(fed.clone()))
145 .expect("Multi type check can't fail");
146 let csv = BtcMiniscript::from_ast(BtcTerminal::Verify(Arc::new(
147 BtcMiniscript::from_ast(BtcTerminal::Older(timelock)).unwrap(),
148 )))
149 .unwrap();
150 let emer_ms = BtcMiniscript::from_ast(BtcTerminal::Multi(emer.clone()))
151 .expect("Multi type check can't fail");
152 let emer_ms =
153 BtcMiniscript::from_ast(BtcTerminal::AndV(Arc::new(csv), Arc::new(emer_ms))).unwrap();
154 let ms = BtcMiniscript::from_ast(BtcTerminal::OrD(Arc::new(fed_ms), Arc::new(emer_ms)))
155 .expect("Type check");
156 Self {
157 fed,
158 emer,
159 timelock,
160 desc,
161 ms,
162 }
163 }
164
165 fn from_ms_and_desc(
168 desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
169 ms: BtcMiniscript<LegacyPeginKey, BtcSegwitv0>,
170 ) -> Self {
171 let ms_clone = ms.clone();
174 let (fed, right) = if let BtcTerminal::OrD(ref a, ref b) = ms_clone.node {
175 if let (BtcTerminal::Multi(t), right) = (&a.node, &b.node) {
176 (t.clone(), right)
177 } else {
178 unreachable!("Only valid pegin miniscripts");
179 }
180 } else {
181 unreachable!("Only valid pegin miniscripts");
182 };
183 let (timelock, emer) = if let BtcTerminal::AndV(l, r) = right {
184 if let (BtcTerminal::Verify(csv), BtcTerminal::Multi(t)) = (&l.node, &r.node) {
185 if let BtcTerminal::Older(timelock) = csv.node {
186 (timelock, t.clone())
187 } else {
188 unreachable!("Only valid pegin miniscripts");
189 }
190 } else {
191 unreachable!("Only valid pegin miniscripts");
192 }
193 } else {
194 unreachable!("Only valid pegin miniscripts");
195 };
196 Self {
197 fed,
198 emer,
199 timelock,
200 desc,
201 ms,
202 }
203 }
204
205 fn explicit_script<C: secp256k1_zkp::Verification>(
206 &self,
207 secp: &secp256k1_zkp::Secp256k1<C>,
208 ) -> BtcScript
209 where
210 Pk: ToPublicKey,
211 {
212 let tweak_vec = self.desc.explicit_script().expect("Tr pegins").into_bytes();
213 let tweak = hashes::sha256::Hash::hash(&tweak_vec);
214 let mut builder = script::Builder::new()
216 .push_opcode(opcodes::all::OP_DEPTH)
217 .push_int(self.fed.k() as i64 + 1)
218 .push_opcode(opcodes::all::OP_EQUAL)
219 .push_opcode(opcodes::all::OP_IF)
220 .push_int(self.fed.k() as i64);
222
223 for key in self.fed.iter() {
224 let tweaked_pk = tweak_key(key.as_untweaked(), secp, tweak.as_byte_array());
225 builder = builder.push_key(&tweaked_pk);
226 }
227 let mut nearly_done = builder
228 .push_int(self.fed.n() as i64)
229 .push_opcode(opcodes::all::OP_ELSE)
230 .into_script()
231 .to_bytes();
232
233 let right = if let BtcTerminal::OrD(_l, right) = &self.ms.node {
234 right
235 } else {
236 unreachable!("Only valid pegin descriptors should be created inside LegacyPegin")
237 };
238 struct TranslateUnTweak;
239
240 impl bitcoin_miniscript::Translator<LegacyPeginKey, bitcoin::PublicKey, ()> for TranslateUnTweak {
241 fn pk(&mut self, pk: &LegacyPeginKey) -> Result<bitcoin::PublicKey, ()> {
242 Ok(*pk.as_untweaked())
243 }
244
245 bitcoin_miniscript::translate_hash_clone!(LegacyPeginKey, bitcoin::PublicKey, ());
246 }
247 let mut t = TranslateUnTweak;
248
249 let right = right
250 .translate_pk(&mut t)
251 .expect("Translation must succeed");
252 let mut rser = right.encode().into_bytes();
253 assert_eq!(rser[4], opcodes::all::OP_VERIFY.to_u8());
256 rser[4] = opcodes::all::OP_DROP.to_u8();
257 nearly_done.extend(rser);
261
262 let insert_point = nearly_done.len() - 1;
263 nearly_done.insert(insert_point, 0x68);
264 BtcScript::from(nearly_done)
265 }
266
267 pub fn new_legacy_fed(user_desc: Descriptor<Pk, CovenantExt<CovExtArgs>>) -> Self {
270 let pks = "
273 020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,
274 02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,
275 02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,
276 029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,
277 02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,
278 02f8a00b269f8c5e59c67d36db3cdc11b11b21f64b4bffb2815e9100d9aa8daf07,
279 03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,
280 03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,
281 0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,
282 03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,
283 035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,
284 03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,
285 03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,
286 03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,
287 03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a";
288 let fed_pks: Vec<LegacyPeginKey> = pks
289 .split(',')
290 .map(|pk| LegacyPeginKey::Functionary(bitcoin::PublicKey::from_str(pk).unwrap()))
291 .collect();
292
293 let fed = bitcoin_miniscript::Threshold::new(22, fed_pks).expect("statically defined");
294
295 let emer_pks = "
296 03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,
297 0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,
298 0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb";
299 let emer_pks: Vec<LegacyPeginKey> = emer_pks
300 .split(',')
301 .map(|pk| LegacyPeginKey::Functionary(bitcoin::PublicKey::from_str(pk).unwrap()))
302 .collect();
303
304 let emer = bitcoin_miniscript::Threshold::new(2, emer_pks).expect("statically defined");
305
306 Self::new(
307 fed,
308 emer,
309 bitcoin_miniscript::RelLockTime::from_consensus(4032).expect("statically defined"),
310 user_desc,
311 )
312 }
313}
314
315impl<Pk: MiniscriptKey> fmt::Debug for LegacyPegin<Pk> {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 write!(f, "legacy_pegin({:?},{:?})", self.ms, self.desc)
318 }
319}
320
321impl<Pk: MiniscriptKey> fmt::Display for LegacyPegin<Pk> {
322 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
323 use fmt::Write;
324 let mut wrapped_f = checksum::Formatter::new(f);
325 write!(wrapped_f, "legacy_pegin({},{})", self.ms, self.desc)?;
326 wrapped_f.write_checksum_if_not_alt()
327 }
328}
329
330impl<Pk: MiniscriptKey> Liftable<LegacyPeginKey> for LegacyPegin<Pk> {
331 fn lift(&self) -> Result<semantic::Policy<LegacyPeginKey>, Error> {
332 let btc_pol = BtcLiftable::lift(&self.ms)?;
333 Liftable::lift(&btc_pol)
334 }
335}
336
337impl<Pk: MiniscriptKey> BtcLiftable<LegacyPeginKey> for LegacyPegin<Pk> {
338 fn lift(&self) -> Result<BtcPolicy<LegacyPeginKey>, BtcError> {
339 self.ms.lift()
340 }
341}
342
343impl_from_tree!(
344 LegacyPegin<Pk>,
345 fn from_tree(top: &expression::Tree<'_>) -> Result<Self, Error> {
346 if top.name == "legacy_pegin" && top.args.len() == 2 {
347 let ms_str = top.args[0].to_string();
350 let ms_expr = BtcTree::from_str(&ms_str)?;
351 let ms = BtcMiniscript::<LegacyPeginKey, BtcSegwitv0>::from_tree(&ms_expr);
353 let desc = Descriptor::<Pk, CovenantExt<CovExtArgs>>::from_tree(&top.args[1]);
354 Ok(LegacyPegin::from_ms_and_desc(desc?, ms?))
355 } else {
356 Err(Error::Unexpected(format!(
357 "{}({} args) while parsing legacy_pegin descriptor",
358 top.name,
359 top.args.len(),
360 )))
361 }
362 }
363);
364
365impl_from_str!(
366 LegacyPegin<Pk>,
367 type Err = Error;,
368 fn from_str(s: &str) -> Result<Self, Self::Err> {
369 let desc_str = verify_checksum(s)?;
370 let top = expression::Tree::from_str(desc_str)?;
371 Self::from_tree(&top)
372 }
373);
374
375impl<Pk: MiniscriptKey> LegacyPegin<Pk> {
376 pub fn sanity_check(&self) -> Result<(), Error> {
380 self.ms
381 .sanity_check()
382 .map_err(|_| Error::Unexpected("Federation script sanity check failed".to_string()))?;
383 self.desc
384 .sanity_check()
385 .map_err(|_| Error::Unexpected("Federation script sanity check failed".to_string()))?;
386 Ok(())
387 }
388
389 pub fn bitcoin_address<C: secp256k1_zkp::Verification>(
392 &self,
393 network: bitcoin::Network,
394 secp: &secp256k1_zkp::Secp256k1<C>,
395 ) -> Result<bitcoin::Address, Error>
396 where
397 Pk: ToPublicKey,
398 {
399 Ok(bitcoin::Address::p2shwsh(
400 &self.explicit_script(secp),
401 network,
402 ))
403 }
404
405 pub fn bitcoin_script_pubkey<C: secp256k1_zkp::Verification>(
408 &self,
409 secp: &secp256k1_zkp::Secp256k1<C>,
410 ) -> BtcScript
411 where
412 Pk: ToPublicKey,
413 {
414 self.bitcoin_address(bitcoin::Network::Bitcoin, secp)
415 .expect("Address cannot fail for pegin")
416 .script_pubkey()
417 }
418
419 pub fn bitcoin_unsigned_script_sig<C: secp256k1_zkp::Verification>(
429 &self,
430 secp: &secp256k1_zkp::Secp256k1<C>,
431 ) -> BtcScript
432 where
433 Pk: ToPublicKey,
434 {
435 let witness_script = self.explicit_script(secp);
436 let push_bytes = <&PushBytes>::try_from(witness_script.as_bytes())
437 .expect("Witness script is not too larg");
438 script::Builder::new().push_slice(push_bytes).into_script()
439 }
440 pub fn bitcoin_witness_script<C: secp256k1_zkp::Verification>(
445 &self,
446 secp: &secp256k1_zkp::Secp256k1<C>,
447 ) -> Result<BtcScript, Error>
448 where
449 Pk: ToPublicKey,
450 {
451 Ok(self.explicit_script(secp))
452 }
453
454 pub fn get_bitcoin_satisfaction<S, C: secp256k1_zkp::Verification>(
458 &self,
459 secp: &secp256k1_zkp::Secp256k1<C>,
460 satisfier: S,
461 ) -> Result<(Vec<Vec<u8>>, BtcScript), Error>
462 where
463 S: BtcSatisfier<bitcoin::PublicKey>,
464 Pk: ToPublicKey,
465 {
466 let tweak_vec = self.desc.explicit_script().expect("Tr pegins").into_bytes();
467 let tweak = hashes::sha256::Hash::hash(&tweak_vec);
468 let unsigned_script_sig = self.bitcoin_unsigned_script_sig(secp);
469 let mut sigs = vec![];
470 for key in self.fed.iter() {
471 let tweaked_pk = tweak_key(key.as_untweaked(), secp, tweak.as_byte_array());
472 if let Some(sig) = satisfier.lookup_ecdsa_sig(&tweaked_pk) {
473 sigs.push(sig.to_vec());
474 }
475 }
476 sigs.sort_by_key(|a| a.len());
477 if sigs.len() >= self.fed.k() {
478 let mut sigs: Vec<Vec<u8>> = sigs.into_iter().take(self.fed.k()).collect();
480 sigs.push(vec![0]); Ok((sigs, unsigned_script_sig))
482 } else {
483 let mut emer_sigs = vec![];
484 for emer_key in self.emer.iter() {
485 if let Some(sig) = satisfier.lookup_ecdsa_sig(emer_key.as_untweaked()) {
486 emer_sigs.push(sig.to_vec());
487 }
488 }
489 emer_sigs.sort_by_key(|a| a.len());
490 if emer_sigs.len() >= self.emer.k() {
491 let mut sigs: Vec<Vec<u8>> = emer_sigs.into_iter().take(self.emer.k()).collect();
492 sigs.push(vec![0]); Ok((sigs, unsigned_script_sig))
494 } else {
495 Err(Error::CouldNotSatisfy)
496 }
497 }
498 }
499
500 pub fn max_satisfaction_weight(&self) -> Result<usize, Error> {
505 let script_size = 628;
506 Ok(4 * 36
507 + varint_len(script_size)
508 + script_size
509 + varint_len(self.ms.max_satisfaction_witness_elements()?)
510 + self.ms.max_satisfaction_size()?)
511 }
512
513 pub fn script_code<C: secp256k1_zkp::Verification>(
518 &self,
519 secp: &secp256k1_zkp::Secp256k1<C>,
520 ) -> Result<BtcScript, Error>
521 where
522 Pk: ToPublicKey,
523 {
524 self.bitcoin_witness_script(secp)
525 }
526
527 pub fn into_user_descriptor(self) -> Descriptor<Pk, CovenantExt<CovExtArgs>> {
532 self.desc
533 }
534}