#![cfg_attr(miniscript_bench, feature(test))]
#![allow(clippy::manual_range_contains)] #![allow(clippy::type_complexity)]
#[cfg(target_pointer_width = "16")]
compile_error!(
"elements-miniscript currently only supports architectures with pointers wider than 16 bits"
);
#[cfg(feature = "serde")]
pub use actual_serde as serde;
pub use {bitcoin, elements};
#[cfg(miniscript_bench)]
extern crate test;
use bitcoin_miniscript::expression::{FromTree as BtcFromTree, Tree as BtcTree};
use bitcoin_miniscript::policy::semantic::Policy as BtcPolicy;
use bitcoin_miniscript::policy::Liftable as BtcLiftable;
pub use bitcoin_miniscript::{hash256, ForEachKey, MiniscriptKey, SigType, ToPublicKey};
pub use bitcoin_miniscript::{
Descriptor as BtcDescriptor, Error as BtcError, Miniscript as BtcMiniscript,
Satisfier as BtcSatisfier, Segwitv0 as BtcSegwitv0, Terminal as BtcTerminal,
};
#[macro_use]
mod macros;
#[macro_use]
mod pub_macros;
pub mod confidential;
pub mod descriptor;
pub mod expression;
pub mod extensions;
pub mod interpreter;
pub mod miniscript;
pub mod policy;
pub mod psbt;
#[cfg(feature = "simplicity")]
mod simplicity;
#[cfg(test)]
mod test_utils;
mod util;
use std::{cmp, error, fmt, str};
use elements::hashes::sha256;
use elements::secp256k1_zkp::Secp256k1;
use elements::{locktime, opcodes, script, secp256k1_zkp};
pub use crate::confidential::slip77;
pub use crate::confidential::Descriptor as ConfidentialDescriptor;
pub use crate::descriptor::{DefiniteDescriptorKey, Descriptor, DescriptorPublicKey};
pub use crate::extensions::{CovenantExt, Extension, NoExt, TxEnv};
pub use crate::interpreter::Interpreter;
pub use crate::miniscript::analyzable::{AnalysisError, ExtParams};
pub use crate::miniscript::context::{BareCtx, Legacy, ScriptContext, Segwitv0, Tap};
pub use crate::miniscript::decode::Terminal;
pub use crate::miniscript::satisfy::{
elementssig_from_rawsig, elementssig_to_rawsig, ElementsSig, Preimage32, Satisfier,
};
pub use crate::miniscript::Miniscript;
mod contracthash {
use bitcoin::secp256k1::Scalar;
use bitcoin::PublicKey;
use elements::hashes::{sha256, Hash, HashEngine, Hmac, HmacEngine};
use elements::secp256k1_zkp::{self, Secp256k1};
pub(super) fn tweak_key<C: secp256k1_zkp::Verification>(
secp: &Secp256k1<C>,
key: PublicKey,
contract: &[u8],
) -> PublicKey {
let hmac_result = compute_tweak(&key, contract);
let secp_key = key
.inner
.add_exp_tweak(
secp,
&Scalar::from_be_bytes(hmac_result.to_byte_array())
.expect("Result of hash must be a valid point"),
)
.expect("HMAC cannot produce invalid tweak");
bitcoin::PublicKey::new(secp_key)
}
fn compute_tweak(pk: &PublicKey, contract: &[u8]) -> Hmac<sha256::Hash> {
let mut hmac_engine: HmacEngine<sha256::Hash> = if pk.compressed {
HmacEngine::new(&pk.inner.serialize())
} else {
HmacEngine::new(&pk.inner.serialize_uncompressed())
};
hmac_engine.input(contract);
Hmac::from_engine(hmac_engine)
}
}
pub fn tweak_key<Pk, C: secp256k1_zkp::Verification>(
pk: &Pk,
secp: &Secp256k1<C>,
contract: &[u8],
) -> bitcoin::PublicKey
where
Pk: MiniscriptKey + ToPublicKey,
{
let pk = pk.to_public_key();
contracthash::tweak_key(secp, pk, contract)
}
pub trait Translator<P, Q, E>
where
P: MiniscriptKey,
Q: MiniscriptKey,
{
fn pk(&mut self, pk: &P) -> Result<Q, E>;
fn sha256(&mut self, sha256: &P::Sha256) -> Result<Q::Sha256, E>;
fn hash256(&mut self, hash256: &P::Hash256) -> Result<Q::Hash256, E>;
fn ripemd160(&mut self, ripemd160: &P::Ripemd160) -> Result<Q::Ripemd160, E>;
fn hash160(&mut self, hash160: &P::Hash160) -> Result<Q::Hash160, E>;
}
pub trait ExtTranslator<PExt, QExt, E>
where
PExt: Extension,
QExt: Extension,
{
fn ext(&mut self, e: &PExt) -> Result<QExt, E>;
}
pub trait TranslatePk<P, Q>
where
P: MiniscriptKey,
Q: MiniscriptKey,
{
type Output;
fn translate_pk<T, E>(&self, translator: &mut T) -> Result<Self::Output, E>
where
T: Translator<P, Q, E>;
}
pub trait TranslateExt<PExt, QExt>
where
PExt: Extension,
QExt: Extension,
{
type Output;
fn translate_ext<T, E>(&self, translator: &mut T) -> Result<Self::Output, E>
where
T: ExtTranslator<PExt, QExt, E>;
}
#[derive(Debug, PartialEq)]
pub enum Error {
InvalidOpcode(opcodes::All),
NonMinimalVerify(String),
InvalidPush(Vec<u8>),
Script(script::Error),
AddrError(bitcoin::address::ParseError),
CmsTooManyKeys(u32),
MultiATooManyKeys(u32),
ExpectedChar(char),
UnexpectedStart,
Unexpected(String),
MultiColon(String),
MultiAt(String),
AtOutsideOr(String),
LikelyFalse,
UnknownWrapper(char),
NonTopLevel(String),
Trailing(String),
BadPubkey(bitcoin::key::ParsePublicKeyError),
BadPubkeySlice(bitcoin::key::FromSliceError),
MissingHash(sha256::Hash),
MissingSig(bitcoin::PublicKey),
RelativeLocktimeNotMet(u32),
AbsoluteLocktimeNotMet(u32),
CouldNotSatisfy,
TypeCheck(String),
BadDescriptor(String),
Secp(elements::secp256k1_zkp::Error),
#[cfg(feature = "compiler")]
CompilerError(policy::compiler::CompilerError),
PolicyError(policy::concrete::PolicyError),
LiftError(policy::LiftError),
ContextError(miniscript::context::ScriptContextError),
MaxRecursiveDepthExceeded,
ScriptSizeTooLarge,
NonStandardBareScript,
AnalysisError(miniscript::analyzable::AnalysisError),
ImpossibleSatisfaction,
BareDescriptorAddr,
BtcError(bitcoin_miniscript::Error),
CovError(descriptor::CovError),
PubKeyCtxError(miniscript::decode::KeyParseError, &'static str),
TaprootSpendInfoUnavialable,
TrNoScriptCode,
TrNoExplicitScript,
MultipathDescLenMismatch,
}
#[doc(hidden)]
impl<Pk, Ctx, Ext> From<miniscript::types::Error<Pk, Ctx, Ext>> for Error
where
Pk: MiniscriptKey,
Ctx: ScriptContext,
Ext: Extension,
{
fn from(e: miniscript::types::Error<Pk, Ctx, Ext>) -> Error {
Error::TypeCheck(e.to_string())
}
}
#[doc(hidden)]
impl From<bitcoin_miniscript::Error> for Error {
fn from(e: bitcoin_miniscript::Error) -> Error {
Error::BtcError(e)
}
}
#[doc(hidden)]
impl From<policy::LiftError> for Error {
fn from(e: policy::LiftError) -> Error {
Error::LiftError(e)
}
}
#[doc(hidden)]
impl From<miniscript::context::ScriptContextError> for Error {
fn from(e: miniscript::context::ScriptContextError) -> Error {
Error::ContextError(e)
}
}
#[doc(hidden)]
impl From<miniscript::analyzable::AnalysisError> for Error {
fn from(e: miniscript::analyzable::AnalysisError) -> Error {
Error::AnalysisError(e)
}
}
#[doc(hidden)]
impl From<elements::secp256k1_zkp::Error> for Error {
fn from(e: elements::secp256k1_zkp::Error) -> Error {
Error::Secp(e)
}
}
#[doc(hidden)]
impl From<elements::secp256k1_zkp::UpstreamError> for Error {
fn from(e: elements::secp256k1_zkp::UpstreamError) -> Error {
Error::Secp(elements::secp256k1_zkp::Error::Upstream(e))
}
}
#[doc(hidden)]
impl From<bitcoin::key::ParsePublicKeyError> for Error {
fn from(e: bitcoin::key::ParsePublicKeyError) -> Error {
Error::BadPubkey(e)
}
}
#[doc(hidden)]
impl From<bitcoin::key::FromSliceError> for Error {
fn from(e: bitcoin::key::FromSliceError) -> Error {
Error::BadPubkeySlice(e)
}
}
impl From<bitcoin::address::ParseError> for Error {
fn from(e: bitcoin::address::ParseError) -> Error {
Error::AddrError(e)
}
}
fn errstr(s: &str) -> Error {
Error::Unexpected(s.to_owned())
}
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(ref tok) => write!(f, "{} VERIFY", tok),
Error::InvalidPush(ref push) => {
write!(f, "invalid push ")?;
elements::hex::format_hex(push, f)
},
Error::Script(ref e) => fmt::Display::fmt(e, f),
Error::AddrError(ref e) => fmt::Display::fmt(e, f),
Error::CmsTooManyKeys(n) => write!(f, "checkmultisig with {} keys", n),
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::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::BadPubkeySlice(ref e) => fmt::Display::fmt(e, f),
Error::TypeCheck(ref e) => write!(f, "typecheck: {}", e),
Error::BadDescriptor(ref e) => write!(f, "Invalid descriptor: {}", e),
Error::Secp(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::LiftError(ref e) => fmt::Display::fmt(e, f),
Error::MaxRecursiveDepthExceeded => write!(
f,
"Recursive depth over {} not permitted",
MAX_RECURSION_DEPTH
),
Error::ScriptSizeTooLarge => write!(
f,
"Standardness rules imply bitcoin than {} bytes",
MAX_SCRIPT_SIZE
),
Error::NonStandardBareScript => write!(
f,
"Anything but c:pk(key) (P2PK), c:pk_h(key) (P2PKH), and thresh_m(k,...) \
up to n=3 is invalid by standardness (bare).
"
),
Error::AnalysisError(ref e) => e.fmt(f),
Error::ImpossibleSatisfaction => write!(f, "Impossible to satisfy Miniscript"),
Error::BareDescriptorAddr => write!(f, "Bare descriptors don't have address"),
Error::BtcError(ref e) => write!(f, " Bitcoin Miniscript Error {}", e),
Error::CovError(ref e) => write!(f, "Covenant Error: {}", e),
Error::PubKeyCtxError(ref pk, ref ctx) => {
write!(f, "Pubkey error: {} under {} scriptcontext", pk, ctx)
}
Error::MultiATooManyKeys(k) => write!(f, "MultiA too many keys {}", k),
Error::TaprootSpendInfoUnavialable => write!(f, "Taproot Spend Info not computed."),
Error::TrNoScriptCode => write!(f, "No script code for Tr descriptors"),
Error::TrNoExplicitScript => write!(f, "No script code for Tr descriptors"),
Error::MultipathDescLenMismatch => write!(f, "At least two BIP389 key expressions in the descriptor contain tuples of derivation indexes of different lengths"),
}
}
}
impl error::Error for Error {
fn cause(&self) -> Option<&dyn error::Error> {
use self::Error::*;
match self {
InvalidOpcode(_)
| NonMinimalVerify(_)
| InvalidPush(_)
| CmsTooManyKeys(_)
| MultiATooManyKeys(_)
| ExpectedChar(_)
| UnexpectedStart
| Unexpected(_)
| MultiColon(_)
| MultiAt(_)
| AtOutsideOr(_)
| LikelyFalse
| UnknownWrapper(_)
| NonTopLevel(_)
| Trailing(_)
| MissingHash(_)
| MissingSig(_)
| RelativeLocktimeNotMet(_)
| AbsoluteLocktimeNotMet(_)
| CouldNotSatisfy
| TypeCheck(_)
| BadDescriptor(_)
| MaxRecursiveDepthExceeded
| ScriptSizeTooLarge
| NonStandardBareScript
| ImpossibleSatisfaction
| BareDescriptorAddr
| TaprootSpendInfoUnavialable
| TrNoScriptCode
| TrNoExplicitScript => None,
MultipathDescLenMismatch => None,
BtcError(e) => Some(e),
CovError(e) => Some(e),
Script(_e) => None, AddrError(e) => Some(e),
BadPubkey(e) => Some(e),
BadPubkeySlice(e) => Some(e),
Secp(e) => Some(e),
#[cfg(feature = "compiler")]
CompilerError(e) => Some(e),
PolicyError(e) => Some(e),
LiftError(e) => Some(e),
ContextError(e) => Some(e),
AnalysisError(e) => Some(e),
PubKeyCtxError(e, _) => Some(e),
}
}
}
#[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)
}
}
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, }
}
fn push_opcode_size(script_size: usize) -> usize {
if script_size < 76 {
1
} else if script_size < 0x100 {
2
} else if script_size < 0x10000 {
3
} else {
5
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AbsLockTime(locktime::LockTime);
impl AbsLockTime {
pub fn from_consensus(n: u32) -> Self {
Self(locktime::LockTime::from_consensus(n))
}
pub fn to_consensus_u32(self) -> u32 {
self.0.to_consensus_u32()
}
pub fn to_u32(self) -> u32 {
self.to_consensus_u32()
}
}
impl From<locktime::LockTime> for AbsLockTime {
fn from(lock_time: locktime::LockTime) -> Self {
Self(lock_time)
}
}
impl From<AbsLockTime> for locktime::LockTime {
fn from(lock_time: AbsLockTime) -> locktime::LockTime {
lock_time.0
}
}
impl cmp::PartialOrd for AbsLockTime {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl cmp::Ord for AbsLockTime {
fn cmp(&self, other: &Self) -> cmp::Ordering {
let this = self.0.to_consensus_u32();
let that = other.0.to_consensus_u32();
this.cmp(&that)
}
}
impl fmt::Display for AbsLockTime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[cfg(test)]
fn hex_script(s: &str) -> elements::Script {
let v: Vec<u8> = elements::hex::FromHex::from_hex(s).unwrap();
elements::Script::from(v)
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use bitcoin::hashes::hash160;
use super::*;
#[test]
fn regression_bitcoin_key_hash() {
use bitcoin::PublicKey;
let pk = PublicKey::from_str(
"042e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af191923a2964c177f5b5923ae500fca49e99492d534aa3759d6b25a8bc971b133"
).unwrap();
let want = hash160::Hash::from_str("ac2e7daf42d2c97418fd9f78af2de552bb9c6a7a").unwrap();
let got = pk.to_pubkeyhash(SigType::Ecdsa);
assert_eq!(got, want)
}
#[test]
fn regression_secp256k1_key_hash() {
use bitcoin::secp256k1::PublicKey;
let pk = PublicKey::from_str(
"032e58afe51f9ed8ad3cc7897f634d881fdbe49a81564629ded8156bebd2ffd1af",
)
.unwrap();
let want = hash160::Hash::from_str("9511aa27ef39bbfa4e4f3dd15f4d66ea57f475b4").unwrap();
let got = pk.to_pubkeyhash(SigType::Ecdsa);
assert_eq!(got, want)
}
#[test]
fn regression_xonly_key_hash() {
use bitcoin::secp256k1::XOnlyPublicKey;
let pk = XOnlyPublicKey::from_str(
"cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115",
)
.unwrap();
let want = hash160::Hash::from_str("eb8ac65f971ae688a94aeabf223506865e7e08f2").unwrap();
let got = pk.to_pubkeyhash(SigType::Schnorr);
assert_eq!(got, want)
}
}