Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement federation descriptor tweak with claiming script to match elements core getpeginaddress #87

Merged
merged 7 commits into from
Aug 7, 2024
Merged
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
Loading