From 909b1809de15c0c2a4217ee1756319dd34c5a50b Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 6 Aug 2024 10:35:45 +0200 Subject: [PATCH 1/7] finish implement bitcoin_witness_script in dynafed pegin --- src/descriptor/pegin/dynafed_pegin.rs | 51 ++++++++++++--------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 11804203..f5be650d 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -33,7 +33,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 +198,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 +208,31 @@ impl Pegin { .explicit_script() .expect("Tr pegins unknown yet") .into_bytes(); - let _tweak = hashes::sha256::Hash::hash(&tweak_vec); + 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, + ); - // 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()) - // } + impl<'a, Pk, C> bitcoin_miniscript::Translator for TranslateTweak<'a, 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[..])) + } - // fn pkh( - // &mut self, - // pkh: &::Hash, - // ) -> Result<::Hash, ()> { - // unreachable!("No keyhashes in elements descriptors") - // } - // } - // let mut t = TranslateTweak(tweak, secp); + // 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, ()); + } + let mut t = TranslateTweak(tweak, secp); - // let tweaked_desc = ::translate_pk(&self.fed_desc, t).expect("Tweaking must succeed"), - // Ok(tweaked_desc.explicit_script()?) + let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(&self.fed_desc, &mut t) + .expect("Tweaking must succeed"); + Ok(tweaked_desc.explicit_script()?) } /// Returns satisfying witness and scriptSig to spend an From 9d87b501606309e5667449788ff93e1812fb2b64 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Tue, 6 Aug 2024 11:40:03 +0200 Subject: [PATCH 2/7] refactor out fn as free fn --- src/descriptor/pegin/dynafed_pegin.rs | 58 +++++++++++++++------------ 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index f5be650d..9ce7b858 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -208,31 +208,7 @@ impl Pegin { .explicit_script() .expect("Tr pegins unknown yet") .into_bytes(); - let tweak = hashes::sha256::Hash::hash(&tweak_vec); - - struct TranslateTweak<'a, C: secp256k1_zkp::Verification>( - hashes::sha256::Hash, - &'a secp256k1_zkp::Secp256k1, - ); - - impl<'a, Pk, C> bitcoin_miniscript::Translator for TranslateTweak<'a, 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, ()); - } - let mut t = TranslateTweak(tweak, secp); - - let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(&self.fed_desc, &mut 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 @@ -298,3 +274,35 @@ impl Pegin { self.elem_desc } } + +fn bitcoin_witness_script( + fed_desc: &BtcDescriptor, + claim_script: &[u8], + secp: &secp256k1_zkp::Secp256k1, +) -> Result { + let tweak = hashes::sha256::Hash::hash(&claim_script); + + struct TranslateTweak<'a, C: secp256k1_zkp::Verification>( + hashes::sha256::Hash, + &'a secp256k1_zkp::Secp256k1, + ); + + impl<'a, Pk, C> bitcoin_miniscript::Translator for TranslateTweak<'a, 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, ()); + } + let mut t = TranslateTweak(tweak, secp); + + let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t) + .expect("Tweaking must succeed"); + Ok(tweaked_desc.explicit_script()?) +} From 97eb5cadbee20c595324fa98215748d19353935b Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 7 Aug 2024 15:36:50 +0200 Subject: [PATCH 3/7] remove the sha256 step in key tweak (will hmac later) --- src/descriptor/pegin/dynafed_pegin.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 9ce7b858..847a10be 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -280,14 +280,13 @@ fn bitcoin_witness_script( claim_script: &[u8], secp: &secp256k1_zkp::Secp256k1, ) -> Result { - let tweak = hashes::sha256::Hash::hash(&claim_script); - - struct TranslateTweak<'a, C: secp256k1_zkp::Verification>( - hashes::sha256::Hash, - &'a secp256k1_zkp::Secp256k1, + struct TranslateTweak<'a, 'b, C: secp256k1_zkp::Verification>( + &'a [u8], + &'b secp256k1_zkp::Secp256k1, ); - impl<'a, Pk, C> bitcoin_miniscript::Translator for TranslateTweak<'a, C> + impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator + for TranslateTweak<'a, 'b, C> where Pk: MiniscriptKey + ToPublicKey, C: secp256k1_zkp::Verification, @@ -300,7 +299,7 @@ fn bitcoin_witness_script( // Fail if we encounter any hash fragments. See also translate_hash_clone! macro. translate_hash_fail!(Pk, bitcoin::PublicKey, ()); } - let mut t = TranslateTweak(tweak, secp); + let mut t = TranslateTweak(claim_script, secp); let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t) .expect("Tweaking must succeed"); From cfe6306332482f067469bffbb34a0bccaf0e0f5d Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 7 Aug 2024 15:37:22 +0200 Subject: [PATCH 4/7] add test_pegin to match with elements core --- src/descriptor/pegin/dynafed_pegin.rs | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 847a10be..399a2c9a 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -305,3 +305,51 @@ fn bitcoin_witness_script( .expect("Tweaking must succeed"); Ok(tweaked_desc.explicit_script()?) } + +#[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()); + } +} From 81b25cdbf67edd8dbaa9ba06fe3eeeb316d0b9ef Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 7 Aug 2024 15:46:30 +0200 Subject: [PATCH 5/7] move only: move TranslateTweak out so that is usable in other fn --- src/descriptor/pegin/dynafed_pegin.rs | 39 ++++++++++++++------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index 399a2c9a..a051081f 100644 --- a/src/descriptor/pegin/dynafed_pegin.rs +++ b/src/descriptor/pegin/dynafed_pegin.rs @@ -280,25 +280,6 @@ fn bitcoin_witness_script( claim_script: &[u8], secp: &secp256k1_zkp::Secp256k1, ) -> Result { - 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, ()); - } let mut t = TranslateTweak(claim_script, secp); let tweaked_desc = bitcoin_miniscript::TranslatePk::translate_pk(fed_desc, &mut t) @@ -306,6 +287,26 @@ fn bitcoin_witness_script( 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; From d79a2cf700441f5f09b86876f66df2283631621d Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 7 Aug 2024 15:51:22 +0200 Subject: [PATCH 6/7] finish to implement get_bitcoin_satisfaction --- src/descriptor/pegin/dynafed_pegin.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/descriptor/pegin/dynafed_pegin.rs b/src/descriptor/pegin/dynafed_pegin.rs index a051081f..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}; @@ -216,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 From 931696f5199a3b9f16eca620bbcbfd2c32cc8502 Mon Sep 17 00:00:00 2001 From: Riccardo Casatta Date: Wed, 7 Aug 2024 17:22:50 +0200 Subject: [PATCH 7/7] publicly expose Btc types pegin handling code needs to touch bitcoin and elements types at the same time so it's handy having this exposed --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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, };