diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 11804203..002cfd21 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -24,8 +24,7 @@ use std::convert::TryFrom; use std::fmt; use bitcoin::blockdata::script::{self, PushBytes}; -use bitcoin::hashes::Hash; -use bitcoin::{self, hashes, ScriptBuf as BtcScript}; +use bitcoin::{self, ScriptBuf as BtcScript}; use elements::secp256k1_zkp; use crate::descriptor::checksum::{desc_checksum, verify_checksum}; @@ -33,7 +32,7 @@ use crate::expression::{self, FromTree}; use crate::extensions::{CovExtArgs, CovenantExt}; use crate::policy::{semantic, Liftable}; use crate::{ - BtcDescriptor, BtcError, BtcFromTree, BtcLiftable, BtcPolicy, BtcSatisfier, BtcTree, + tweak_key, BtcDescriptor, BtcError, BtcFromTree, BtcLiftable, BtcPolicy, BtcSatisfier, BtcTree, Descriptor, Error, MiniscriptKey, ToPublicKey, }; @@ -198,7 +197,7 @@ impl Pegin { /// for the others it is the witness script. pub fn bitcoin_witness_script( &self, - _secp: &secp256k1_zkp::Secp256k1, + secp: &secp256k1_zkp::Secp256k1, ) -> Result where Pk: ToPublicKey, @@ -208,36 +207,7 @@ impl Pegin { .explicit_script() .expect("Tr pegins unknown yet") .into_bytes(); - let _tweak = hashes::sha256::Hash::hash(&tweak_vec); - - unreachable!("TODO: After upstream Refactor for Translator trait") - // let derived = self.fed_desc.derive - - // struct TranslateTweak<'a, C: secp256k1_zkp::Verification>( - // hashes::sha256::Hash, - // &'a secp256k1_zkp::Secp256k1, - // ); - - // impl<'a, Pk, C> PkTranslator for TranslateTweak<'a, C> - // where - // Pk: MiniscriptKey, - // C: secp256k1_zkp::Verification, - // { - // fn pk(&mut self, pk: &Pk) -> Result { - // tweak_key(pk, self.1, self.0.as_inner()) - // } - - // fn pkh( - // &mut self, - // pkh: &::Hash, - // ) -> Result<::Hash, ()> { - // unreachable!("No keyhashes in elements descriptors") - // } - // } - // let mut t = TranslateTweak(tweak, secp); - - // let tweaked_desc = ::translate_pk(&self.fed_desc, t).expect("Tweaking must succeed"), - // Ok(tweaked_desc.explicit_script()?) + bitcoin_witness_script(&self.fed_desc, &tweak_vec[..], secp) } /// Returns satisfying witness and scriptSig to spend an @@ -245,26 +215,25 @@ impl Pegin { /// construct one using the satisfier S. pub fn get_bitcoin_satisfaction( &self, - _secp: &secp256k1_zkp::Secp256k1, - _satisfier: S, + secp: &secp256k1_zkp::Secp256k1, + satisfier: S, ) -> Result<(Vec>, BtcScript), Error> where S: BtcSatisfier, Pk: ToPublicKey, { - let tweak_vec = self + let claim_script = self .elem_desc .explicit_script() .expect("Tr pegins unknown yet") .into_bytes(); - let _tweak = hashes::sha256::Hash::hash(&tweak_vec); - unreachable!("TODO: After upstream refactor"); - // let tweaked_desc = self.fed_desc.translate_pk_infallible( - // |pk| tweak_key(pk, secp, tweak.as_inner()), - // |_| unreachable!("No keyhashes in elements descriptors"), - // ); - // let res = tweaked_desc.get_satisfaction(satisfier)?; - // Ok(res) + let mut t = TranslateTweak(&claim_script[..], secp); + + let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(&self.fed_desc, &mut t) + .expect("Tweaking must succeed"); + + let res = tweaked_desc.get_satisfaction(satisfier)?; + Ok(res) } /// Computes an upper bound on the weight of a satisfying witness to the @@ -303,3 +272,83 @@ impl Pegin { self.elem_desc } } + +fn bitcoin_witness_script( + fed_desc: &BtcDescriptor, + claim_script: &[u8], + secp: &secp256k1_zkp::Secp256k1, +) -> Result { + let mut t = TranslateTweak(claim_script, secp); + + let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t) + .expect("Tweaking must succeed"); + Ok(tweaked_desc.explicit_script()?) +} + +struct TranslateTweak<'a, 'b, C: secp256k1_zkp::Verification>( + &'a [u8], + &'b secp256k1_zkp::Secp256k1, +); + +impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator + for TranslateTweak<'a, 'b, C> +where + Pk: MiniscriptKey + ToPublicKey, + C: secp256k1_zkp::Verification, +{ + fn pk(&mut self, pk: &Pk) -> Result { + Ok(tweak_key(&pk.to_public_key(), self.1, &self.0[..])) + } + + // We don't need to implement these methods as we are not using them in the policy. + // Fail if we encounter any hash fragments. See also translate_hash_clone! macro. + translate_hash_fail!(Pk, bitcoin::PublicKey, ()); +} + +#[cfg(test)] +mod tests { + use elements::hex::FromHex; + + use crate::BtcDescriptor; + + // test vector created with: + // ``` + // $ elements-cli getnetworkinfo | jq .version + // 230201 + // $ elements-cli getblockchaininfo | jq .blocks + // 2976078 + // elements-cli getsidechaininfo | jq '.current_fedpegscripts[0]'` + // "5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68" + // $ elements-cli getpeginaddress + // { + // "mainchain_address": "bc1qyya0twwz58kgfslpdgsygeq0r4nngl9tkt89v6phk8nqrwyenwrq5h0dk8", + // "claim_script": "0014a15906e643f2c9635527ab8658d370e8eaf149b5" + // } + // ``` + #[test] + fn test_pegin() { + let fedpegscript ="5b21020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b678172612102675333a4e4b8fb51d9d4e22fa5a8eaced3fdac8a8cbf9be8c030f75712e6af992102896807d54bc55c24981f24a453c60ad3e8993d693732288068a23df3d9f50d4821029e51a5ef5db3137051de8323b001749932f2ff0d34c82e96a2c2461de96ae56c2102a4e1a9638d46923272c266631d94d36bdb03a64ee0e14c7518e49d2f29bc401021031c41fdbcebe17bec8d49816e00ca1b5ac34766b91c9f2ac37d39c63e5e008afb2103079e252e85abffd3c401a69b087e590a9b86f33f574f08129ccbd3521ecf516b2103111cf405b627e22135b3b3733a4a34aa5723fb0f58379a16d32861bf576b0ec2210318f331b3e5d38156da6633b31929c5b220349859cc9ca3d33fb4e68aa08401742103230dae6b4ac93480aeab26d000841298e3b8f6157028e47b0897c1e025165de121035abff4281ff00660f99ab27bb53e6b33689c2cd8dcd364bc3c90ca5aea0d71a62103bd45cddfacf2083b14310ae4a84e25de61e451637346325222747b157446614c2103cc297026b06c71cbfa52089149157b5ff23de027ac5ab781800a578192d175462103d3bde5d63bdb3a6379b461be64dad45eabff42f758543a9645afd42f6d4248282103ed1e8d5109c9ed66f7941bc53cc71137baa76d50d274bda8d5e8ffbd6e61fe9a5fae736402c00fb269522103aab896d53a8e7d6433137bbba940f9c521e085dd07e60994579b64a6d992cf79210291b7d0b1b692f8f524516ed950872e5da10fb1b808b5a526dedc6fed1cf29807210386aa9372fbab374593466bc5451dc59954e90787f08060964d95c87ef34ca5bb53ae68"; + let s = bitcoin::ScriptBuf::from_hex(fedpegscript).unwrap(); + + type Segwitv0Script = + bitcoin_miniscript::Miniscript; + + let m = Segwitv0Script::parse(&s).unwrap(); + assert_eq!(m.encode(), s); + + let d = BtcDescriptor::<_>::new_wsh(m).unwrap(); + + 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"; + assert_eq!(&d.to_string(), fedpegdesc); + + let claimscript = + Vec::::from_hex("0014de8e299d5347503f7ee33247e780b7f412727623").unwrap(); + let secp = secp256k1::Secp256k1::new(); + + let mainchain_address = "bc1qssx7ha3zxpq25l6uukphlwj3jumvmcv8qr3dy6uy8l8j4vwa5fhswpcw3p"; + + let s = super::bitcoin_witness_script(&d, &claimscript, &secp).unwrap(); + let b = bitcoin::Address::p2wsh(&s, bitcoin::Network::Bitcoin); + assert_eq!(mainchain_address, b.to_string()); + } +} diff --git a/src/lib.rs b/src/lib.rs index a419669c..a1119898 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -113,7 +113,7 @@ use bitcoin_miniscript::policy::semantic::Policy as BtcPolicy; use bitcoin_miniscript::policy::Liftable as BtcLiftable; // re-export imports pub use bitcoin_miniscript::{hash256, ForEachKey, MiniscriptKey, SigType, ToPublicKey}; -use bitcoin_miniscript::{ +pub use bitcoin_miniscript::{ Descriptor as BtcDescriptor, Error as BtcError, Miniscript as BtcMiniscript, Satisfier as BtcSatisfier, Segwitv0 as BtcSegwitv0, Terminal as BtcTerminal, };