Skip to content

Instantly share code, notes, and snippets.

@sanket1729
Created December 28, 2020 18:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sanket1729/32c23c9a8023f4327ed92f47c84975a7 to your computer and use it in GitHub Desktop.
Save sanket1729/32c23c9a8023f4327ed92f47c84975a7 to your computer and use it in GitHub Desktop.
// Miniscript
// Written in 2018 by
// Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! Pegin Descriptor Support
//!
//! Traits and implementations for Pegin descriptors
//! Note that this is a bitcoin descriptor and thus cannot be
//! added to elements Descriptor. Upstream rust-miniscript does
//! has a Descriptor enum which should ideally have it, but
//! bitcoin descriptors cannot depend on elements descriptors
//! Thus, as a simple solution we implement these as a separate
//! struct with it's own API.
use bitcoin::hashes::Hash;
use bitcoin::{self, blockdata::script, hashes};
use bitcoin::{blockdata::opcodes, util::contracthash};
use bitcoin::{hashes::hash160, Address as BtcAddress};
use bitcoin::{secp256k1, Script as BtcScript};
use expression::{self, FromTree};
use policy::{semantic, Liftable};
use std::{
fmt::Debug,
fmt::{self, Display},
marker::PhantomData,
str::FromStr,
sync::Arc,
};
use Descriptor;
use Error;
use Miniscript;
use NullCtx;
use {
BtcDescriptor, BtcDescriptorTrait, BtcError, BtcFromTree, BtcLiftable, BtcMiniscript,
BtcPolicy, BtcSatisfier, BtcSegwitv0, BtcTerminal, BtcTree,
};
use {DescriptorTrait, PkTranslate, Segwitv0};
use crate::{tweak_key, util::varint_len, DescriptorPublicKeyCtx};
use super::checksum::{desc_checksum, verify_checksum};
use {MiniscriptKey, ToPublicKey};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LegacyPeginKey {
Functionary(bitcoin::PublicKey),
NonFunctionary(bitcoin::PublicKey),
}
/// 'f' represents tweakable functionary keys and
/// 'u' represents untweakable keys
impl FromStr for LegacyPeginKey {
// only allow compressed keys in LegacyPegin
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.is_empty() {
Err(Error::BadDescriptor(format!("Empty Legacy pegin")))
} else if &s[0..1] == "f" && s.len() == 67 {
Ok(LegacyPeginKey::Functionary(bitcoin::PublicKey::from_str(
&s[1..],
)?))
} else if &s[0..1] == "u" && s.len() == 67 {
Ok(LegacyPeginKey::NonFunctionary(
bitcoin::PublicKey::from_str(&s[1..])?,
))
} else {
Err(Error::BadDescriptor(format!(
"Invalid Legacy Pegin descriptor"
)))
}
}
}
impl fmt::Display for LegacyPeginKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
LegacyPeginKey::Functionary(ref pk) => write!(f, "f{}", pk),
LegacyPeginKey::NonFunctionary(ref pk) => write!(f, "u{}", pk),
}
}
}
impl MiniscriptKey for LegacyPeginKey {
type Hash = hash160::Hash;
fn is_uncompressed(&self) -> bool {
false
}
fn serialized_len(&self) -> usize {
33
}
fn to_pubkeyhash(&self) -> Self::Hash {
let pk = match *self {
LegacyPeginKey::Functionary(ref pk) => pk,
LegacyPeginKey::NonFunctionary(ref pk) => pk,
};
MiniscriptKey::to_pubkeyhash(pk)
}
}
/// Context information required for tweaking Pegin Keys
/// Needs secp_ctx to actually compute the tweak and and the
/// tweak value.
/// In general, users should never really have use this struct
/// in any shape as it used internally in tweak creation.
/// In most cases, you would [DescriptorTweakCtx] which is the context
/// used in pegin descriptors for descriptor supported operations
pub struct LegacyPeginKeyCtx<'secp, C: secp256k1::Verification> {
/// The underlying secp context
secp_ctx: &'secp secp256k1::Secp256k1<C>,
// Use zero tweak for untweakable keys
tweak: Option<[u8; 32]>,
}
impl<'secp, C: secp256k1::Verification> Clone for LegacyPeginKeyCtx<'secp, C> {
fn clone(&self) -> Self {
Self {
secp_ctx: self.secp_ctx,
tweak: self.tweak.clone(),
}
}
}
impl<'secp, C: secp256k1::Verification> Copy for LegacyPeginKeyCtx<'secp, C> {}
impl<'secp, C: secp256k1::Verification> LegacyPeginKeyCtx<'secp, C> {
/// Create a new context
pub fn new(secp_ctx: &'secp secp256k1::Secp256k1<C>, tweak: Option<[u8; 32]>) -> Self {
Self {
secp_ctx: secp_ctx,
tweak: tweak,
}
}
}
/// Context information for computing tweaks for pegin descriptors
pub struct DescriptorTweakCtx<'secp, C: secp256k1::Verification, UserPkCtx: Copy> {
/// The underlying secp context to compute the tweak.
secp_ctx: &'secp secp256k1::Secp256k1<C>,
/// Context required for derivation of user's PublicKey
user_key_ctx: UserPkCtx,
}
impl<'secp, C: secp256k1::Verification, UserPkCtx: Copy> Clone
for DescriptorTweakCtx<'secp, C, UserPkCtx>
{
fn clone(&self) -> Self {
Self {
secp_ctx: self.secp_ctx,
user_key_ctx: self.user_key_ctx,
}
}
}
impl<'secp, C: secp256k1::Verification, UserPkCtx: Copy> Copy
for DescriptorTweakCtx<'secp, C, UserPkCtx>
{
}
impl<'secp, C: secp256k1::Verification, UserPkCtx: Copy> DescriptorTweakCtx<'secp, C, UserPkCtx> {
/// Create a new context
pub fn new(user_key_ctx: UserPkCtx, secp_ctx: &'secp secp256k1::Secp256k1<C>) -> Self {
Self {
user_key_ctx: user_key_ctx,
secp_ctx: secp_ctx,
}
}
}
impl<'secp, C: secp256k1::Verification> ToPublicKey<LegacyPeginKeyCtx<'secp, C>>
for LegacyPeginKey
{
fn to_public_key(&self, to_pk_ctx: LegacyPeginKeyCtx<'secp, C>) -> bitcoin::PublicKey {
match *self {
LegacyPeginKey::Functionary(ref pk) => {
let tweak = to_pk_ctx.tweak.unwrap_or([0u8; 32]);
#[allow(deprecated)]
contracthash::tweak_key(to_pk_ctx.secp_ctx, pk.clone(), &tweak)
}
LegacyPeginKey::NonFunctionary(ref pk) => pk.clone(),
}
}
fn hash_to_hash160(
hash: &Self::Hash,
_to_pk_ctx: LegacyPeginKeyCtx<'secp, C>,
) -> hash160::Hash {
*hash
}
}
/// Legacy Pegin Descriptor
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct LegacyPegin<Pk: MiniscriptKey, ToPkCtx: Copy> {
/// The federation pks
pub fed_pks: Vec<LegacyPeginKey>,
/// The federation threshold
pub fed_k: usize,
/// The emergency pks
pub emer_pks: Vec<LegacyPeginKey>,
/// The emergency threshold
pub emer_k: usize,
/// csv timelock
pub timelock: u32,
/// The elements descriptor required to redeem
pub desc: Descriptor<Pk>,
// Representation of federation policy as a miniscript
// Allows for easier implementation
ms: BtcMiniscript<LegacyPeginKey, BtcSegwitv0>,
// Inner Ctx
phantom: PhantomData<ToPkCtx>,
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> LegacyPegin<Pk, ToPkCtx> {
/// Create a new LegacyPegin descriptor
pub fn new(
fed_pks: Vec<LegacyPeginKey>,
fed_k: usize,
emer_pks: Vec<LegacyPeginKey>,
emer_k: usize,
timelock: u32,
desc: Descriptor<Pk>,
) -> Self {
let fed_ms = BtcMiniscript::from_ast(BtcTerminal::Multi(fed_k, fed_pks.clone()))
.expect("Multi type check can't fail");
let csv = BtcMiniscript::from_ast(BtcTerminal::Verify(Arc::new(
BtcMiniscript::from_ast(BtcTerminal::Older(timelock)).unwrap(),
)))
.unwrap();
let emer_ms = BtcMiniscript::from_ast(BtcTerminal::Multi(emer_k, emer_pks.clone()))
.expect("Multi type check can't fail");
let emer_ms =
BtcMiniscript::from_ast(BtcTerminal::AndV(Arc::new(csv), Arc::new(emer_ms))).unwrap();
let ms = BtcMiniscript::from_ast(BtcTerminal::OrD(Arc::new(fed_ms), Arc::new(emer_ms)))
.expect("Type check");
Self {
fed_pks,
fed_k,
emer_pks,
emer_k,
timelock,
desc,
ms,
phantom: PhantomData,
}
}
// Internal function to set the fields of Self according to
// miniscript
fn from_ms_and_desc(
desc: Descriptor<Pk>,
ms: BtcMiniscript<LegacyPeginKey, BtcSegwitv0>,
) -> Self {
let (fed_pks, fed_k, right) = if let BtcTerminal::OrD(a, b) = &ms.node {
if let (BtcTerminal::Multi(fed_k, ref fed_pks), right) = (&a.node, &b.node) {
(fed_pks, *fed_k, right)
} else {
unreachable!("Only valid pegin miniscripts");
}
} else {
unreachable!("Only valid pegin miniscripts");
};
let (timelock, emer_pks, emer_k) = if let BtcTerminal::AndV(l, r) = right {
if let (BtcTerminal::Verify(csv), BtcTerminal::Multi(emer_k, emer_pks)) =
(&l.node, &r.node)
{
if let BtcTerminal::Older(timelock) = csv.node {
(timelock, emer_pks, *emer_k)
} else {
unreachable!("Only valid pegin miniscripts");
}
} else {
unreachable!("Only valid pegin miniscripts");
}
} else {
unreachable!("Only valid pegin miniscripts");
};
Self {
fed_pks: fed_pks.clone(),
fed_k,
emer_pks: emer_pks.clone(),
emer_k,
timelock,
desc,
ms,
phantom: PhantomData,
}
}
/// Create a new descriptor with hard coded values for the
/// legacy federation and emergency keys
pub fn new_legacy_fed(_desc: Descriptor<Pk>) -> Self {
unimplemented!()
}
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> fmt::Debug for LegacyPegin<Pk, ToPkCtx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "legacy_pegin({:?},{:?})", self.ms, self.desc)
}
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> fmt::Display for LegacyPegin<Pk, ToPkCtx> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let desc = format!("legacy_pegin({},{})", self.ms, self.desc);
let checksum = desc_checksum(&desc).map_err(|_| fmt::Error)?;
write!(f, "{}#{}", &desc, &checksum)
}
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> Liftable<LegacyPeginKey> for LegacyPegin<Pk, ToPkCtx> {
fn lift(&self) -> Result<semantic::Policy<LegacyPeginKey>, Error> {
let btc_pol = BtcLiftable::lift(&self.ms)?;
Liftable::lift(&btc_pol)
}
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> BtcLiftable<LegacyPeginKey> for LegacyPegin<Pk, ToPkCtx> {
fn lift(&self) -> Result<BtcPolicy<LegacyPeginKey>, BtcError> {
self.ms.lift()
}
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> FromTree for LegacyPegin<Pk, ToPkCtx>
where
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
fn from_tree(top: &expression::Tree) -> Result<Self, Error> {
if top.name == "legacy_pegin" && top.args.len() == 2 {
// a roundtrip hack to use FromTree from bitcoin::Miniscript from
// expression::Tree in elements.
let ms_str = top.args[0].to_string();
let ms_expr = BtcTree::from_str(&ms_str)?;
//
let ms = BtcMiniscript::<LegacyPeginKey, BtcSegwitv0>::from_tree(&ms_expr);
let desc = Descriptor::<Pk>::from_tree(&top.args[1]);
Ok(LegacyPegin::from_ms_and_desc(desc?, ms?))
} else {
Err(Error::Unexpected(format!(
"{}({} args) while parsing legacy_pegin descriptor",
top.name,
top.args.len(),
)))
}
}
}
impl<Pk: MiniscriptKey, ToPkCtx: Copy> FromStr for LegacyPegin<Pk, ToPkCtx>
where
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
{
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let desc_str = verify_checksum(s)?;
let top = expression::Tree::from_str(desc_str)?;
Self::from_tree(&top)
}
}
// Implementation of Descriptor for Legacy Pegin
impl<Pk: MiniscriptKey, UserCtx: Copy> BtcDescriptorTrait<Pk> for LegacyPegin<Pk, UserCtx>
where
<Pk as FromStr>::Err: ToString,
<<Pk as MiniscriptKey>::Hash as FromStr>::Err: ToString,
UserCtx: Ord,
{
fn sanity_check(&self) -> Result<(), BtcError> {
self.ms
.sanity_check()
.map_err(|_| BtcError::Unexpected(format!("Federation script sanity check failed")))?;
self.desc
.sanity_check()
.map_err(|_| BtcError::Unexpected(format!("Federation script sanity check failed")))?;
Ok(())
}
fn address<ToPkCtx: Copy>(
&self,
to_pk_ctx: ToPkCtx,
network: bitcoin::Network,
) -> Option<BtcAddress>
where
Pk: ToPublicKey<ToPkCtx>,
{
Some(bitcoin::Address::p2shwsh(
&self.witness_script(to_pk_ctx),
network,
))
}
fn script_pubkey<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> BtcScript
where
Pk: ToPublicKey<ToPkCtx>,
{
self.address(to_pk_ctx, bitcoin::Network::Bitcoin)
.expect("Address cannot fail for pegin")
.script_pubkey()
}
fn unsigned_script_sig<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> BtcScript
where
Pk: ToPublicKey<ToPkCtx>,
{
let witness_script = self.witness_script(to_pk_ctx);
script::Builder::new()
.push_slice(&witness_script.to_v0_p2wsh()[..])
.into_script()
}
fn witness_script<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> BtcScript
where
Pk: ToPublicKey<ToPkCtx>,
{
let tweak_vec = self.desc.witness_script(to_pk_ctx).into_bytes();
// Hopefully, we never have to use this and dynafed is deployed
let mut builder = script::Builder::new()
.push_opcode(opcodes::all::OP_DEPTH)
.push_int(self.fed_k as i64 + 1)
.push_opcode(opcodes::all::OP_EQUAL)
.push_opcode(opcodes::all::OP_IF)
// manually serialize the left CMS branch, without the OP_CMS
.push_int(self.fed_k as i64);
// Issue 1:
// Creating context is expensive, but sadly our API does not support that
// As per the last discussion, ToPkCtx is something that Pk -> bitcoin::PublicKey
// But we also additionally need the secp ctx to perform the tweak addition
let secp_ctx = secp256k1::Secp256k1::verification_only();
let tweak = hashes::sha256::Hash::hash(&tweak_vec);
let key_ctx = LegacyPeginKeyCtx::new(&secp_ctx, Some(tweak.into_inner()));
for key in &self.fed_pks {
let tweaked_pk = key.to_public_key(key_ctx);
builder = builder.push_key(&tweaked_pk);
}
let mut nearly_done = builder
.push_int(self.fed_pks.len() as i64)
.push_opcode(opcodes::all::OP_ELSE)
.into_script()
.to_bytes();
let right = if let BtcTerminal::OrD(l, right) = &self.ms.node {
right
} else {
unreachable!("Only valid pegin descriptors should be created inside LegacyPegin")
};
let mut rser = right.encode(key_ctx).into_bytes();
// ...and we have an OP_VERIFY style checksequenceverify, which in
// Liquid production was encoded with OP_DROP instead...
assert_eq!(rser[4], opcodes::all::OP_VERIFY.into_u8());
rser[4] = opcodes::all::OP_DROP.into_u8();
// ...then we should serialize it by sharing the OP_CMS across
// both branches, and add an OP_DEPTH check to distinguish the
// branches rather than doing the normal cascade construction
nearly_done.extend(rser);
let insert_point = nearly_done.len() - 1;
nearly_done.insert(insert_point, 0x68);
bitcoin::Script::from(nearly_done)
}
fn get_satisfaction<ToPkCtx, S>(
&self,
satisfier: S,
to_pk_ctx: ToPkCtx,
) -> Result<(Vec<Vec<u8>>, BtcScript), BtcError>
where
ToPkCtx: Copy,
S: BtcSatisfier<ToPkCtx, Pk>,
Pk: ToPublicKey<ToPkCtx>,
{
// Issue 2:
// satisfaction API is also not consistent.
// The trait bound requires S: BtcSatisfier<ToPkCtx, Pk>,
// But what we actually need is S: Satisfier<LegacyPeginCtx<'a, T>, LegacyPeginKey>
// Which we cannot do because it will impose a stricter bound than trait definition
// I am starting to think as per our current definition ToPkCtx is something that
// takes Pk into bitcoin::PublicKey to the one that is finally used in script instead
// just something that takes into bitcoin::PublicKey.
// But we cannot declare the 'a and T in the function definition
// because it won't match the trait interface.
Err(BtcError::Unexpected(format!(
"Satisfaction not supported for pegin descriptors"
)))
}
fn max_satisfaction_weight(&self) -> Option<usize> {
let script_size = 628;
Some(
4 * 36
+ varint_len(script_size)
+ script_size
+ varint_len(self.ms.max_satisfaction_witness_elements()?)
+ self.ms.max_satisfaction_size()?,
)
}
fn script_code<ToPkCtx: Copy>(&self, to_pk_ctx: ToPkCtx) -> BtcScript
where
Pk: ToPublicKey<ToPkCtx>,
{
self.witness_script(to_pk_ctx)
}
}
// /// New Pegin Descriptor with Miniscript support
// /// Useful with dynamic federations
// #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
// pub struct Pegin<Pk: MiniscriptKey> {
// /// The untweaked pegin bitcoin descriptor
// pub fed_desc: BtcDescriptor<Pk>,
// /// The redeem elements descriptor
// pub elem_desc: Descriptor<Pk>,
// }
// impl<Pk: MiniscriptKey, ToPkCtx: Copy> Pegin<Pk> {
// /// Create a new LegacyPegin descriptor
// pub fn new(fed_desc: BtcDescriptor<Pk>, elem_desc: Descriptor<Pk>) -> Self {
// Self {
// fed_desc,
// elem_desc,
// }
// }
// }
// // Implementation of PeginDescriptor for Pegin
// // impl<Pk: MiniscriptKey, ToPkCtx: Copy> PeginDescriptor<Pk> for Pegin<Pk>{}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment