1use std::convert::TryFrom;
24use std::fmt;
25
26use bitcoin::blockdata::script::{self, PushBytes};
27use bitcoin::{self, PublicKey, ScriptBuf as BtcScript, Weight};
28use bitcoin_miniscript::descriptor::DescriptorType;
29use elements::secp256k1_zkp;
30
31use crate::descriptor::checksum::{self, verify_checksum};
32use crate::expression::{self, FromTree};
33use crate::extensions::{CovExtArgs, CovenantExt};
34use crate::policy::{semantic, Liftable};
35use crate::{
36 tweak_key, BtcDescriptor, BtcError, BtcFromTree, BtcLiftable, BtcPolicy, BtcSatisfier, BtcTree,
37 Descriptor, DescriptorPublicKey, Error, MiniscriptKey, ToPublicKey,
38};
39
40#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
43pub struct Pegin<Pk: MiniscriptKey> {
44 pub fed_desc: BtcDescriptor<PublicKey>,
46 pub elem_desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
50}
51
52impl<Pk: MiniscriptKey> Pegin<Pk> {
53 pub fn new(
55 fed_desc: BtcDescriptor<PublicKey>,
56 elem_desc: Descriptor<Pk, CovenantExt<CovExtArgs>>,
57 ) -> Self {
58 Self {
59 fed_desc,
60 elem_desc,
61 }
62 }
63}
64
65impl Pegin<DescriptorPublicKey> {
66 pub fn derived_descriptor<C: secp256k1_zkp::Verification>(
67 &self,
68 arg: u32,
69 secp: &secp256k1_zkp::Secp256k1<C>,
70 ) -> Result<Pegin<PublicKey>, Error> {
71 let elem_desc = self.elem_desc.at_derivation_index(arg)?;
72 let elem_desc = elem_desc.derived_descriptor(secp)?;
73 Ok(Pegin::new(self.fed_desc.clone(), elem_desc))
74 }
75}
76
77impl<Pk: MiniscriptKey> fmt::Debug for Pegin<Pk> {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "pegin({:?},{:?})", self.fed_desc, self.elem_desc)
80 }
81}
82
83impl<Pk: MiniscriptKey> fmt::Display for Pegin<Pk> {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 use fmt::Write;
86 let mut wrapped_f = checksum::Formatter::new(f);
87 write!(wrapped_f, "pegin({:#},{:#})", self.fed_desc, self.elem_desc)?;
88 wrapped_f.write_checksum_if_not_alt()
89 }
90}
91
92impl<Pk: MiniscriptKey> Liftable<PublicKey> for Pegin<Pk> {
93 fn lift(&self) -> Result<semantic::Policy<PublicKey>, Error> {
94 let btc_pol = BtcLiftable::lift(&self.fed_desc)?;
95 Liftable::lift(&btc_pol)
96 }
97}
98
99impl<Pk: MiniscriptKey> BtcLiftable<PublicKey> for Pegin<Pk> {
100 fn lift(&self) -> Result<BtcPolicy<PublicKey>, BtcError> {
101 self.fed_desc.lift()
102 }
103}
104
105impl_from_tree!(
106 Pegin<Pk>,
107 fn from_tree(top: &expression::Tree<'_>) -> Result<Self, Error> {
108 if top.name == "pegin" && top.args.len() == 2 {
109 let ms_str = top.args[0].to_string();
112 let ms_expr = BtcTree::from_str(&ms_str)?;
113 let fed_desc = BtcDescriptor::<PublicKey>::from_tree(&ms_expr)?;
118 let elem_desc = Descriptor::<Pk, CovenantExt<CovExtArgs>>::from_tree(&top.args[1])?;
119 Ok(Pegin::new(fed_desc, elem_desc))
120 } else {
121 Err(Error::Unexpected(format!(
122 "{}({} args) while parsing legacy_pegin descriptor",
123 top.name,
124 top.args.len(),
125 )))
126 }
127 }
128);
129
130impl_from_str!(
131 Pegin<Pk>,
132 type Err = Error;,
133 fn from_str(s: &str) -> Result<Self, Self::Err> {
134 let desc_str = verify_checksum(s)?;
135 let top = expression::Tree::from_str(desc_str)?;
136 Self::from_tree(&top)
137 }
138);
139
140impl<Pk: MiniscriptKey> Pegin<Pk> {
141 pub fn sanity_check(&self) -> Result<(), Error> {
143 self.fed_desc
144 .sanity_check()
145 .map_err(|_| Error::Unexpected("Federation script sanity check failed".to_string()))?;
146 self.elem_desc
147 .sanity_check()
148 .map_err(|_| Error::Unexpected("Federation script sanity check failed".to_string()))?;
149 Ok(())
150 }
151
152 pub fn bitcoin_address<C: secp256k1_zkp::Verification>(
155 &self,
156 network: bitcoin::Network,
157 secp: &secp256k1_zkp::Secp256k1<C>,
158 ) -> Result<bitcoin::Address, Error>
159 where
160 Pk: ToPublicKey,
161 {
162 match self.fed_desc.desc_type() {
163 DescriptorType::Wsh => Ok(bitcoin::Address::p2wsh(
164 &self
165 .bitcoin_witness_script(secp)
166 .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"),
167 network,
168 )),
169 DescriptorType::ShWsh => Ok(bitcoin::Address::p2shwsh(
170 &self
171 .bitcoin_witness_script(secp)
172 .expect("DO this cleanly after TR. Pay to taproot pegins unspecified till now"),
173 network,
174 )),
175 _ => Err(Error::UnsupportedAddressForPegin),
176 }
177 }
178
179 pub fn bitcoin_script_pubkey<C: secp256k1_zkp::Verification>(
182 &self,
183 secp: &secp256k1_zkp::Secp256k1<C>,
184 ) -> BtcScript
185 where
186 Pk: ToPublicKey,
187 {
188 self.bitcoin_address(bitcoin::Network::Bitcoin, secp)
189 .expect("Address cannot fail for pegin")
190 .script_pubkey()
191 }
192
193 pub fn bitcoin_unsigned_script_sig<C: secp256k1_zkp::Verification>(
203 &self,
204 secp: &secp256k1_zkp::Secp256k1<C>,
205 ) -> BtcScript
206 where
207 Pk: ToPublicKey,
208 {
209 let witness_script = self
210 .bitcoin_witness_script(secp)
211 .expect("TODO after taproot");
212 let push_bytes = <&PushBytes>::try_from(witness_script.as_bytes())
213 .expect("Witness script is not too larg");
214 script::Builder::new().push_slice(push_bytes).into_script()
215 }
216
217 pub fn bitcoin_witness_script<C: secp256k1_zkp::Verification>(
222 &self,
223 secp: &secp256k1_zkp::Secp256k1<C>,
224 ) -> Result<BtcScript, Error>
225 where
226 Pk: ToPublicKey,
227 {
228 let tweak_vec = self
229 .elem_desc
230 .explicit_script()
231 .expect("Tr pegins unknown yet")
232 .into_bytes();
233 bitcoin_witness_script(&self.fed_desc, &tweak_vec[..], secp)
234 }
235
236 pub fn get_bitcoin_satisfaction<S, C: secp256k1_zkp::Verification>(
240 &self,
241 secp: &secp256k1_zkp::Secp256k1<C>,
242 satisfier: S,
243 ) -> Result<(Vec<Vec<u8>>, BtcScript), Error>
244 where
245 S: BtcSatisfier<bitcoin::PublicKey>,
246 Pk: ToPublicKey,
247 {
248 let claim_script = self
249 .elem_desc
250 .explicit_script()
251 .expect("Tr pegins unknown yet")
252 .into_bytes();
253 let mut t = TranslateTweak(&claim_script[..], secp);
254
255 let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(&self.fed_desc, &mut t)
256 .expect("Tweaking must succeed");
257
258 let res = tweaked_desc.get_satisfaction(satisfier)?;
259 Ok(res)
260 }
261
262 pub fn max_satisfaction_weight(&self) -> Result<Weight, Error>
268 where
269 Pk: ToPublicKey,
270 {
271 let w = self.fed_desc.max_weight_to_satisfy()?;
273 Ok(w)
274 }
275
276 pub fn script_code<C: secp256k1_zkp::Verification>(
281 &self,
282 secp: &secp256k1_zkp::Secp256k1<C>,
283 ) -> Result<BtcScript, Error>
284 where
285 Pk: ToPublicKey,
286 {
287 self.bitcoin_witness_script(secp)
288 }
289
290 pub fn into_user_descriptor(self) -> Descriptor<Pk, CovenantExt<CovExtArgs>> {
295 self.elem_desc
296 }
297}
298
299fn bitcoin_witness_script<C: secp256k1_zkp::Verification, Pk: ToPublicKey>(
300 fed_desc: &BtcDescriptor<Pk>,
301 claim_script: &[u8],
302 secp: &secp256k1_zkp::Secp256k1<C>,
303) -> Result<BtcScript, Error> {
304 let mut t = TranslateTweak(claim_script, secp);
305
306 let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t)
307 .expect("Tweaking must succeed");
308 Ok(tweaked_desc.explicit_script()?)
309}
310
311struct TranslateTweak<'a, 'b, C: secp256k1_zkp::Verification>(
312 &'a [u8],
313 &'b secp256k1_zkp::Secp256k1<C>,
314);
315
316impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator<Pk, bitcoin::PublicKey, ()>
317 for TranslateTweak<'a, 'b, C>
318where
319 Pk: MiniscriptKey + ToPublicKey,
320 C: secp256k1_zkp::Verification,
321{
322 fn pk(&mut self, pk: &Pk) -> Result<bitcoin::PublicKey, ()> {
323 Ok(tweak_key(&pk.to_public_key(), self.1, self.0))
324 }
325
326 translate_hash_fail!(Pk, bitcoin::PublicKey, ());
329}
330
331#[cfg(test)]
332mod tests {
333 use bitcoin::hex::FromHex;
334 use bitcoin::PublicKey;
335
336 use crate::descriptor::pegin::Pegin;
337 use crate::{BtcDescriptor, ConfidentialDescriptor, DescriptorPublicKey};
338
339 fn fed_peg_desc() -> BtcDescriptor<PublicKey> {
340 let s = bitcoin::ScriptBuf::from_hex("5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68").unwrap();
341
342 type Segwitv0Script =
343 bitcoin_miniscript::Miniscript<bitcoin::PublicKey, bitcoin_miniscript::Segwitv0>;
344
345 let m = Segwitv0Script::parse(&s).unwrap();
346 assert_eq!(m.encode(), s);
347 BtcDescriptor::<_>::new_wsh(m).unwrap()
348 }
349
350 #[test]
365 fn test_pegin() {
366 let d = fed_peg_desc();
367
368 let fedpegdesc = "wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb))))#7jwwklk4";
369 assert_eq!(&d.to_string(), fedpegdesc);
370
371 let claimscript =
372 Vec::<u8>::from_hex("0014de8e299d5347503f7ee33247e780b7f412727623").unwrap();
373 let secp = secp256k1::Secp256k1::new();
374
375 let mainchain_address = "bc1qssx7ha3zxpq25l6uukphlwj3jumvmcv8qr3dy6uy8l8j4vwa5fhswpcw3p";
376
377 let s = super::bitcoin_witness_script(&d, &claimscript, &secp).unwrap();
378 let b = bitcoin::Address::p2wsh(&s, bitcoin::Network::Bitcoin);
379 assert_eq!(mainchain_address, b.to_string());
380
381 let elem_desc = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/0/*))";
382 let elem_desc: ConfidentialDescriptor<DescriptorPublicKey> = elem_desc.parse().unwrap();
383 let elem_desc = elem_desc.descriptor.at_derivation_index(0).unwrap();
384 let elem_desc = elem_desc.derived_descriptor(&secp).unwrap();
385 let pegin = Pegin::new(d.clone(), elem_desc);
386
387 assert_eq!(pegin.to_string(), "pegin(wsh(or_d(multi(11,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,02675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af99,02896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d48,029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c,02a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc4010,031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb,03079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b,03111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2,0318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa0840174,03230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de1,035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a6,03bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c,03cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d17546,03d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d424828,03ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a),and_v(v:older(4032),multi(2,03aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79,0291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807,0386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb)))),elwpkh(0321da398ca2ddc09be89caa26e6730ae84751b6ea3a1ca46aa365bb5e1c3d9620))#qp4fan9q");
388 }
389
390 #[test]
391 fn test_pegin_derive() {
392 let elem_desc = "ct(slip77(ab5824f4477b4ebb00a132adfd8eb0b7935cf24f6ac151add5d1913db374ce92),elwpkh([759db348/84'/1'/0']tpubDCRMaF33e44pcJj534LXVhFbHibPbJ5vuLhSSPFAw57kYURv4tzXFL6LSnd78bkjqdmE3USedkbpXJUPA1tdzKfuYSL7PianceqAhwL2UkA/0/*))";
393 let elem_desc: ConfidentialDescriptor<DescriptorPublicKey> = elem_desc.parse().unwrap();
394 let fed_peg_desc = fed_peg_desc();
395 let pegin = Pegin::new(fed_peg_desc, elem_desc.descriptor);
396 let secp = secp256k1::Secp256k1::new();
397
398 let address_0 = pegin
399 .derived_descriptor(0, &secp)
400 .unwrap()
401 .bitcoin_address(bitcoin::Network::Bitcoin, &secp)
402 .unwrap();
403 assert_eq!(
404 address_0.to_string(),
405 "bc1qqkq6czql4zqwsylgrfzttjrn5wjeqmwfq5yn80p39amxtnkng9lsn6c5qr"
406 );
407
408 let address_1 = pegin
409 .derived_descriptor(1, &secp)
410 .unwrap()
411 .bitcoin_address(bitcoin::Network::Bitcoin, &secp)
412 .unwrap();
413 assert_ne!(address_0, address_1);
414 assert_eq!(
415 address_1.to_string(),
416 "bc1qmevs3n40t394230lptclz55rmxkmr7dmnqhuflxf0cezdupsmvdsk25n3m"
417 );
418 }
419}