use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
use bitcoin::util::bip143;
use bitcoin::{self, secp256k1};
use miniscript::context::NoChecks;
use miniscript::ScriptContext;
use Miniscript;
use Terminal;
use {BitcoinSig, Descriptor, ToPublicKey};
mod error;
mod inner;
mod stack;
pub use self::error::Error;
use self::stack::Stack;
pub struct Interpreter<'txin> {
inner: inner::Inner,
stack: Stack<'txin>,
script_code: bitcoin::Script,
age: u32,
height: u32,
}
impl<'txin> Interpreter<'txin> {
pub fn from_txdata(
spk: &bitcoin::Script,
script_sig: &'txin bitcoin::Script,
witness: &'txin [Vec<u8>],
age: u32,
height: u32,
) -> Result<Self, Error> {
let (inner, stack, script_code) = inner::from_txdata(spk, script_sig, witness)?;
Ok(Interpreter {
inner,
stack,
script_code,
age,
height,
})
}
pub fn iter<'iter, F: FnMut(&bitcoin::PublicKey, BitcoinSig) -> bool>(
&'iter mut self,
verify_sig: F,
) -> Iter<'txin, 'iter, F> {
Iter {
verify_sig: verify_sig,
public_key: if let inner::Inner::PublicKey(ref pk, _) = self.inner {
Some(pk)
} else {
None
},
state: if let inner::Inner::Script(ref script, _) = self.inner {
vec![NodeEvaluationState {
node: script,
n_evaluated: 0,
n_satisfied: 0,
}]
} else {
vec![]
},
stack: &mut self.stack,
age: self.age,
height: self.height,
has_errored: false,
}
}
pub fn inferred_descriptor_string(&self) -> String {
match self.inner {
inner::Inner::PublicKey(ref pk, inner::PubkeyType::Pk) => format!("pk({})", pk),
inner::Inner::PublicKey(ref pk, inner::PubkeyType::Pkh) => format!("pkh({})", pk),
inner::Inner::PublicKey(ref pk, inner::PubkeyType::Wpkh) => format!("wpkh({})", pk),
inner::Inner::PublicKey(ref pk, inner::PubkeyType::ShWpkh) => {
format!("sh(wpkh({}))", pk)
}
inner::Inner::Script(ref ms, inner::ScriptType::Bare) => format!("{}", ms),
inner::Inner::Script(ref ms, inner::ScriptType::Sh) => format!("sh({})", ms),
inner::Inner::Script(ref ms, inner::ScriptType::Wsh) => format!("wsh({})", ms),
inner::Inner::Script(ref ms, inner::ScriptType::ShWsh) => format!("sh(wsh({}))", ms),
}
}
pub fn is_legacy(&self) -> bool {
match self.inner {
inner::Inner::PublicKey(_, inner::PubkeyType::Pk) => true,
inner::Inner::PublicKey(_, inner::PubkeyType::Pkh) => true,
inner::Inner::PublicKey(_, inner::PubkeyType::Wpkh) => false,
inner::Inner::PublicKey(_, inner::PubkeyType::ShWpkh) => false,
inner::Inner::Script(_, inner::ScriptType::Bare) => true,
inner::Inner::Script(_, inner::ScriptType::Sh) => true,
inner::Inner::Script(_, inner::ScriptType::Wsh) => false,
inner::Inner::Script(_, inner::ScriptType::ShWsh) => false,
}
}
pub fn inferred_descriptor(&self) -> Result<Descriptor<bitcoin::PublicKey>, ::Error> {
use std::str::FromStr;
Descriptor::from_str(&self.inferred_descriptor_string())
}
pub fn sighash_message(
&self,
unsigned_tx: &bitcoin::Transaction,
input_idx: usize,
amount: u64,
sighash_type: bitcoin::SigHashType,
) -> secp256k1::Message {
let hash = if self.is_legacy() {
unsigned_tx.signature_hash(input_idx, &self.script_code, sighash_type.as_u32())
} else {
let mut sighash_cache = bip143::SigHashCache::new(unsigned_tx);
sighash_cache.signature_hash(input_idx, &self.script_code, amount, sighash_type)
};
secp256k1::Message::from_slice(&hash[..])
.expect("cryptographically unreachable for this to fail")
}
pub fn sighash_verify<'a, C: secp256k1::Verification>(
&self,
secp: &'a secp256k1::Secp256k1<C>,
unsigned_tx: &'a bitcoin::Transaction,
input_idx: usize,
amount: u64,
) -> impl Fn(&bitcoin::PublicKey, BitcoinSig) -> bool + 'a {
let sighashes = [
self.sighash_message(unsigned_tx, input_idx, amount, bitcoin::SigHashType::All),
self.sighash_message(unsigned_tx, input_idx, amount, bitcoin::SigHashType::None),
self.sighash_message(unsigned_tx, input_idx, amount, bitcoin::SigHashType::Single),
self.sighash_message(
unsigned_tx,
input_idx,
amount,
bitcoin::SigHashType::AllPlusAnyoneCanPay,
),
self.sighash_message(
unsigned_tx,
input_idx,
amount,
bitcoin::SigHashType::NonePlusAnyoneCanPay,
),
self.sighash_message(
unsigned_tx,
input_idx,
amount,
bitcoin::SigHashType::SinglePlusAnyoneCanPay,
),
];
move |pk: &bitcoin::PublicKey, (sig, sighash_type)| {
let sighash = match sighash_type {
bitcoin::SigHashType::All => sighashes[0],
bitcoin::SigHashType::None => sighashes[1],
bitcoin::SigHashType::Single => sighashes[2],
bitcoin::SigHashType::AllPlusAnyoneCanPay => sighashes[3],
bitcoin::SigHashType::NonePlusAnyoneCanPay => sighashes[4],
bitcoin::SigHashType::SinglePlusAnyoneCanPay => sighashes[5],
};
secp.verify(&sighash, &sig, &pk.key).is_ok()
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum HashLockType<'intp> {
Sha256(&'intp sha256::Hash),
Hash256(&'intp sha256d::Hash),
Hash160(&'intp hash160::Hash),
Ripemd160(&'intp ripemd160::Hash),
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum SatisfiedConstraint<'intp, 'txin> {
PublicKey {
key: &'intp bitcoin::PublicKey,
sig: secp256k1::Signature,
},
PublicKeyHash {
keyhash: &'intp hash160::Hash,
key: bitcoin::PublicKey,
sig: secp256k1::Signature,
},
HashLock {
hash: HashLockType<'intp>,
preimage: &'txin [u8],
},
RelativeTimeLock {
time: &'intp u32,
},
AbsoluteTimeLock {
time: &'intp u32,
},
}
struct NodeEvaluationState<'intp> {
node: &'intp Miniscript<bitcoin::PublicKey, NoChecks>,
n_evaluated: usize,
n_satisfied: usize,
}
pub struct Iter<'intp, 'txin: 'intp, F: FnMut(&bitcoin::PublicKey, BitcoinSig) -> bool> {
verify_sig: F,
public_key: Option<&'intp bitcoin::PublicKey>,
state: Vec<NodeEvaluationState<'intp>>,
stack: &'intp mut Stack<'txin>,
age: u32,
height: u32,
has_errored: bool,
}
impl<'intp, 'txin: 'intp, F> Iterator for Iter<'intp, 'txin, F>
where
NoChecks: ScriptContext,
F: FnMut(&bitcoin::PublicKey, BitcoinSig) -> bool,
{
type Item = Result<SatisfiedConstraint<'intp, 'txin>, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.has_errored {
None
} else {
let res = self.iter_next();
if let Some(Err(_)) = res {
self.has_errored = true;
}
res
}
}
}
impl<'intp, 'txin: 'intp, F> Iter<'intp, 'txin, F>
where
NoChecks: ScriptContext,
F: FnMut(&bitcoin::PublicKey, BitcoinSig) -> bool,
{
fn push_evaluation_state(
&mut self,
node: &'intp Miniscript<bitcoin::PublicKey, NoChecks>,
n_evaluated: usize,
n_satisfied: usize,
) -> () {
self.state.push(NodeEvaluationState {
node,
n_evaluated,
n_satisfied,
})
}
fn iter_next(&mut self) -> Option<Result<SatisfiedConstraint<'intp, 'txin>, Error>> {
while let Some(node_state) = self.state.pop() {
match node_state.node.node {
Terminal::True => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
self.stack.push(stack::Element::Satisfied);
}
Terminal::False => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
self.stack.push(stack::Element::Dissatisfied);
}
Terminal::PkK(ref pk) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_pk(&mut self.verify_sig, pk);
if res.is_some() {
return res;
}
}
Terminal::PkH(ref pkh) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_pkh(&mut self.verify_sig, pkh);
if res.is_some() {
return res;
}
}
Terminal::After(ref n) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_after(n, self.age);
if res.is_some() {
return res;
}
}
Terminal::Older(ref n) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_older(n, self.height);
if res.is_some() {
return res;
}
}
Terminal::Sha256(ref hash) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_sha256(hash);
if res.is_some() {
return res;
}
}
Terminal::Hash256(ref hash) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_hash256(hash);
if res.is_some() {
return res;
}
}
Terminal::Hash160(ref hash) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_hash160(hash);
if res.is_some() {
return res;
}
}
Terminal::Ripemd160(ref hash) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
let res = self.stack.evaluate_ripemd160(hash);
if res.is_some() {
return res;
}
}
Terminal::Alt(ref sub) | Terminal::Swap(ref sub) | Terminal::Check(ref sub) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
self.push_evaluation_state(sub, 0, 0);
}
Terminal::DupIf(ref sub) if node_state.n_evaluated == 0 => match self.stack.pop() {
Some(stack::Element::Dissatisfied) => {
self.stack.push(stack::Element::Dissatisfied);
}
Some(stack::Element::Satisfied) => {
self.push_evaluation_state(node_state.node, 1, 1);
self.push_evaluation_state(sub, 0, 0);
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
},
Terminal::DupIf(ref _sub) if node_state.n_evaluated == 1 => {
self.stack.push(stack::Element::Satisfied);
}
Terminal::ZeroNotEqual(ref sub) | Terminal::Verify(ref sub)
if node_state.n_evaluated == 0 =>
{
self.push_evaluation_state(node_state.node, 1, 0);
self.push_evaluation_state(sub, 0, 0);
}
Terminal::Verify(ref _sub) if node_state.n_evaluated == 1 => {
match self.stack.pop() {
Some(stack::Element::Satisfied) => (),
Some(_) => return Some(Err(Error::VerifyFailed)),
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::ZeroNotEqual(ref _sub) if node_state.n_evaluated == 1 => {
match self.stack.pop() {
Some(stack::Element::Dissatisfied) => {
self.stack.push(stack::Element::Dissatisfied)
}
Some(_) => self.stack.push(stack::Element::Satisfied),
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::NonZero(ref sub) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
match self.stack.last() {
Some(&stack::Element::Dissatisfied) => (),
Some(_) => self.push_evaluation_state(sub, 0, 0),
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::AndV(ref left, ref right) => {
debug_assert_eq!(node_state.n_evaluated, 0);
debug_assert_eq!(node_state.n_satisfied, 0);
self.push_evaluation_state(right, 0, 0);
self.push_evaluation_state(left, 0, 0);
}
Terminal::OrB(ref left, ref _right) | Terminal::AndB(ref left, ref _right)
if node_state.n_evaluated == 0 =>
{
self.push_evaluation_state(node_state.node, 1, 0);
self.push_evaluation_state(left, 0, 0);
}
Terminal::OrB(ref _left, ref right) | Terminal::AndB(ref _left, ref right)
if node_state.n_evaluated == 1 =>
{
match self.stack.pop() {
Some(stack::Element::Dissatisfied) => {
self.push_evaluation_state(node_state.node, 2, 0);
self.push_evaluation_state(right, 0, 0);
}
Some(stack::Element::Satisfied) => {
self.push_evaluation_state(node_state.node, 2, 1);
self.push_evaluation_state(right, 0, 0);
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::AndB(ref _left, ref _right) if node_state.n_evaluated == 2 => {
match self.stack.pop() {
Some(stack::Element::Satisfied) if node_state.n_satisfied == 1 => {
self.stack.push(stack::Element::Satisfied)
}
Some(_) => self.stack.push(stack::Element::Dissatisfied),
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::AndOr(ref left, ref _right, _)
| Terminal::OrC(ref left, ref _right)
| Terminal::OrD(ref left, ref _right)
if node_state.n_evaluated == 0 =>
{
self.push_evaluation_state(node_state.node, 1, 0);
self.push_evaluation_state(left, 0, 0);
}
Terminal::OrB(ref _left, ref _right) if node_state.n_evaluated == 2 => {
match self.stack.pop() {
Some(stack::Element::Dissatisfied) if node_state.n_satisfied == 0 => {
self.stack.push(stack::Element::Dissatisfied)
}
Some(_) => {
self.stack.push(stack::Element::Satisfied);
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::OrC(ref _left, ref right) if node_state.n_evaluated == 1 => {
match self.stack.pop() {
Some(stack::Element::Satisfied) => (),
Some(stack::Element::Dissatisfied) => {
self.push_evaluation_state(right, 0, 0)
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::OrD(ref _left, ref right) if node_state.n_evaluated == 1 => {
match self.stack.pop() {
Some(stack::Element::Satisfied) => {
self.stack.push(stack::Element::Satisfied)
}
Some(stack::Element::Dissatisfied) => {
self.push_evaluation_state(right, 0, 0)
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::AndOr(_, ref left, ref right) | Terminal::OrI(ref left, ref right) => {
match self.stack.pop() {
Some(stack::Element::Satisfied) => self.push_evaluation_state(left, 0, 0),
Some(stack::Element::Dissatisfied) => {
self.push_evaluation_state(right, 0, 0)
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::Thresh(ref _k, ref subs) if node_state.n_evaluated == 0 => {
self.push_evaluation_state(node_state.node, 1, 0);
self.push_evaluation_state(&subs[0], 0, 0);
}
Terminal::Thresh(k, ref subs) if node_state.n_evaluated == subs.len() => {
match self.stack.pop() {
Some(stack::Element::Dissatisfied) if node_state.n_satisfied == k => {
self.stack.push(stack::Element::Satisfied)
}
Some(stack::Element::Satisfied) if node_state.n_satisfied == k - 1 => {
self.stack.push(stack::Element::Satisfied)
}
Some(stack::Element::Satisfied) | Some(stack::Element::Dissatisfied) => {
self.stack.push(stack::Element::Dissatisfied)
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::Thresh(ref _k, ref subs) if node_state.n_evaluated != 0 => {
match self.stack.pop() {
Some(stack::Element::Dissatisfied) => {
self.push_evaluation_state(
node_state.node,
node_state.n_evaluated + 1,
node_state.n_satisfied,
);
self.push_evaluation_state(&subs[node_state.n_evaluated], 0, 0);
}
Some(stack::Element::Satisfied) => {
self.push_evaluation_state(
node_state.node,
node_state.n_evaluated + 1,
node_state.n_satisfied + 1,
);
self.push_evaluation_state(&subs[node_state.n_evaluated], 0, 0);
}
Some(stack::Element::Push(_v)) => {
return Some(Err(Error::UnexpectedStackElementPush))
}
None => return Some(Err(Error::UnexpectedStackEnd)),
}
}
Terminal::Multi(ref k, ref subs) if node_state.n_evaluated == 0 => {
let len = self.stack.len();
if len < k + 1 {
return Some(Err(Error::InsufficientSignaturesMultiSig));
} else {
match self.stack.last() {
Some(&stack::Element::Dissatisfied) => {
let sigs = self.stack.split_off(len - (k + 1));
let nonsat = sigs
.iter()
.map(|sig| *sig == stack::Element::Dissatisfied)
.filter(|empty| *empty)
.count();
if nonsat == *k + 1 {
self.stack.push(stack::Element::Dissatisfied);
} else {
return Some(Err(Error::MissingExtraZeroMultiSig));
}
}
None => return Some(Err(Error::UnexpectedStackEnd)),
_ => {
match self
.stack
.evaluate_multi(&mut self.verify_sig, &subs[subs.len() - 1])
{
Some(Ok(x)) => {
self.push_evaluation_state(
node_state.node,
node_state.n_evaluated + 1,
node_state.n_satisfied + 1,
);
return Some(Ok(x));
}
None => self.push_evaluation_state(
node_state.node,
node_state.n_evaluated + 1,
node_state.n_satisfied,
),
x => return x,
}
}
}
}
}
Terminal::Multi(k, ref subs) => {
if node_state.n_satisfied == k {
if let Some(stack::Element::Dissatisfied) = self.stack.pop() {
self.stack.push(stack::Element::Satisfied);
} else {
return Some(Err(Error::MissingExtraZeroMultiSig));
}
} else if node_state.n_evaluated == subs.len() {
return Some(Err(Error::MultiSigEvaluationError));
} else {
match self.stack.evaluate_multi(
&mut self.verify_sig,
&subs[subs.len() - node_state.n_evaluated - 1],
) {
Some(Ok(x)) => {
self.push_evaluation_state(
node_state.node,
node_state.n_evaluated + 1,
node_state.n_satisfied + 1,
);
return Some(Ok(x));
}
None => self.push_evaluation_state(
node_state.node,
node_state.n_evaluated + 1,
node_state.n_satisfied,
),
x => return x,
}
}
}
_ => return Some(Err(Error::CouldNotEvaluate)),
};
}
if let Some(pk) = self.public_key {
if let Some(stack::Element::Push(sig)) = self.stack.pop() {
if let Ok(sig) = verify_sersig(&mut self.verify_sig, &pk, &sig) {
self.public_key = None;
self.stack.push(stack::Element::Satisfied);
return Some(Ok(SatisfiedConstraint::PublicKey { key: pk, sig }));
} else {
return Some(Err(Error::PkEvaluationError(pk.clone().to_public_key())));
}
} else {
return Some(Err(Error::UnexpectedStackEnd));
}
} else {
if self.stack.pop() == Some(stack::Element::Satisfied) && self.stack.is_empty() {
return None;
} else {
return Some(Err(Error::ScriptSatisfactionError));
}
}
}
}
fn verify_sersig<'txin, F>(
verify_sig: F,
pk: &bitcoin::PublicKey,
sigser: &[u8],
) -> Result<secp256k1::Signature, Error>
where
F: FnOnce(&bitcoin::PublicKey, BitcoinSig) -> bool,
{
if let Some((sighash_byte, sig)) = sigser.split_last() {
let sighashtype = bitcoin::SigHashType::from_u32(*sighash_byte as u32);
let sig = secp256k1::Signature::from_der(sig)?;
if verify_sig(pk, (sig, sighashtype)) {
Ok(sig)
} else {
Err(Error::InvalidSignature(*pk))
}
} else {
Err(Error::PkEvaluationError(*pk))
}
}
#[cfg(test)]
mod tests {
use super::*;
use bitcoin;
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use bitcoin::secp256k1::{self, Secp256k1, VerifyOnly};
use miniscript::context::NoChecks;
use BitcoinSig;
use Miniscript;
use MiniscriptKey;
use ToPublicKey;
fn setup_keys_sigs(
n: usize,
) -> (
Vec<bitcoin::PublicKey>,
Vec<Vec<u8>>,
Vec<secp256k1::Signature>,
secp256k1::Message,
Secp256k1<VerifyOnly>,
) {
let secp_sign = secp256k1::Secp256k1::signing_only();
let secp_verify = secp256k1::Secp256k1::verification_only();
let msg = secp256k1::Message::from_slice(&b"Yoda: btc, I trust. HODL I must!"[..])
.expect("32 bytes");
let mut pks = vec![];
let mut secp_sigs = vec![];
let mut der_sigs = vec![];
let mut sk = [0; 32];
for i in 1..n + 1 {
sk[0] = i as u8;
sk[1] = (i >> 8) as u8;
sk[2] = (i >> 16) as u8;
let sk = secp256k1::SecretKey::from_slice(&sk[..]).expect("secret key");
let pk = bitcoin::PublicKey {
key: secp256k1::PublicKey::from_secret_key(&secp_sign, &sk),
compressed: true,
};
let sig = secp_sign.sign(&msg, &sk);
secp_sigs.push(sig);
let mut sigser = sig.serialize_der().to_vec();
sigser.push(0x01);
pks.push(pk);
der_sigs.push(sigser);
}
(pks, der_sigs, secp_sigs, msg, secp_verify)
}
#[test]
fn sat_constraints() {
let (pks, der_sigs, secp_sigs, sighash, secp) = setup_keys_sigs(10);
let vfyfn_ =
|pk: &bitcoin::PublicKey, (sig, _)| secp.verify(&sighash, &sig, &pk.key).is_ok();
fn from_stack<'txin, 'elem, F>(
verify_fn: F,
stack: &'elem mut Stack<'txin>,
ms: &'elem Miniscript<bitcoin::PublicKey, NoChecks>,
) -> Iter<'elem, 'txin, F>
where
F: FnMut(&bitcoin::PublicKey, BitcoinSig) -> bool,
{
Iter {
verify_sig: verify_fn,
stack: stack,
public_key: None,
state: vec![NodeEvaluationState {
node: ms,
n_evaluated: 0,
n_satisfied: 0,
}],
age: 1002,
height: 1002,
has_errored: false,
}
};
let pk = ms_str!("c:pk_k({})", pks[0]);
let pkh = ms_str!("c:pk_h({})", pks[1].to_pubkeyhash());
let after = ms_str!("after({})", 1000);
let older = ms_str!("older({})", 1000);
let preimage = vec![0xab as u8; 32];
let sha256_hash = sha256::Hash::hash(&preimage);
let sha256 = ms_str!("sha256({})", sha256_hash);
let sha256d_hash_rev = sha256d::Hash::hash(&preimage);
let mut sha256d_hash_bytes = sha256d_hash_rev.clone().into_inner();
sha256d_hash_bytes.reverse();
let sha256d_hash = sha256d::Hash::from_inner(sha256d_hash_bytes);
let hash256 = ms_str!("hash256({})", sha256d_hash);
let hash160_hash = hash160::Hash::hash(&preimage);
let hash160 = ms_str!("hash160({})", hash160_hash);
let ripemd160_hash = ripemd160::Hash::hash(&preimage);
let ripemd160 = ms_str!("ripemd160({})", ripemd160_hash);
let mut stack = Stack::from(vec![stack::Element::Push(&der_sigs[0])]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &pk);
let pk_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
pk_satisfied.unwrap(),
vec![SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
}]
);
let mut stack = Stack::from(vec![stack::Element::Dissatisfied]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &pk);
let pk_err: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert!(pk_err.is_err());
let pk_bytes = pks[1].to_public_key().to_bytes();
let mut stack = Stack::from(vec![
stack::Element::Push(&der_sigs[1]),
stack::Element::Push(&pk_bytes),
]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &pkh);
let pkh_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
pkh_satisfied.unwrap(),
vec![SatisfiedConstraint::PublicKeyHash {
keyhash: &pks[1].to_pubkeyhash(),
key: pks[1].clone(),
sig: secp_sigs[1].clone(),
}]
);
let mut stack = Stack::from(vec![]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &after);
let after_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
after_satisfied.unwrap(),
vec![SatisfiedConstraint::AbsoluteTimeLock { time: &1000 }]
);
let mut stack = Stack::from(vec![]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &older);
let older_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
older_satisfied.unwrap(),
vec![SatisfiedConstraint::RelativeTimeLock { time: &1000 }]
);
let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &sha256);
let sah256_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
sah256_satisfied.unwrap(),
vec![SatisfiedConstraint::HashLock {
hash: HashLockType::Sha256(&sha256_hash),
preimage: &preimage,
}]
);
let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &hash256);
let sha256d_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
sha256d_satisfied.unwrap(),
vec![SatisfiedConstraint::HashLock {
hash: HashLockType::Hash256(&sha256d_hash_rev),
preimage: &preimage,
}]
);
let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &hash160);
let hash160_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
hash160_satisfied.unwrap(),
vec![SatisfiedConstraint::HashLock {
hash: HashLockType::Hash160(&hash160_hash),
preimage: &preimage,
}]
);
let mut stack = Stack::from(vec![stack::Element::Push(&preimage)]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &ripemd160);
let ripemd160_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
ripemd160_satisfied.unwrap(),
vec![SatisfiedConstraint::HashLock {
hash: HashLockType::Ripemd160(&ripemd160_hash),
preimage: &preimage
}]
);
let pk_bytes = pks[1].to_public_key().to_bytes();
let mut stack = Stack::from(vec![
stack::Element::Push(&der_sigs[1]),
stack::Element::Push(&pk_bytes),
stack::Element::Push(&der_sigs[0]),
]);
let elem = ms_str!(
"and_v(vc:pk_k({}),c:pk_h({}))",
pks[0],
pks[1].to_pubkeyhash()
);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let and_v_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
and_v_satisfied.unwrap(),
vec![
SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
},
SatisfiedConstraint::PublicKeyHash {
keyhash: &pks[1].to_pubkeyhash(),
key: pks[1].clone(),
sig: secp_sigs[1].clone(),
}
]
);
let mut stack = Stack::from(vec![
stack::Element::Push(&preimage),
stack::Element::Push(&der_sigs[0]),
]);
let elem = ms_str!("and_b(c:pk_k({}),sjtv:sha256({}))", pks[0], sha256_hash);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let and_b_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
and_b_satisfied.unwrap(),
vec![
SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
},
SatisfiedConstraint::HashLock {
hash: HashLockType::Sha256(&sha256_hash),
preimage: &preimage,
}
]
);
let mut stack = Stack::from(vec![
stack::Element::Push(&preimage),
stack::Element::Push(&der_sigs[0]),
]);
let elem = ms_str!(
"andor(c:pk_k({}),jtv:sha256({}),c:pk_h({}))",
pks[0],
sha256_hash,
pks[1].to_pubkeyhash(),
);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let and_or_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
and_or_satisfied.unwrap(),
vec![
SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
},
SatisfiedConstraint::HashLock {
hash: HashLockType::Sha256(&sha256_hash),
preimage: &preimage,
}
]
);
let pk_bytes = pks[1].to_public_key().to_bytes();
let mut stack = Stack::from(vec![
stack::Element::Push(&der_sigs[1]),
stack::Element::Push(&pk_bytes),
stack::Element::Dissatisfied,
]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let and_or_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
and_or_satisfied.unwrap(),
vec![SatisfiedConstraint::PublicKeyHash {
keyhash: &pks[1].to_pubkeyhash(),
key: pks[1].clone(),
sig: secp_sigs[1].clone(),
}]
);
let mut stack = Stack::from(vec![
stack::Element::Push(&preimage),
stack::Element::Dissatisfied,
]);
let elem = ms_str!("or_b(c:pk_k({}),sjtv:sha256({}))", pks[0], sha256_hash);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let or_b_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
or_b_satisfied.unwrap(),
vec![SatisfiedConstraint::HashLock {
hash: HashLockType::Sha256(&sha256_hash),
preimage: &preimage,
}]
);
let mut stack = Stack::from(vec![stack::Element::Push(&der_sigs[0])]);
let elem = ms_str!("or_d(c:pk_k({}),jtv:sha256({}))", pks[0], sha256_hash);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let or_d_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
or_d_satisfied.unwrap(),
vec![SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
}]
);
let mut stack = Stack::from(vec![
stack::Element::Push(&der_sigs[0]),
stack::Element::Dissatisfied,
]);
let elem = ms_str!("t:or_c(jtv:sha256({}),vc:pk_k({}))", sha256_hash, pks[0]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let or_c_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
or_c_satisfied.unwrap(),
vec![SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
}]
);
let mut stack = Stack::from(vec![
stack::Element::Push(&der_sigs[0]),
stack::Element::Dissatisfied,
]);
let elem = ms_str!("or_i(jtv:sha256({}),c:pk_k({}))", sha256_hash, pks[0]);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let or_i_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
or_i_satisfied.unwrap(),
vec![SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
}]
);
let mut stack = Stack::from(vec![
stack::Element::Push(&der_sigs[0]),
stack::Element::Push(&der_sigs[1]),
stack::Element::Push(&der_sigs[2]),
stack::Element::Dissatisfied,
stack::Element::Dissatisfied,
]);
let elem = ms_str!(
"thresh(3,c:pk_k({}),sc:pk_k({}),sc:pk_k({}),sc:pk_k({}),sc:pk_k({}))",
pks[4],
pks[3],
pks[2],
pks[1],
pks[0],
);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let thresh_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
thresh_satisfied.unwrap(),
vec![
SatisfiedConstraint::PublicKey {
key: &pks[2],
sig: secp_sigs[2].clone(),
},
SatisfiedConstraint::PublicKey {
key: &pks[1],
sig: secp_sigs[1].clone(),
},
SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
}
]
);
let mut stack = Stack::from(vec![
stack::Element::Dissatisfied,
stack::Element::Push(&der_sigs[2]),
stack::Element::Push(&der_sigs[1]),
stack::Element::Push(&der_sigs[0]),
]);
let elem = ms_str!(
"multi(3,{},{},{},{},{})",
pks[4],
pks[3],
pks[2],
pks[1],
pks[0],
);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let multi_satisfied: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert_eq!(
multi_satisfied.unwrap(),
vec![
SatisfiedConstraint::PublicKey {
key: &pks[0],
sig: secp_sigs[0].clone(),
},
SatisfiedConstraint::PublicKey {
key: &pks[1],
sig: secp_sigs[1].clone(),
},
SatisfiedConstraint::PublicKey {
key: &pks[2],
sig: secp_sigs[2].clone(),
},
]
);
let mut stack = Stack::from(vec![
stack::Element::Dissatisfied,
stack::Element::Push(&der_sigs[0]),
stack::Element::Push(&der_sigs[2]),
stack::Element::Push(&der_sigs[1]),
]);
let elem = ms_str!(
"multi(3,{},{},{},{},{})",
pks[4],
pks[3],
pks[2],
pks[1],
pks[0],
);
let mut vfyfn = vfyfn_.clone();
let constraints = from_stack(&mut vfyfn, &mut stack, &elem);
let multi_error: Result<Vec<SatisfiedConstraint>, Error> = constraints.collect();
assert!(multi_error.is_err());
}
}