1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Miniscript and Output Descriptors
// 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/>.
//

//! Miniscript
//!
//! Blurb blurb blurb
//! cf https://gist.github.com/sipa/b7eec358de29d8e54c74e811820ed662
//! cf https://gist.github.com/sipa/e3d23d498c430bb601c5bca83523fa82
//!

#![cfg_attr(all(test, feature = "unstable"), feature(test))]
#[cfg(all(test, feature = "unstable"))] extern crate test;

#[cfg(feature="compiler")]
extern crate arrayvec;
extern crate bitcoin;
extern crate bitcoin_hashes;
extern crate secp256k1;
#[cfg(feature="serde")] extern crate serde;

pub mod miniscript;
pub mod descriptor;
pub mod expression;
pub mod policy;
pub mod psbt;

use std::{error, fmt, str};

use bitcoin::blockdata::{opcodes, script};
use bitcoin_hashes::sha256;

pub use miniscript::astelem::AstElem;
pub use descriptor::Descriptor;
pub use miniscript::Miniscript;
pub use policy::AbstractPolicy;
pub use policy::Policy;

/// Fully-typed `None` value to give to satisfaction functions when there is no hash preimages
pub static NO_HASHES: Option<&'static fn(sha256::Hash) -> Option<[u8; 32]>> = None;

/// Trait describing public key types which can be converted to bitcoin pubkeys
pub trait ToPublicKey {
    /// Converts an object to a public key
    fn to_public_key(&self) -> bitcoin::PublicKey;
}

impl ToPublicKey for bitcoin::PublicKey {
    fn to_public_key(&self) -> bitcoin::PublicKey { *self }
}

/// Dummy key which de/serializes to the empty string; useful sometimes for testing
#[derive(Copy, Clone, Debug)]
pub struct DummyKey;

impl str::FromStr for DummyKey {
    type Err = &'static str;
    fn from_str(x: &str) -> Result<DummyKey, &'static str> {
        if x.is_empty() {
            Ok(DummyKey)
        } else {
            Err("non empty dummy key")
        }
    }
}

impl fmt::Display for DummyKey {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("")
    }
}

/// Script Descriptor error
#[derive(Debug)]
pub enum Error {
    /// Opcode appeared which is not part of the script subset
    InvalidOpcode(opcodes::All),
    /// Push was illegal in some context
    InvalidPush(Vec<u8>),
    /// PSBT-related error
    Psbt(psbt::Error),
    /// rust-bitcoin script error
    Script(script::Error),
    /// Encountered unprintable character in descriptor
    Unprintable(u8),
    /// expected character while parsing descriptor; didn't find one
    ExpectedChar(char),
    /// While parsing backward, hit beginning of script
    UnexpectedStart,
    /// Got something we were not expecting
    Unexpected(String),
    /// Failed to parse a push as a public key
    BadPubkey(bitcoin::consensus::encode::Error),
    /// Could not satisfy a script (fragment) because of a missing hash preimage
    MissingHash(sha256::Hash),
    /// Could not satisfy a script (fragment) because of a missing signature
    MissingSig(bitcoin::PublicKey),
    /// Could not satisfy, locktime not met
    LocktimeNotMet(u32),
    /// General failure to satisfy
    CouldNotSatisfy
}

fn errstr(s: &str) -> Error {
    Error::Unexpected(s.to_owned())
}

impl error::Error for Error {
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::BadPubkey(ref e) => Some(e),
            Error::Psbt(ref e) => Some(e),
            _ => None,
        }
    }

    fn description(&self) -> &str {
        ""
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::InvalidOpcode(ref op) => write!(f, "invalid opcode {}", op),
            Error::InvalidPush(ref push) => write!(f, "invalid push {:?}", push), // TODO hexify this
            Error::Psbt(ref e) => fmt::Display::fmt(e, f),
            Error::Script(ref e) => fmt::Display::fmt(e, f),
            Error::Unprintable(x) => write!(f, "unprintable character 0x{:02x}", x),
            Error::ExpectedChar(c) => write!(f, "expected {}", c),
            Error::UnexpectedStart => f.write_str("unexpected start of script"),
            Error::Unexpected(ref s) => write!(f, "unexpected «{}»", s),
            Error::MissingHash(ref h) => write!(f, "missing preimage of hash {}", h),
            Error::MissingSig(ref pk) => write!(f, "missing signature for key {:?}", pk),
            Error::LocktimeNotMet(n) => write!(f, "required locktime of {} blocks, not met", n),
            Error::CouldNotSatisfy => f.write_str("could not satisfy"),
            Error::BadPubkey(ref e) => fmt::Display::fmt(e, f),
        }
    }
}

#[doc(hidden)]
impl From<psbt::Error> for Error {
    fn from(e: psbt::Error) -> Error {
        Error::Psbt(e)
    }
}