use std::io;
use std::io::{Read, Write};
use std::sync::Arc;
use bitcoin::consensus::ReadExt;
use bitcoin::XOnlyPublicKey;
use miniscript::descriptor::{
self, Descriptor, DescriptorPublicKey, DescriptorXKey, InnerXKey,
SinglePub, SinglePubKey, TapTree, Wildcard,
};
use miniscript::policy::concrete::Policy;
use miniscript::{
hash256, BareCtx, Legacy, Miniscript, MiniscriptKey, Segwitv0, Tap,
Terminal,
};
use crate::{strategies, Error, Strategy, StrictDecode, StrictEncode};
pub const MINISCRIPT_DEPTH_LIMIT: u8 = 64;
const MS_FALSE: u8 = 0;
const MS_TRUE: u8 = 1;
const MS_KEY: u8 = 0x02;
const MS_KEY_HASH: u8 = 0x0a;
const MS_HASHED_KEY: u8 = 0x03;
const MS_THRESH: u8 = 0x20;
const MS_MULTI: u8 = 0x21;
const MS_MULTI_A: u8 = 0x2c;
const MS_AFTER: u8 = 0x09;
const MS_OLDER: u8 = 0x08;
const MS_SHA256: u8 = 0x04;
const MS_HASH256: u8 = 0x05;
const MS_RIPEMD160: u8 = 0x06;
const MS_HASH160: u8 = 0x07;
const MS_AND_V: u8 = 0x22;
const MS_AND_B: u8 = 0x23;
const MS_AND_OR: u8 = 0x24;
const MS_OR_B: u8 = 0x28;
const MS_OR_D: u8 = 0x29;
const MS_OR_C: u8 = 0x2a;
const MS_OR_I: u8 = 0x2b;
const MS_ALT: u8 = 0x10;
const MS_SWAP: u8 = 0x11;
const MS_CHECK: u8 = 0x12;
const MS_DUP_IF: u8 = 0x13;
const MS_VERIFY: u8 = 0x18;
const MS_NON_ZERO: u8 = 0x19;
const MS_ZERO_NE: u8 = 0x1a;
macro_rules! strict_encode_tuple {
($encoder:ident; $tag:ident, $item:ident) => {{
$encoder.write_all(&[$tag])?;
1 + $item.strict_encode($encoder)?
}};
}
macro_rules! strict_encode_usize {
( $encoder:ident; $int:expr ) => { {
let count = $int; if count > u16::MAX as usize {
return Err(Error::ExceedMaxItems(count));
}
$encoder.write_all(&(count as u16).to_le_bytes())?;
2 } };
}
impl From<miniscript::Error> for Error {
fn from(err: miniscript::Error) -> Self {
Error::DataIntegrityError(format!(": {}", err))
}
}
impl StrictEncode for BareCtx {
#[inline]
fn strict_encode<E: Write>(&self, _: E) -> Result<usize, Error> { Ok(0) }
}
impl StrictDecode for BareCtx {
fn strict_decode<D: Read>(_: D) -> Result<Self, Error> {
unreachable!("attempt to construct miniscript context object")
}
}
impl StrictEncode for Legacy {
#[inline]
fn strict_encode<E: Write>(&self, _: E) -> Result<usize, Error> { Ok(0) }
}
impl StrictDecode for Legacy {
fn strict_decode<D: Read>(_: D) -> Result<Self, Error> {
unreachable!("attempt to construct miniscript context object")
}
}
impl StrictEncode for Segwitv0 {
#[inline]
fn strict_encode<E: Write>(&self, _: E) -> Result<usize, Error> { Ok(0) }
}
impl StrictDecode for Segwitv0 {
fn strict_decode<D: Read>(_: D) -> Result<Self, Error> {
unreachable!("attempt to construct miniscript context object")
}
}
impl StrictEncode for Tap {
#[inline]
fn strict_encode<E: Write>(&self, _: E) -> Result<usize, Error> { Ok(0) }
}
impl StrictDecode for Tap {
fn strict_decode<D: Read>(_: D) -> Result<Self, Error> {
unreachable!("attempt to construct miniscript context object")
}
}
impl Strategy for hash256::Hash {
type Strategy = strategies::HashFixedBytes;
}
impl<Pk> StrictEncode for TapTree<Pk>
where
Pk: MiniscriptKey + StrictEncode,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
fn strict_encode<E: Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(match self {
TapTree::Tree(tree1, tree2) => {
let mut len = 2u8.strict_encode(&mut e)?;
len += tree1.strict_serialize()?.strict_encode(&mut e)?;
len += tree2.strict_serialize()?.strict_encode(&mut e)?;
len
}
TapTree::Leaf(pk) => {
strict_encode_list!(e; 1u8, pk)
}
})
}
}
impl<Pk> StrictDecode for TapTree<Pk>
where
Pk: MiniscriptKey + StrictDecode,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
fn strict_decode<D: Read>(mut d: D) -> Result<Self, Error> {
match u8::strict_decode(&mut d)? {
1u8 => Ok(TapTree::Leaf(StrictDecode::strict_decode(&mut d)?)),
2u8 => {
let vec1 = Vec::<u8>::strict_decode(&mut d)?;
let vec2 = Vec::<u8>::strict_decode(&mut d)?;
Ok(TapTree::Tree(
Arc::new(TapTree::strict_deserialize(vec1)?),
Arc::new(TapTree::strict_deserialize(vec2)?),
))
}
wrong => Err(Error::EnumValueNotKnown("TapTree", wrong as usize)),
}
}
}
impl<Pk> StrictEncode for Policy<Pk>
where
Pk: MiniscriptKey + StrictEncode,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
fn encode_policy_inner<Pk>(
policy: &Policy<Pk>,
e: &mut impl io::Write,
mut depth: u8,
) -> Result<usize, Error>
where
Pk: MiniscriptKey + StrictEncode,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
if depth > MINISCRIPT_DEPTH_LIMIT {
return Err(Error::ExceedMaxItems(
MINISCRIPT_DEPTH_LIMIT as usize,
));
}
depth += 1;
Ok(match policy {
Policy::Unsatisfiable => MS_FALSE.strict_encode(e)?,
Policy::Trivial => MS_TRUE.strict_encode(e)?,
Policy::Key(pk) => strict_encode_tuple!(e; MS_KEY, pk),
Policy::After(tl) => strict_encode_tuple!(e; MS_AFTER, tl),
Policy::Older(tl) => strict_encode_tuple!(e; MS_OLDER, tl),
Policy::Sha256(hash) => {
strict_encode_tuple!(e; MS_SHA256, hash)
}
Policy::Hash256(hash) => {
strict_encode_tuple!(e; MS_HASH256, hash)
}
Policy::Ripemd160(hash) => {
strict_encode_tuple!(e; MS_RIPEMD160, hash)
}
Policy::Hash160(hash) => {
strict_encode_tuple!(e; MS_HASH160, hash)
}
Policy::And(vec) => {
let mut len = 1usize;
e.write_all(&[MS_AND_B])?;
len += strict_encode_usize!(e; vec.len());
for p in vec {
len += encode_policy_inner(p, e, depth)?;
}
len
}
Policy::Or(vec) => {
let mut len = 1usize;
e.write_all(&[MS_OR_B])?;
len += strict_encode_usize!(e; vec.len());
for (x, p) in vec {
len += strict_encode_usize!(e; *x);
len += encode_policy_inner(p, e, depth)?;
}
len
}
Policy::Threshold(thresh, vec) => {
let mut len = 1usize;
e.write_all(&[MS_THRESH])?;
len += strict_encode_usize!(e; *thresh);
len += strict_encode_usize!(e; vec.len());
for p in vec {
len += encode_policy_inner(p, e, depth)?;
}
len
}
})
}
encode_policy_inner(self, &mut e, 1)
}
}
impl<Pk> StrictDecode for Policy<Pk>
where
Pk: MiniscriptKey + StrictDecode,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
fn decode_policy_inner<Pk>(
d: &mut impl io::Read,
mut depth: u8,
) -> Result<Policy<Pk>, Error>
where
Pk: MiniscriptKey + StrictDecode,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
if depth > MINISCRIPT_DEPTH_LIMIT {
return Err(Error::ExceedMaxItems(
MINISCRIPT_DEPTH_LIMIT as usize,
));
}
depth += 1;
let byte = d.read_u8()?;
Ok(match byte {
MS_TRUE => Policy::Trivial,
MS_FALSE => Policy::Unsatisfiable,
MS_KEY => Policy::Key(Pk::strict_decode(d)?),
MS_AFTER => Policy::After(StrictDecode::strict_decode(d)?),
MS_OLDER => Policy::Older(StrictDecode::strict_decode(d)?),
MS_SHA256 => Policy::Sha256(StrictDecode::strict_decode(d)?),
MS_HASH256 => Policy::Hash256(StrictDecode::strict_decode(d)?),
MS_RIPEMD160 => {
Policy::Ripemd160(StrictDecode::strict_decode(d)?)
}
MS_HASH160 => Policy::Hash160(StrictDecode::strict_decode(d)?),
MS_AND_B => {
let len = d.read_u16()?;
let mut vec = Vec::with_capacity(len as usize);
for _ in 0..len {
vec.push(decode_policy_inner(d, depth)?);
}
Policy::And(vec)
}
MS_OR_B => {
let len = d.read_u16()?;
let mut vec = Vec::with_capacity(len as usize);
for _ in 0..len {
vec.push((
d.read_u16()? as usize,
decode_policy_inner(d, depth)?,
));
}
Policy::Or(vec)
}
MS_THRESH => {
let thresh = d.read_u16()? as usize;
let len = d.read_u16()?;
let mut vec = Vec::with_capacity(len as usize);
for _ in 0..len {
vec.push(decode_policy_inner(d, depth)?);
}
Policy::Threshold(thresh, vec)
}
MS_KEY_HASH | MS_HASHED_KEY | MS_ALT | MS_SWAP | MS_CHECK
| MS_DUP_IF | MS_VERIFY | MS_NON_ZERO | MS_ZERO_NE
| MS_AND_V | MS_AND_OR | MS_OR_D | MS_OR_C | MS_OR_I
| MS_MULTI => {
return Err(Error::DataIntegrityError(format!(
"byte {:#04X} is a valid miniscript instruction, but \
does not belong to a set of concrete policy \
instructions. Try to decode data using different \
miniscript type",
byte
)))
}
wrong => {
return Err(Error::DataIntegrityError(format!(
"byte {:#04X} does not correspond to any of \
miniscript concrete policy instructions",
wrong
)))
}
})
}
decode_policy_inner(&mut d, 1)
}
}
impl<Pk, Ctx> StrictEncode for Miniscript<Pk, Ctx>
where
Pk: MiniscriptKey + StrictEncode,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
Ctx: miniscript::ScriptContext,
{
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
macro_rules! strict_encode_ms {
($encoder:ident; $tag:ident, $depth:expr, $($ms:expr),+) => { {
let mut len = 1usize;
$encoder.write_all(&[$tag])?;
$( len += encode_miniscript_inner($ms, $encoder, $depth)?; )+
len
} };
}
fn encode_miniscript_inner<Pk, Ctx>(
ms: &Miniscript<Pk, Ctx>,
mut e: &mut impl io::Write,
mut depth: u8,
) -> Result<usize, Error>
where
Pk: MiniscriptKey + StrictEncode,
Ctx: miniscript::ScriptContext,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
if depth > MINISCRIPT_DEPTH_LIMIT {
return Err(Error::ExceedMaxItems(
MINISCRIPT_DEPTH_LIMIT as usize,
));
}
depth += 1;
Ok(match &ms.node {
Terminal::False => MS_FALSE.strict_encode(e)?,
Terminal::True => MS_TRUE.strict_encode(e)?,
Terminal::PkK(pk) => strict_encode_tuple!(e; MS_KEY, pk),
Terminal::PkH(pk) => {
strict_encode_tuple!(e; MS_HASHED_KEY, pk)
}
Terminal::RawPkH(hash) => {
strict_encode_tuple!(e; MS_KEY_HASH, hash)
}
Terminal::After(tl) => strict_encode_tuple!(e; MS_AFTER, tl),
Terminal::Older(tl) => strict_encode_tuple!(e; MS_OLDER, tl),
Terminal::Sha256(hash) => {
strict_encode_tuple!(e; MS_SHA256, hash)
}
Terminal::Hash256(hash) => {
strict_encode_tuple!(e; MS_HASH256, hash)
}
Terminal::Ripemd160(hash) => {
strict_encode_tuple!(e; MS_RIPEMD160, hash)
}
Terminal::Hash160(hash) => {
strict_encode_tuple!(e; MS_HASH160, hash)
}
Terminal::Alt(ms) => strict_encode_ms!(e; MS_ALT, depth, ms),
Terminal::Swap(ms) => strict_encode_ms!(e; MS_SWAP, depth, ms),
Terminal::Check(ms) => {
strict_encode_ms!(e; MS_CHECK, depth, ms)
}
Terminal::DupIf(ms) => {
strict_encode_ms!(e; MS_DUP_IF, depth, ms)
}
Terminal::Verify(ms) => {
strict_encode_ms!(e; MS_VERIFY, depth, ms)
}
Terminal::NonZero(ms) => {
strict_encode_ms!(e; MS_NON_ZERO, depth, ms)
}
Terminal::ZeroNotEqual(ms) => {
strict_encode_ms!(e; MS_ZERO_NE, depth, ms)
}
Terminal::AndV(ms1, ms2) => {
strict_encode_ms!(e; MS_AND_V, depth, ms1, ms2)
}
Terminal::AndB(ms1, ms2) => {
strict_encode_ms!(e; MS_AND_B, depth, ms1, ms2)
}
Terminal::AndOr(ms1, ms2, ms3) => {
strict_encode_ms!(e; MS_AND_OR, depth, ms1, ms2, ms3)
}
Terminal::OrB(ms1, ms2) => {
strict_encode_ms!(e; MS_OR_B, depth, ms1, ms2)
}
Terminal::OrD(ms1, ms2) => {
strict_encode_ms!(e; MS_OR_D, depth, ms1, ms2)
}
Terminal::OrC(ms1, ms2) => {
strict_encode_ms!(e; MS_OR_C, depth, ms1, ms2)
}
Terminal::OrI(ms1, ms2) => {
strict_encode_ms!(e; MS_OR_I, depth, ms1, ms2)
}
Terminal::Multi(thresh, vec) => {
let mut len = 1usize;
e.write_all(&[MS_MULTI])?;
len += strict_encode_usize!(e; *thresh);
len += strict_encode_usize!(e; vec.len());
for pk in vec {
len += pk.strict_encode(&mut e)?;
}
len
}
Terminal::Thresh(thresh, vec) => {
let mut len = 1usize;
e.write_all(&[MS_THRESH])?;
len += strict_encode_usize!(e; *thresh);
len += strict_encode_usize!(e; vec.len());
for ms in vec {
len += encode_miniscript_inner(ms, e, depth)?;
}
len
}
Terminal::MultiA(thresh, vec) => {
let mut len = 1usize;
e.write_all(&[MS_MULTI_A])?;
len += strict_encode_usize!(e; *thresh);
len += strict_encode_usize!(e; vec.len());
for pk in vec {
len += pk.strict_encode(&mut e)?;
}
len
}
})
}
encode_miniscript_inner(self, &mut e, 1)
}
}
impl<Pk, Ctx> StrictDecode for Miniscript<Pk, Ctx>
where
Pk: MiniscriptKey + StrictDecode,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
Ctx: miniscript::ScriptContext,
{
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
fn decode_miniscript_inner<Pk, Ctx>(
d: &mut impl io::Read,
mut depth: u8,
) -> Result<Miniscript<Pk, Ctx>, Error>
where
Pk: MiniscriptKey + StrictDecode,
Ctx: miniscript::ScriptContext,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
if depth > MINISCRIPT_DEPTH_LIMIT {
return Err(Error::ExceedMaxItems(
MINISCRIPT_DEPTH_LIMIT as usize,
));
}
depth += 1;
let term = match d.read_u8()? {
MS_TRUE => Terminal::True,
MS_FALSE => Terminal::False,
MS_KEY => Terminal::PkK(Pk::strict_decode(d)?),
MS_HASHED_KEY => Terminal::PkH(Pk::strict_decode(d)?),
MS_KEY_HASH => {
Terminal::RawPkH(StrictDecode::strict_decode(d)?)
}
MS_AFTER => Terminal::After(StrictDecode::strict_decode(d)?),
MS_OLDER => Terminal::Older(StrictDecode::strict_decode(d)?),
MS_SHA256 => Terminal::Sha256(StrictDecode::strict_decode(d)?),
MS_HASH256 => {
Terminal::Hash256(StrictDecode::strict_decode(d)?)
}
MS_RIPEMD160 => {
Terminal::Ripemd160(StrictDecode::strict_decode(d)?)
}
MS_HASH160 => {
Terminal::Hash160(StrictDecode::strict_decode(d)?)
}
MS_ALT => {
Terminal::Alt(decode_miniscript_inner(d, depth)?.into())
}
MS_SWAP => {
Terminal::Swap(decode_miniscript_inner(d, depth)?.into())
}
MS_CHECK => {
Terminal::Check(decode_miniscript_inner(d, depth)?.into())
}
MS_DUP_IF => {
Terminal::DupIf(decode_miniscript_inner(d, depth)?.into())
}
MS_VERIFY => {
Terminal::Verify(decode_miniscript_inner(d, depth)?.into())
}
MS_NON_ZERO => {
Terminal::NonZero(decode_miniscript_inner(d, depth)?.into())
}
MS_ZERO_NE => Terminal::ZeroNotEqual(
decode_miniscript_inner(d, depth)?.into(),
),
MS_AND_V => Terminal::AndV(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_AND_B => Terminal::AndB(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_AND_OR => Terminal::AndOr(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_OR_B => Terminal::OrB(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_OR_D => Terminal::OrD(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_OR_C => Terminal::OrC(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_OR_I => Terminal::OrI(
decode_miniscript_inner(d, depth)?.into(),
decode_miniscript_inner(d, depth)?.into(),
),
MS_MULTI => Terminal::Multi(
d.read_u16()? as usize,
Vec::strict_decode(d)?,
),
MS_THRESH => {
let thresh = d.read_u16()? as usize;
let len = d.read_u16()?;
let mut vec = Vec::with_capacity(len as usize);
for _ in 0..len {
vec.push(decode_miniscript_inner(d, depth)?.into());
}
Terminal::Thresh(thresh, vec)
}
MS_MULTI_A => Terminal::MultiA(
d.read_u16()? as usize,
Vec::strict_decode(d)?,
),
wrong => {
return Err(Error::DataIntegrityError(format!(
"byte {:#04X} does not correspond to any of \
miniscript instructions",
wrong
)))
}
};
Miniscript::from_ast(term).map_err(|err| {
Error::DataIntegrityError(format!(
"miniscript does not pass check: {}",
err
))
})
}
decode_miniscript_inner(&mut d, 1)
}
}
const DESCRIPTOR_BARE: u8 = 0x00;
const DESCRIPTOR_PKH: u8 = 0x01;
const DESCRIPTOR_SH: u8 = 0x02;
const DESCRIPTOR_WPKH: u8 = 0x10;
const DESCRIPTOR_WSH: u8 = 0x11;
const DESCRIPTOR_TR: u8 = 0x20;
const DESCRIPTOR_SORTED_MULTI: u8 = 0x03;
const DESCRIPTOR_MINISCRIPT: u8 = 0x04;
impl StrictEncode for SinglePubKey {
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(match self {
SinglePubKey::FullKey(pk) => {
strict_encode_list!(e; 0x01u8, pk)
}
SinglePubKey::XOnly(xpk) => {
strict_encode_list!(e; 0x02u8, xpk)
}
})
}
}
impl StrictDecode for SinglePubKey {
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Ok(match u8::strict_decode(&mut d)? {
0x01 => SinglePubKey::FullKey(bitcoin::PublicKey::strict_decode(
&mut d,
)?),
0x02 => SinglePubKey::XOnly(XOnlyPublicKey::strict_decode(&mut d)?),
wrong => {
return Err(Error::DataIntegrityError(format!(
"unknown miniscript single pubkey tag `{:#04X}",
wrong
)))
}
})
}
}
impl StrictEncode for DescriptorPublicKey {
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(match self {
DescriptorPublicKey::Single(pk) => {
strict_encode_list!(e; 0x01u8, pk)
}
DescriptorPublicKey::XPub(xpub) => {
strict_encode_list!(e; 0x02u8, xpub)
}
})
}
}
impl StrictDecode for DescriptorPublicKey {
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Ok(match u8::strict_decode(&mut d)? {
0x01 => {
DescriptorPublicKey::Single(SinglePub::strict_decode(&mut d)?)
}
0x02 => DescriptorPublicKey::XPub(DescriptorXKey::strict_decode(
&mut d,
)?),
wrong => {
return Err(Error::DataIntegrityError(format!(
"unknown descriptor key tag `{:#04X}",
wrong
)))
}
})
}
}
impl StrictEncode for SinglePub {
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(strict_encode_list!(e; self.origin, self.key))
}
}
impl StrictDecode for SinglePub {
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Ok(strict_decode_self!(d; origin, key; crate))
}
}
impl<Pk> StrictEncode for DescriptorXKey<Pk>
where
Pk: InnerXKey + StrictEncode,
{
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(
strict_encode_list!(e; self.origin, self.derivation_path, self.xkey, self.wildcard),
)
}
}
impl<Pk> StrictDecode for DescriptorXKey<Pk>
where
Pk: InnerXKey + StrictDecode,
{
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Ok(
strict_decode_self!(d; origin, derivation_path, xkey, wildcard; crate),
)
}
}
impl StrictEncode for Wildcard {
fn strict_encode<E: io::Write>(&self, e: E) -> Result<usize, Error> {
match self {
Wildcard::None => 0u8,
Wildcard::Unhardened => 1u8,
Wildcard::Hardened => 2u8,
}
.strict_encode(e)
}
}
impl StrictDecode for Wildcard {
fn strict_decode<D: io::Read>(d: D) -> Result<Self, Error> {
Ok(match u8::strict_decode(d)? {
0 => Wildcard::None,
1 => Wildcard::Unhardened,
2 => Wildcard::Hardened,
wrong => {
return Err(Error::DataIntegrityError(format!(
"unknown descriptor xpub wildcard type `{:#04X}`",
wrong
)))
}
})
}
}
impl<Pk> StrictEncode for Descriptor<Pk>
where
Pk: MiniscriptKey + StrictEncode,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
fn strict_encode<E: io::Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(match self {
Descriptor::Bare(bare) => {
strict_encode_list!(e; DESCRIPTOR_BARE, bare)
}
Descriptor::Pkh(pkh) => {
strict_encode_list!(e; DESCRIPTOR_PKH, pkh)
}
Descriptor::Wpkh(wpkh) => {
strict_encode_list!(e; DESCRIPTOR_WPKH, wpkh)
}
Descriptor::Sh(sh) => {
strict_encode_list!(e; DESCRIPTOR_SH, sh)
}
Descriptor::Wsh(wsh) => {
strict_encode_list!(e; DESCRIPTOR_WSH, wsh)
}
Descriptor::Tr(tr) => {
strict_encode_list!(e; DESCRIPTOR_TR, tr)
}
})
}
}
impl<Pk> StrictDecode for Descriptor<Pk>
where
Pk: MiniscriptKey + StrictDecode,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
fn strict_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Ok(match u8::strict_decode(&mut d)? {
DESCRIPTOR_BARE => {
Descriptor::Bare(descriptor::Bare::strict_decode(&mut d)?)
}
DESCRIPTOR_PKH => {
Descriptor::Pkh(descriptor::Pkh::strict_decode(&mut d)?)
}
DESCRIPTOR_SH => {
Descriptor::Sh(descriptor::Sh::strict_decode(&mut d)?)
}
DESCRIPTOR_WPKH => {
Descriptor::Wpkh(descriptor::Wpkh::strict_decode(&mut d)?)
}
DESCRIPTOR_WSH => {
Descriptor::Wsh(descriptor::Wsh::strict_decode(&mut d)?)
}
DESCRIPTOR_TR => {
Descriptor::Tr(descriptor::Tr::strict_decode(&mut d)?)
}
wrong => {
return Err(Error::DataIntegrityError(format!(
"unknown miniscript descriptor type: #{:#04X}",
wrong
)))
}
})
}
}
impl<Pk> StrictEncode for descriptor::Bare<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
#[inline]
fn strict_encode<E: Write>(&self, e: E) -> Result<usize, Error> {
self.as_inner().strict_encode(e)
}
}
impl<Pk> StrictDecode for descriptor::Bare<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
#[inline]
fn strict_decode<D: Read>(d: D) -> Result<Self, Error> {
Self::new(StrictDecode::strict_decode(d)?).map_err(Error::from)
}
}
impl<Pk> StrictEncode for descriptor::Pkh<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
#[inline]
fn strict_encode<E: Write>(&self, e: E) -> Result<usize, Error> {
self.as_inner().strict_encode(e)
}
}
impl<Pk> StrictDecode for descriptor::Pkh<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
#[inline]
fn strict_decode<D: Read>(d: D) -> Result<Self, Error> {
Ok(Self::new(StrictDecode::strict_decode(d)?))
}
}
impl<Pk> StrictEncode for descriptor::Wpkh<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
#[inline]
fn strict_encode<E: Write>(&self, e: E) -> Result<usize, Error> {
self.as_inner().strict_encode(e)
}
}
impl<Pk> StrictDecode for descriptor::Wpkh<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
#[inline]
fn strict_decode<D: Read>(d: D) -> Result<Self, Error> {
Self::new(StrictDecode::strict_decode(d)?).map_err(Error::from)
}
}
impl<Pk> StrictEncode for descriptor::Sh<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
#[inline]
fn strict_encode<E: Write>(&self, e: E) -> Result<usize, Error> {
self.as_inner().strict_encode(e)
}
}
impl<Pk> StrictDecode for descriptor::Sh<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
#[inline]
fn strict_decode<D: Read>(d: D) -> Result<Self, Error> {
Ok(match descriptor::ShInner::strict_decode(d)? {
descriptor::ShInner::Wsh(wsh) => descriptor::Sh::new_with_wsh(wsh),
descriptor::ShInner::Wpkh(wpkh) => {
descriptor::Sh::new_with_wpkh(wpkh)
}
descriptor::ShInner::SortedMulti(inner) => {
descriptor::Sh::new_sortedmulti(inner.k, inner.pks)?
}
descriptor::ShInner::Ms(ms) => descriptor::Sh::new(ms)?,
})
}
}
impl<Pk> StrictEncode for descriptor::Wsh<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
#[inline]
fn strict_encode<E: Write>(&self, e: E) -> Result<usize, Error> {
self.as_inner().strict_encode(e)
}
}
impl<Pk> StrictDecode for descriptor::Wsh<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
#[inline]
fn strict_decode<D: Read>(d: D) -> Result<Self, Error> {
match descriptor::WshInner::strict_decode(d)? {
descriptor::WshInner::SortedMulti(inner) => {
descriptor::Wsh::new_sortedmulti(inner.k, inner.pks)
}
descriptor::WshInner::Ms(ms) => descriptor::Wsh::new(ms),
}
.map_err(Error::from)
}
}
impl<Pk> StrictEncode for descriptor::Tr<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
fn strict_encode<E: Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(strict_encode_list!(e; self.internal_key(), self.taptree()))
}
}
impl<Pk> StrictDecode for descriptor::Tr<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
fn strict_decode<D: Read>(mut d: D) -> Result<Self, Error> {
descriptor::Tr::new(
StrictDecode::strict_decode(&mut d)?,
StrictDecode::strict_decode(&mut d)?,
)
.map_err(Error::from)
}
}
impl<Pk, Ctx> StrictEncode for descriptor::SortedMultiVec<Pk, Ctx>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
Ctx: miniscript::ScriptContext,
{
fn strict_encode<E: Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(strict_encode_list!(e; self.k, self.pks))
}
}
impl<Pk, Ctx> StrictDecode for descriptor::SortedMultiVec<Pk, Ctx>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
Ctx: miniscript::ScriptContext,
{
fn strict_decode<D: Read>(mut d: D) -> Result<Self, Error> {
descriptor::SortedMultiVec::new(
StrictDecode::strict_decode(&mut d)?,
StrictDecode::strict_decode(&mut d)?,
)
.map_err(Error::from)
}
}
impl<Pk> StrictEncode for descriptor::ShInner<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
fn strict_encode<E: Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(match self {
descriptor::ShInner::Wsh(wsh) => {
strict_encode_list!(e; DESCRIPTOR_WSH, wsh)
}
descriptor::ShInner::Wpkh(wpkh) => {
strict_encode_list!(e; DESCRIPTOR_WPKH, wpkh)
}
descriptor::ShInner::SortedMulti(multi) => {
strict_encode_list!(e; DESCRIPTOR_SORTED_MULTI, multi)
}
descriptor::ShInner::Ms(ms) => {
strict_encode_list!(e; DESCRIPTOR_MINISCRIPT, ms)
}
})
}
}
impl<Pk> StrictDecode for descriptor::ShInner<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
fn strict_decode<D: Read>(mut d: D) -> Result<Self, Error> {
Ok(match u8::strict_decode(&mut d)? {
DESCRIPTOR_MINISCRIPT => {
descriptor::ShInner::Ms(StrictDecode::strict_decode(&mut d)?)
}
DESCRIPTOR_SORTED_MULTI => descriptor::ShInner::SortedMulti(
StrictDecode::strict_decode(&mut d)?,
),
DESCRIPTOR_WPKH => {
descriptor::ShInner::Wpkh(StrictDecode::strict_decode(&mut d)?)
}
DESCRIPTOR_WSH => {
descriptor::ShInner::Wsh(StrictDecode::strict_decode(&mut d)?)
}
wrong => {
return Err(Error::DataIntegrityError(format!(
"invalid miniscript ShInner descriptor type: #{:#04X}",
wrong
)))
}
})
}
}
impl<Pk> StrictEncode for descriptor::WshInner<Pk>
where
Pk: StrictEncode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictEncode,
<Pk as MiniscriptKey>::Ripemd160: StrictEncode,
<Pk as MiniscriptKey>::Hash256: StrictEncode,
<Pk as MiniscriptKey>::Sha256: StrictEncode,
{
fn strict_encode<E: Write>(&self, mut e: E) -> Result<usize, Error> {
Ok(match self {
descriptor::WshInner::SortedMulti(multi) => {
strict_encode_list!(e; DESCRIPTOR_SORTED_MULTI, multi)
}
descriptor::WshInner::Ms(ms) => {
strict_encode_list!(e; DESCRIPTOR_MINISCRIPT, ms)
}
})
}
}
impl<Pk> StrictDecode for descriptor::WshInner<Pk>
where
Pk: StrictDecode + MiniscriptKey,
<Pk as MiniscriptKey>::Hash160: StrictDecode,
<Pk as MiniscriptKey>::Ripemd160: StrictDecode,
<Pk as MiniscriptKey>::Hash256: StrictDecode,
<Pk as MiniscriptKey>::Sha256: StrictDecode,
{
fn strict_decode<D: Read>(mut d: D) -> Result<Self, Error> {
Ok(match u8::strict_decode(&mut d)? {
DESCRIPTOR_MINISCRIPT => {
descriptor::WshInner::Ms(StrictDecode::strict_decode(&mut d)?)
}
DESCRIPTOR_SORTED_MULTI => descriptor::WshInner::SortedMulti(
StrictDecode::strict_decode(&mut d)?,
),
wrong => {
return Err(Error::DataIntegrityError(format!(
"invalid miniscript WshInner descriptor type: #{:#04X}",
wrong
)))
}
})
}
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use miniscript::{
policy, BareCtx, Descriptor, Legacy, Miniscript, Segwitv0,
};
use strict_encoding_test::*;
use crate::StrictDecode;
#[test]
#[should_panic]
fn test_bare_ctx() { BareCtx::strict_deserialize([0u8]).unwrap(); }
#[test]
#[should_panic]
fn test_legacy_ctx() { Legacy::strict_deserialize([0u8]).unwrap(); }
#[test]
#[should_panic]
fn test_segwitv0_ctx() { Segwitv0::strict_deserialize([0u8]).unwrap(); }
#[test]
fn test_policy() {
const SET: [&str; 12] = [
"and(pk(A),or(and(after(9),pk(B)),and(after(1000000000),pk(C))))",
"pk(A)",
"after(9)",
"older(1)",
"sha256(1111111111111111111111111111111111111111111111111111111111111111)",
"and(pk(A),pk(B))",
"or(pk(A),pk(B))",
"thresh(2,pk(A),pk(B),pk(C))",
"thresh(2,after(9),after(9),pk(A))",
"and(pk(A),or(after(9),after(9)))",
"or(1@and(pk(A),pk(B)),127@pk(C))",
"and(and(and(or(127@thresh(2,pk(A),pk(B),thresh(2,or(127@pk(A),1@pk(B)),after(100),or(and(pk(C),after(200)),and(pk(D),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925))),pk(E))),1@pk(F)),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925)),or(127@pk(G),1@after(300))),or(127@after(400),pk(H)))",
];
for s in &SET {
let policy = policy::Concrete::<String>::from_str(s).unwrap();
test_object_encoding_roundtrip(&policy).unwrap();
}
}
#[test]
fn test_miniscript() {
const SET: [&str; 28] = [
"lltvln:after(1231488000)",
"uuj:and_v(v:multi(2,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a,025601570cb47f238d2b0286db4a990fa0f3ba28d1a319f5e7cf55c2a2444da7cc),after(1231488000))",
"or_b(un:multi(2,03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),al:older(16))",
"j:and_v(vdv:after(1567547623),older(2016))",
"t:and_v(vu:hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),v:sha256(ec4916dd28fc4c10d78e287ca5d9cc51ee1ae73cbfde08c6b37324cbfaac8bc5))",
"t:andor(multi(3,02d7924d4f7d43ea965a465ae3095ff41131e5946f3c85f79e44adbcf8e27e080e,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,02e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd13),v:older(4194305),v:sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2))",
"or_d(multi(1,02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9),or_b(multi(3,022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01,032fa2104d6b38d11b0230010559879124e42ab8dfeff5ff29dc9cdadd4ecacc3f,03d01115d548e7561b15c38f004d734633687cf4419620095bc5b0f47070afe85a),su:after(500000)))",
"or_d(sha256(38df1c1f64a24a77b23393bca50dff872e31edc4f3b5aa3b90ad0b82f4f089b6),and_n(un:after(499999999),older(4194305)))",
"and_v(or_i(v:multi(2,02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5,03774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb),v:multi(2,03e60fce93b59e9ec53011aabc21c23e97b2a31369b87a5ae9c44ee89e2a6dec0a,025cbdf0646e5db4eaa398f365f2ea7a0e3d419b7e0330e39ce92bddedcac4f9bc)),sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68))",
"j:and_b(multi(2,0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),s:or_i(older(1),older(4252898)))",
"and_b(older(16),s:or_d(sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),n:after(1567547623)))",
"j:and_v(v:hash160(20195b5a3d650c17f0f29f91c33f8f6335193d07),or_d(sha256(96de8fc8c256fa1e1556d41af431cace7dca68707c78dd88c3acab8b17164c47),older(16)))",
"and_b(hash256(32ba476771d01e37807990ead8719f08af494723de1d228f2c2c07cc0aa40bac),a:and_b(hash256(131772552c01444cd81360818376a040b7c3b2b7b0a53550ee3edde216cec61b),a:older(1)))",
"thresh(2,multi(2,03a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),a:multi(1,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00),ac:pk_k(022f01e5e15cca351daff3843fb70f3c2f0a1bdd05e5af888a67784ef3e10a2a01))",
"and_n(sha256(d1ec675902ef1633427ca360b290b0b3045a0d9058ddb5e648b4c3c3224c5c68),t:or_i(v:older(4252898),v:older(144)))",
"c:and_v(or_c(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),v:multi(1,02c44d12c7065d812e8acf28d7cbb19f9011ecd9e9fdf281b0e6a3b5e87d22e7db)),pk_k(03acd484e2f0c7f65309ad178a9f559abde09796974c57e714c35f110dfc27ccbe))",
"c:and_v(or_c(multi(2,036d2b085e9e382ed10b69fc311a03f8641ccfff21574de0927513a49d9a688a00,02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),v:ripemd160(1b0f3c404d12075c68c938f9f60ebea4f74941a0)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))",
"and_v(andor(hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),v:hash256(939894f70e6c3a25da75da0cc2071b4076d9b006563cf635986ada2e93c0d735),v:older(50000)),after(499999999))",
"andor(hash256(5f8d30e655a7ba0d7596bb3ddfb1d2d20390d23b1845000e1e118b3be1b3f040),j:and_v(v:hash160(3a2bff0da9d96868e66abc4427bea4691cf61ccd),older(4194305)),ripemd160(44d90e2d3714c8663b632fcf0f9d5f22192cc4c8))",
"or_i(c:and_v(v:after(500000),pk_k(02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5)),sha256(d9147961436944f43cd99d28b2bbddbf452ef872b30c8279e255e7daafc7f946))",
"thresh(2,c:pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),s:sha256(e38990d0c7fc009880a9c07c23842e886c6bbdc964ce6bdd5817ad357335ee6f),a:hash160(dd69735817e0e3f6f826a9238dc2e291184f0131))",
"and_n(sha256(9267d3dbed802941483f1afa2a6bc68de5f653128aca9bf1461c5d0a3ad36ed2),uc:and_v(v:older(144),pk_k(03fe72c435413d33d48ac09c9161ba8b09683215439d62b7940502bda8b202e6ce)))",
"and_n(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_b(l:older(4252898),a:older(16)))",
"c:or_i(and_v(v:older(16),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729)),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5))",
"or_d(c:pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),andor(c:pk_k(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),older(2016),after(1567547623)))",
"c:andor(ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),and_v(v:hash256(8a35d9ca92a48eaade6f53a64985e9e2afeb74dcf8acb4c3721e0dc7e4294b25),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5)))",
"c:andor(u:ripemd160(6ad07d21fd5dfc646f0b30577045ce201616b9ba),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729),or_i(pk_h(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5)))",
"c:or_i(andor(c:pk_h(02352bbf4a4cdd12564f93fa332ce333301d9ad40271f8107181340aef25be59d5),pk_h(024ce119c96e2fa357200b559b2f7dd5a5f02d5290aff74b03f3e471b273211c97),pk_h(03daed4f2be3a8bf278e70132fb0beb7522f570e144bf615c07e996d443dee8729)),pk_k(03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))",
];
for s in &SET {
println!("{}", s);
let ms =
Miniscript::<bitcoin::PublicKey, Segwitv0>::from_str_insane(s)
.unwrap();
test_object_encoding_roundtrip(&ms).unwrap();
}
}
#[test]
fn test_descriptor() {
const SET: [&str; 16] = [
"pk(020000000000000000000000000000000000000000000000000000000000000002)",
"multi(1,020000000000000000000000000000000000000000000000000000000000000002)",
"pkh(020000000000000000000000000000000000000000000000000000000000000002)",
"wsh(c:pk_k(020000000000000000000000000000000000000000000000000000000000000002))",
"sh(wsh(c:pk_k(020000000000000000000000000000000000000000000000000000000000000002)))",
"wsh(after(1000))",
"wsh(older(1000))",
"wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357)",
"sh(wpkh(03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873))",
"wsh(multi(2,03789ed0bb717d88f7d321a368d905e7430207ebbd82bd342cf11ae157a7ace5fd,03dbc6764b8884a92e871274b87583e6d5c2a58819473e17e107ef3f6aa5a61626))",
"sh(sortedmulti(1,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556,0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352))#uetvewm2",
"wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH))#7etm7zk7",
"sh(wsh(sortedmulti(1,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*)))#u60cee0u",
"wpkh(tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/44'/0'/0'/0/*)",
"wpkh([2cbe2a6d/44'/0'/0']tpubDCvNhURocXGZsLNqWcqD3syHTqPXrMSTwi8feKVwAcpi29oYKsDD3Vex7x2TDneKMVN23RbLprfxB69v94iYqdaYHsVz3kPR37NQXeqouVz/0/*)#nhdxg96s",
"sh(multi(2,[00000000/111'/222]xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc,xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L/0))#ggrsrxfy",
];
let secp = bitcoin::secp256k1::Secp256k1::new();
for s in SET {
let (descr, _) = Descriptor::parse_descriptor(&secp, s).unwrap();
test_object_encoding_roundtrip(&descr).unwrap();
}
}
}