use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use {bitcoin, Miniscript};
use miniscript::lex::{Token as Tk, TokenIter};
use miniscript::types::extra_props::ExtData;
use miniscript::types::Property;
use miniscript::types::Type;
use std::sync::Arc;
use Error;
use MiniscriptKey;
fn return_none<T>(_: usize) -> Option<T> {
None
}
#[derive(Copy, Clone, Debug)]
enum NonTerm {
Expression,
MaybeSwap,
MaybeAndV,
Alt,
Check,
DupIf,
Verify,
NonZero,
ZeroNotEqual,
AndV,
AndB,
Tern,
OrB,
OrD,
OrC,
ThreshW { k: usize, n: usize },
ThreshE { k: usize, n: usize },
EndIf,
EndIfNotIf,
EndIfElse,
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Terminal<Pk: MiniscriptKey> {
True,
False,
Pk(Pk),
PkH(Pk::Hash),
After(u32),
Older(u32),
Sha256(sha256::Hash),
Hash256(sha256d::Hash),
Ripemd160(ripemd160::Hash),
Hash160(hash160::Hash),
Alt(Arc<Miniscript<Pk>>),
Swap(Arc<Miniscript<Pk>>),
Check(Arc<Miniscript<Pk>>),
DupIf(Arc<Miniscript<Pk>>),
Verify(Arc<Miniscript<Pk>>),
NonZero(Arc<Miniscript<Pk>>),
ZeroNotEqual(Arc<Miniscript<Pk>>),
AndV(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>),
AndB(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>),
AndOr(
Arc<Miniscript<Pk>>,
Arc<Miniscript<Pk>>,
Arc<Miniscript<Pk>>,
),
OrB(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>),
OrD(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>),
OrC(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>),
OrI(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>),
Thresh(usize, Vec<Arc<Miniscript<Pk>>>),
ThreshM(usize, Vec<Pk>),
}
macro_rules! match_token {
($tokens:expr => $sub:expr,) => { $sub };
($tokens:expr, $($first:pat $(,$rest:pat)* => $sub:expr,)*) => {
match $tokens.next() {
$(
Some($first) => match_token!($tokens $(,$rest)* => $sub,),
)*
Some(other) => return Err(Error::Unexpected(other.to_string())),
None => return Err(Error::UnexpectedStart),
}
};
}
struct TerminalStack<Pk: MiniscriptKey>(Vec<Miniscript<Pk>>);
impl<Pk: MiniscriptKey> TerminalStack<Pk> {
fn pop(&mut self) -> Option<Miniscript<Pk>> {
self.0.pop()
}
fn reduce0(&mut self, ms: Terminal<Pk>) -> Result<(), Error> {
let ty = Type::type_check(&ms, return_none)?;
let ext = ExtData::type_check(&ms, return_none)?;
self.0.push(Miniscript {
node: ms,
ty: ty,
ext: ext,
});
Ok(())
}
fn reduce1<F>(&mut self, wrap: F) -> Result<(), Error>
where
F: FnOnce(Arc<Miniscript<Pk>>) -> Terminal<Pk>,
{
let top = self.pop().unwrap();
let wrapped_ms = wrap(Arc::new(top));
let ty = Type::type_check(&wrapped_ms, return_none)?;
let ext = ExtData::type_check(&wrapped_ms, return_none)?;
self.0.push(Miniscript {
node: wrapped_ms,
ty: ty,
ext: ext,
});
Ok(())
}
fn reduce2<F>(&mut self, wrap: F) -> Result<(), Error>
where
F: FnOnce(Arc<Miniscript<Pk>>, Arc<Miniscript<Pk>>) -> Terminal<Pk>,
{
let left = self.pop().unwrap();
let right = self.pop().unwrap();
let wrapped_ms = wrap(Arc::new(left), Arc::new(right));
let ty = Type::type_check(&wrapped_ms, return_none)?;
let ext = ExtData::type_check(&wrapped_ms, return_none)?;
self.0.push(Miniscript {
node: wrapped_ms,
ty: ty,
ext: ext,
});
Ok(())
}
}
#[allow(unreachable_patterns)]
pub fn parse(tokens: &mut TokenIter) -> Result<Miniscript<bitcoin::PublicKey>, Error> {
let mut non_term = Vec::with_capacity(tokens.len());
let mut term = TerminalStack(Vec::with_capacity(tokens.len()));
non_term.push(NonTerm::MaybeAndV);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
loop {
match non_term.pop() {
Some(NonTerm::Expression) => {
match_token!(
tokens,
Tk::Pubkey(pk) => term.reduce0(Terminal::Pk(pk))?,
Tk::CheckSig => {
non_term.push(NonTerm::Check);
non_term.push(NonTerm::Expression);
},
Tk::Verify => match_token!(
tokens,
Tk::Equal, Tk::Hash20(hash), Tk::Hash160, Tk::Dup
=> term.reduce0(Terminal::PkH(
hash160::Hash::from_inner(hash)
))?,
x => {
tokens.un_next(x);
non_term.push(NonTerm::Verify);
non_term.push(NonTerm::Expression);
},
),
Tk::ZeroNotEqual => {
non_term.push(NonTerm::ZeroNotEqual);
non_term.push(NonTerm::Expression);
},
Tk::CheckSequenceVerify, Tk::Num(n)
=> term.reduce0(Terminal::Older(n))?,
Tk::CheckLockTimeVerify, Tk::Num(n)
=> term.reduce0(Terminal::After(n))?,
Tk::Equal => match_token!(
tokens,
Tk::Hash32(hash) => match_token!(
tokens,
Tk::Sha256,
Tk::Verify,
Tk::Equal,
Tk::Num(32),
Tk::Size => term.reduce0(Terminal::Sha256(
sha256::Hash::from_inner(hash)
))?,
Tk::Hash256,
Tk::Verify,
Tk::Equal,
Tk::Num(32),
Tk::Size => term.reduce0(Terminal::Hash256(
sha256d::Hash::from_inner(hash)
))?,
),
Tk::Hash20(hash) => match_token!(
tokens,
Tk::Ripemd160,
Tk::Verify,
Tk::Equal,
Tk::Num(32),
Tk::Size => term.reduce0(Terminal::Ripemd160(
ripemd160::Hash::from_inner(hash)
))?,
Tk::Hash160,
Tk::Verify,
Tk::Equal,
Tk::Num(32),
Tk::Size => term.reduce0(Terminal::Hash160(
hash160::Hash::from_inner(hash)
))?,
),
Tk::Num(k) => {
non_term.push(NonTerm::ThreshW {
k: k as usize,
n: 0
});
},
),
Tk::FromAltStack => {
non_term.push(NonTerm::Alt);
non_term.push(NonTerm::MaybeAndV);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
},
Tk::Num(0) => term.reduce0(Terminal::False)?,
Tk::Num(1) => term.reduce0(Terminal::True)?,
Tk::EndIf => {
non_term.push(NonTerm::EndIf);
non_term.push(NonTerm::MaybeAndV);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
},
Tk::BoolAnd => {
non_term.push(NonTerm::AndB);
non_term.push(NonTerm::Expression);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
},
Tk::BoolOr => {
non_term.push(NonTerm::OrB);
non_term.push(NonTerm::Expression);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
},
Tk::CheckMultiSig, Tk::Num(n) => {
if n > 20 {
return Err(Error::CmsTooManyKeys(n));
}
let mut keys = Vec::with_capacity(n as usize);
for _ in 0..n {
match_token!(
tokens,
Tk::Pubkey(pk) => keys.push(pk),
);
}
let k = match_token!(
tokens,
Tk::Num(k) => k,
);
keys.reverse();
term.reduce0(Terminal::ThreshM(k as usize, keys))?;
},
);
}
Some(NonTerm::MaybeAndV) => {
match tokens.peek() {
None
| Some(&Tk::If)
| Some(&Tk::NotIf)
| Some(&Tk::Else)
| Some(&Tk::ToAltStack) => {}
_ => {
non_term.push(NonTerm::AndV);
non_term.push(NonTerm::Expression);
}
}
}
Some(NonTerm::MaybeSwap) => {
if let Some(&Tk::Swap) = tokens.peek() {
tokens.next();
term.reduce1(Terminal::Swap)?;
non_term.push(NonTerm::MaybeSwap);
}
}
Some(NonTerm::Alt) => {
match_token!(
tokens,
Tk::ToAltStack => {},
);
term.reduce1(Terminal::Alt)?;
}
Some(NonTerm::Check) => term.reduce1(Terminal::Check)?,
Some(NonTerm::DupIf) => term.reduce1(Terminal::DupIf)?,
Some(NonTerm::Verify) => term.reduce1(Terminal::Verify)?,
Some(NonTerm::NonZero) => term.reduce1(Terminal::NonZero)?,
Some(NonTerm::ZeroNotEqual) => term.reduce1(Terminal::ZeroNotEqual)?,
Some(NonTerm::AndV) => term.reduce2(Terminal::AndV)?,
Some(NonTerm::AndB) => term.reduce2(Terminal::AndB)?,
Some(NonTerm::OrB) => term.reduce2(Terminal::OrB)?,
Some(NonTerm::OrC) => term.reduce2(Terminal::OrC)?,
Some(NonTerm::OrD) => term.reduce2(Terminal::OrD)?,
Some(NonTerm::Tern) => {
let a = term.pop().unwrap();
let b = term.pop().unwrap();
let c = term.pop().unwrap();
let wrapped_ms = Terminal::AndOr(Arc::new(a), Arc::new(b), Arc::new(c));
let ty = Type::type_check(&wrapped_ms, return_none)?;
let ext = ExtData::type_check(&wrapped_ms, return_none)?;
term.0.push(Miniscript {
node: wrapped_ms,
ty: ty,
ext: ext,
});
}
Some(NonTerm::ThreshW { n, k }) => {
match_token!(
tokens,
Tk::Add => {
non_term.push(NonTerm::ThreshW { n: n + 1, k });
},
x => {
tokens.un_next(x);
non_term.push(NonTerm::ThreshE { n: n + 1, k });
},
);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
}
Some(NonTerm::ThreshE { n, k }) => {
let mut subs = Vec::with_capacity(n);
for _ in 0..n {
subs.push(Arc::new(term.pop().unwrap()));
}
term.reduce0(Terminal::Thresh(k, subs))?;
}
Some(NonTerm::EndIf) => {
match_token!(
tokens,
Tk::Else => {
non_term.push(NonTerm::EndIfElse);
non_term.push(NonTerm::MaybeAndV);
non_term.push(NonTerm::MaybeSwap);
non_term.push(NonTerm::Expression);
},
Tk::If => match_token!(
tokens,
Tk::Dup => non_term.push(NonTerm::DupIf),
Tk::ZeroNotEqual, Tk::Size
=> non_term.push(NonTerm::NonZero),
),
Tk::NotIf => {
non_term.push(NonTerm::EndIfNotIf);
},
);
}
Some(NonTerm::EndIfNotIf) => {
match_token!(
tokens,
Tk::IfDup => non_term.push(NonTerm::OrD),
x => {
tokens.un_next(x);
non_term.push(NonTerm::OrC);
},
);
non_term.push(NonTerm::Expression);
}
Some(NonTerm::EndIfElse) => {
match_token!(
tokens,
Tk::If => {
term.reduce2(Terminal::OrI)?;
},
Tk::NotIf => {
non_term.push(NonTerm::Tern);
non_term.push(NonTerm::Expression);
},
);
}
None => {
break;
}
}
}
assert_eq!(non_term.len(), 0);
assert_eq!(term.0.len(), 1);
Ok(term.pop().unwrap())
}