#![allow(bare_trait_objects)]
#![cfg_attr(all(test, feature = "unstable"), feature(test))]
pub extern crate bitcoin;
#[cfg(feature = "serde")]
pub extern crate serde;
#[cfg(all(test, feature = "unstable"))]
extern crate test;
#[macro_use]
mod macros;
pub mod descriptor;
pub mod expression;
pub mod miniscript;
pub mod policy;
pub mod psbt;
use std::str::FromStr;
use std::{error, fmt, hash, str};
use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::{hash160, sha256, Hash};
pub use descriptor::{Descriptor, SatisfiedConstraints};
pub use miniscript::context::{Legacy, ScriptContext, Segwitv0};
pub use miniscript::decode::Terminal;
pub use miniscript::satisfy::{BitcoinSig, Satisfier};
pub use miniscript::Miniscript;
pub trait MiniscriptKey:
Clone + Eq + Ord + str::FromStr + fmt::Debug + fmt::Display + hash::Hash
{
fn is_uncompressed(&self) -> bool {
false
}
type Hash: Clone + Eq + Ord + str::FromStr + fmt::Display + fmt::Debug + hash::Hash;
fn to_pubkeyhash(&self) -> Self::Hash;
}
impl MiniscriptKey for bitcoin::PublicKey {
fn is_uncompressed(&self) -> bool {
!self.compressed
}
type Hash = hash160::Hash;
fn to_pubkeyhash(&self) -> Self::Hash {
let mut engine = hash160::Hash::engine();
self.write_into(&mut engine);
hash160::Hash::from_engine(engine)
}
}
impl MiniscriptKey for String {
type Hash = String;
fn to_pubkeyhash(&self) -> Self::Hash {
format!("{}", &self)
}
}
pub trait ToPublicKey: MiniscriptKey {
fn to_public_key(&self) -> bitcoin::PublicKey;
fn serialized_len(&self) -> usize {
if self.to_public_key().compressed {
34
} else {
66
}
}
fn hash_to_hash160(hash: &<Self as MiniscriptKey>::Hash) -> hash160::Hash;
}
impl ToPublicKey for bitcoin::PublicKey {
fn to_public_key(&self) -> bitcoin::PublicKey {
*self
}
fn hash_to_hash160(hash: &hash160::Hash) -> hash160::Hash {
*hash
}
}
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, 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 MiniscriptKey for DummyKey {
type Hash = DummyKeyHash;
fn to_pubkeyhash(&self) -> Self::Hash {
DummyKeyHash
}
}
impl hash::Hash for DummyKey {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
"DummyKey".hash(state);
}
}
impl fmt::Display for DummyKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("")
}
}
impl ToPublicKey for DummyKey {
fn to_public_key(&self) -> bitcoin::PublicKey {
bitcoin::PublicKey::from_str(
"0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352",
)
.unwrap()
}
fn hash_to_hash160(_: &DummyKeyHash) -> hash160::Hash {
hash160::Hash::from_str("f54a5851e9372b87810a8e60cdd2e7cfd80b6e31").unwrap()
}
}
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
pub struct DummyKeyHash;
impl str::FromStr for DummyKeyHash {
type Err = &'static str;
fn from_str(x: &str) -> Result<DummyKeyHash, &'static str> {
if x.is_empty() {
Ok(DummyKeyHash)
} else {
Err("non empty dummy key")
}
}
}
impl fmt::Display for DummyKeyHash {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("")
}
}
impl hash::Hash for DummyKeyHash {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
"DummyKeyHash".hash(state);
}
}
#[derive(Debug)]
pub enum Error {
InvalidOpcode(opcodes::All),
NonMinimalVerify(miniscript::lex::Token),
InvalidPush(Vec<u8>),
Script(script::Error),
CmsTooManyKeys(u32),
Unprintable(u8),
ExpectedChar(char),
UnexpectedStart,
Unexpected(String),
MultiColon(String),
MultiAt(String),
AtOutsideOr(String),
NonCanonicalTrue,
NonCanonicalFalse,
LikelyFalse,
UnknownWrapper(char),
NonTopLevel(String),
Trailing(String),
BadPubkey(bitcoin::util::key::Error),
MissingHash(sha256::Hash),
MissingSig(bitcoin::PublicKey),
RelativeLocktimeNotMet(u32),
AbsoluteLocktimeNotMet(u32),
CouldNotSatisfy,
TypeCheck(String),
BadDescriptor,
Secp(bitcoin::secp256k1::Error),
#[cfg(feature = "compiler")]
CompilerError(policy::compiler::CompilerError),
PolicyError(policy::concrete::PolicyError),
InterpreterError(descriptor::InterpreterError),
ContextError(miniscript::context::ScriptContextError),
BadScriptSig,
NonEmptyWitness,
NonEmptyScriptSig,
IncorrectPubkeyHash,
IncorrectScriptHash,
MaxRecursiveDepthExceeded,
ScriptSizeTooLarge,
}
#[doc(hidden)]
impl<Pk, Ctx> From<miniscript::types::Error<Pk, Ctx>> for Error
where
Pk: MiniscriptKey,
Ctx: ScriptContext,
{
fn from(e: miniscript::types::Error<Pk, Ctx>) -> Error {
Error::TypeCheck(e.to_string())
}
}
#[doc(hidden)]
impl From<miniscript::context::ScriptContextError> for Error {
fn from(e: miniscript::context::ScriptContextError) -> Error {
Error::ContextError(e)
}
}
#[doc(hidden)]
impl From<bitcoin::secp256k1::Error> for Error {
fn from(e: bitcoin::secp256k1::Error) -> Error {
Error::Secp(e)
}
}
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),
_ => None,
}
}
fn description(&self) -> &str {
""
}
}
const MAX_RECURSION_DEPTH: u32 = 402;
const MAX_SCRIPT_SIZE: u32 = 10000;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::InvalidOpcode(op) => write!(f, "invalid opcode {}", op),
Error::NonMinimalVerify(tok) => write!(f, "{} VERIFY", tok),
Error::InvalidPush(ref push) => write!(f, "invalid push {:?}", push),
Error::Script(ref e) => fmt::Display::fmt(e, f),
Error::CmsTooManyKeys(n) => write!(f, "checkmultisig with {} keys", n),
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::MultiColon(ref s) => write!(f, "«{}» has multiple instances of «:»", s),
Error::MultiAt(ref s) => write!(f, "«{}» has multiple instances of «@»", s),
Error::AtOutsideOr(ref s) => write!(f, "«{}» contains «@» in non-or() context", s),
Error::NonCanonicalTrue => f.write_str("Use «t:X» rather than «and_v(X,true())»"),
Error::NonCanonicalFalse => {
f.write_str("Use «u:X» «l:X» rather than «or_i(X,false)» «or_i(false,X)»")
}
Error::LikelyFalse => write!(f, "0 is not very likely (use «u:0»)"),
Error::UnknownWrapper(ch) => write!(f, "unknown wrapper «{}:»", ch),
Error::NonTopLevel(ref s) => write!(f, "non-T miniscript: {}", s),
Error::Trailing(ref s) => write!(f, "trailing tokens: {}", s),
Error::MissingHash(ref h) => write!(f, "missing preimage of hash {}", h),
Error::MissingSig(ref pk) => write!(f, "missing signature for key {:?}", pk),
Error::RelativeLocktimeNotMet(n) => {
write!(f, "required relative locktime CSV of {} blocks, not met", n)
}
Error::AbsoluteLocktimeNotMet(n) => write!(
f,
"required absolute locktime CLTV of {} blocks, not met",
n
),
Error::CouldNotSatisfy => f.write_str("could not satisfy"),
Error::BadPubkey(ref e) => fmt::Display::fmt(e, f),
Error::TypeCheck(ref e) => write!(f, "typecheck: {}", e),
Error::BadDescriptor => f.write_str("could not create a descriptor"),
Error::Secp(ref e) => fmt::Display::fmt(e, f),
Error::InterpreterError(ref e) => fmt::Display::fmt(e, f),
Error::ContextError(ref e) => fmt::Display::fmt(e, f),
#[cfg(feature = "compiler")]
Error::CompilerError(ref e) => fmt::Display::fmt(e, f),
Error::PolicyError(ref e) => fmt::Display::fmt(e, f),
Error::BadScriptSig => f.write_str("Script sig must only consist of pushes"),
Error::NonEmptyWitness => f.write_str("Non empty witness for Pk/Pkh"),
Error::NonEmptyScriptSig => f.write_str("Non empty script sig for segwit spend"),
Error::IncorrectScriptHash => {
f.write_str("Incorrect script hash for redeem script sh/wsh")
}
Error::IncorrectPubkeyHash => {
f.write_str("Incorrect pubkey hash for given descriptor pkh/wpkh")
}
Error::MaxRecursiveDepthExceeded => write!(
f,
"Recusive depth over {} not permitted",
MAX_RECURSION_DEPTH
),
Error::ScriptSizeTooLarge => write!(
f,
"Standardness rules imply bitcoin than {} bytes",
MAX_SCRIPT_SIZE
),
}
}
}
#[doc(hidden)]
#[cfg(feature = "compiler")]
impl From<policy::compiler::CompilerError> for Error {
fn from(e: policy::compiler::CompilerError) -> Error {
Error::CompilerError(e)
}
}
#[doc(hidden)]
impl From<policy::concrete::PolicyError> for Error {
fn from(e: policy::concrete::PolicyError) -> Error {
Error::PolicyError(e)
}
}
#[doc(hidden)]
impl From<descriptor::InterpreterError> for Error {
fn from(e: descriptor::InterpreterError) -> Error {
Error::InterpreterError(e)
}
}
pub fn script_num_size(n: usize) -> usize {
match n {
n if n <= 0x10 => 1,
n if n < 0x80 => 2,
n if n < 0x8000 => 3,
n if n < 0x800000 => 4,
n if n < 0x80000000 => 5,
_ => 6,
}
}
#[cfg(test)]
fn hex_script(s: &str) -> bitcoin::Script {
let v: Vec<u8> = bitcoin::hashes::hex::FromHex::from_hex(s).unwrap();
bitcoin::Script::from(v)
}