Skip to content

Commit

Permalink
Merge #87: Implement federation descriptor tweak with claiming script…
Browse files Browse the repository at this point in the history
… to match elements core `getpeginaddress`

931696f publicly expose Btc types (Riccardo Casatta)
d79a2cf finish to implement get_bitcoin_satisfaction (Riccardo Casatta)
81b25cd move only: move TranslateTweak out so that is usable in other fn (Riccardo Casatta)
cfe6306 add test_pegin to match with elements core (Riccardo Casatta)
97eb5ca remove the sha256 step in key tweak (will hmac<sha256> later) (Riccardo Casatta)
9d87b50 refactor out fn as free fn (Riccardo Casatta)
909b180 finish implement bitcoin_witness_script in dynafed pegin (Riccardo Casatta)

Pull request description:

  This will be  needed by lwk with a claim script generated by the lwk wallet so it will be able to claim pegins by its own

ACKs for top commit:
  apoelstra:
    ACK 931696f successfully ran local tests

Tree-SHA512: 6b0d2f8dd31934d2dc96786193632917e6be5d73eb66845050a8871ef507d08d7d51444dafe224d918021752e0e6753c39ebbb190b1a0c9578429f8cc380f690
  • Loading branch information
apoelstra committed Aug 7, 2024
2 parents 9425f2e + 931696f commit 9898534
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 46 deletions.
139 changes: 94 additions & 45 deletions src/descriptor/pegin/dynafed_pegin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@ 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};
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,
};

Expand Down Expand Up @@ -198,7 +197,7 @@ impl<Pk: MiniscriptKey> Pegin<Pk> {
/// for the others it is the witness script.
pub fn bitcoin_witness_script<C: secp256k1_zkp::Verification>(
&self,
_secp: &secp256k1_zkp::Secp256k1<C>,
secp: &secp256k1_zkp::Secp256k1<C>,
) -> Result<BtcScript, Error>
where
Pk: ToPublicKey,
Expand All @@ -208,63 +207,33 @@ impl<Pk: MiniscriptKey> Pegin<Pk> {
.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<C>,
// );

// impl<'a, Pk, C> PkTranslator<Pk, bitcoin::PublicKey, ()> for TranslateTweak<'a, C>
// where
// Pk: MiniscriptKey,
// C: secp256k1_zkp::Verification,
// {
// fn pk(&mut self, pk: &Pk) -> Result<bitcoin::PublicKey, ()> {
// tweak_key(pk, self.1, self.0.as_inner())
// }

// fn pkh(
// &mut self,
// pkh: &<Pk as MiniscriptKey>::Hash,
// ) -> Result<<bitcoin::PublicKey as MiniscriptKey>::Hash, ()> {
// unreachable!("No keyhashes in elements descriptors")
// }
// }
// let mut t = TranslateTweak(tweak, secp);

// let tweaked_desc = <bitcoin_miniscript::TranslatePk>::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
/// output controlled by the given descriptor if it possible to
/// construct one using the satisfier S.
pub fn get_bitcoin_satisfaction<S, C: secp256k1_zkp::Verification>(
&self,
_secp: &secp256k1_zkp::Secp256k1<C>,
_satisfier: S,
secp: &secp256k1_zkp::Secp256k1<C>,
satisfier: S,
) -> Result<(Vec<Vec<u8>>, BtcScript), Error>
where
S: BtcSatisfier<bitcoin::PublicKey>,
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
Expand Down Expand Up @@ -303,3 +272,83 @@ impl<Pk: MiniscriptKey> Pegin<Pk> {
self.elem_desc
}
}

fn bitcoin_witness_script<C: secp256k1_zkp::Verification, Pk: ToPublicKey>(
fed_desc: &BtcDescriptor<Pk>,
claim_script: &[u8],
secp: &secp256k1_zkp::Secp256k1<C>,
) -> Result<BtcScript, Error> {
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<C>,
);

impl<'a, 'b, Pk, C> bitcoin_miniscript::Translator<Pk, bitcoin::PublicKey, ()>
for TranslateTweak<'a, 'b, C>
where
Pk: MiniscriptKey + ToPublicKey,
C: secp256k1_zkp::Verification,
{
fn pk(&mut self, pk: &Pk) -> Result<bitcoin::PublicKey, ()> {
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<bitcoin::PublicKey, bitcoin_miniscript::Segwitv0>;

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::<u8>::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());
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down

0 comments on commit 9898534

Please # to comment.