use bitcoin;
use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::{hash160, ripemd160, sha256, Hash};
use bitcoin::{LockTime, Sequence};
use super::error::PkEvalErrInner;
use super::{verify_sersig, BitcoinKey, Error, HashLockType, KeySigPair, SatisfiedConstraint};
use crate::hash256;
use crate::miniscript::context::SigType;
use crate::prelude::*;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub enum Element<'txin> {
Satisfied,
Dissatisfied,
Push(&'txin [u8]),
}
impl<'txin> From<&'txin Vec<u8>> for Element<'txin> {
fn from(v: &'txin Vec<u8>) -> Element<'txin> {
From::from(&v[..])
}
}
impl<'txin> From<&'txin [u8]> for Element<'txin> {
fn from(v: &'txin [u8]) -> Element<'txin> {
if *v == [1] {
Element::Satisfied
} else if v.is_empty() {
Element::Dissatisfied
} else {
Element::Push(v)
}
}
}
impl<'txin> Element<'txin> {
pub fn from_instruction(
ins: Result<script::Instruction<'txin>, bitcoin::blockdata::script::Error>,
) -> Result<Self, Error> {
match ins {
Ok(script::Instruction::PushBytes(v)) => Ok(Element::from(v)),
Ok(script::Instruction::Op(opcodes::all::OP_PUSHNUM_1)) => Ok(Element::Satisfied),
_ => Err(Error::ExpectedPush),
}
}
pub(super) fn as_push(&self) -> Result<&[u8], Error> {
if let Element::Push(sl) = *self {
Ok(sl)
} else {
Err(Error::UnexpectedStackBoolean)
}
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Default, Hash)]
pub struct Stack<'txin>(Vec<Element<'txin>>);
impl<'txin> From<Vec<Element<'txin>>> for Stack<'txin> {
fn from(v: Vec<Element<'txin>>) -> Self {
Stack(v)
}
}
impl<'txin> Stack<'txin> {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&mut self) -> usize {
self.0.len()
}
pub fn pop(&mut self) -> Option<Element<'txin>> {
self.0.pop()
}
pub fn push(&mut self, elem: Element<'txin>) {
self.0.push(elem);
}
pub fn split_off(&mut self, k: usize) -> Vec<Element<'txin>> {
self.0.split_off(k)
}
pub fn last(&self) -> Option<&Element<'txin>> {
self.0.last()
}
pub(super) fn evaluate_pk<'intp>(
&mut self,
verify_sig: &mut Box<dyn FnMut(&KeySigPair) -> bool + 'intp>,
pk: BitcoinKey,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(sigser) = self.pop() {
match sigser {
Element::Dissatisfied => {
self.push(Element::Dissatisfied);
None
}
Element::Push(sigser) => {
let key_sig = verify_sersig(verify_sig, &pk, sigser);
match key_sig {
Ok(key_sig) => {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::PublicKey { key_sig }))
}
Err(e) => Some(Err(e)),
}
}
Element::Satisfied => Some(Err(Error::PkEvaluationError(PkEvalErrInner::from(pk)))),
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub(super) fn evaluate_pkh<'intp>(
&mut self,
verify_sig: &mut Box<dyn FnMut(&KeySigPair) -> bool + 'intp>,
pkh: hash160::Hash,
sig_type: SigType,
) -> Option<Result<SatisfiedConstraint, Error>> {
fn bitcoin_key_from_slice(sl: &[u8], sig_type: SigType) -> Option<BitcoinKey> {
let key: BitcoinKey = match sig_type {
SigType::Schnorr => bitcoin::XOnlyPublicKey::from_slice(sl).ok()?.into(),
SigType::Ecdsa => bitcoin::PublicKey::from_slice(sl).ok()?.into(),
};
Some(key)
}
if let Some(Element::Push(pk)) = self.pop() {
let pk_hash = hash160::Hash::hash(pk);
if pk_hash != pkh {
return Some(Err(Error::PkHashVerifyFail(pkh)));
}
match bitcoin_key_from_slice(pk, sig_type) {
Some(pk) => {
if let Some(sigser) = self.pop() {
match sigser {
Element::Dissatisfied => {
self.push(Element::Dissatisfied);
None
}
Element::Push(sigser) => {
let key_sig = verify_sersig(verify_sig, &pk, sigser);
match key_sig {
Ok(key_sig) => {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::PublicKeyHash {
keyhash: pkh,
key_sig,
}))
}
Err(e) => Some(Err(e)),
}
}
Element::Satisfied => Some(Err(Error::PkEvaluationError(pk.into()))),
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
None => Some(Err(Error::PubkeyParseError)),
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub(super) fn evaluate_after(
&mut self,
n: &LockTime,
lock_time: LockTime,
) -> Option<Result<SatisfiedConstraint, Error>> {
use LockTime::*;
let is_satisfied = match (*n, lock_time) {
(Blocks(n), Blocks(lock_time)) => n <= lock_time,
(Seconds(n), Seconds(lock_time)) => n <= lock_time,
_ => {
return Some(Err(Error::AbsoluteLocktimeComparisonInvalid(
n.to_consensus_u32(),
lock_time.to_consensus_u32(),
)))
}
};
if is_satisfied {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::AbsoluteTimelock { n: *n }))
} else {
Some(Err(Error::AbsoluteLocktimeNotMet(n.to_consensus_u32())))
}
}
pub(super) fn evaluate_older(
&mut self,
n: &Sequence,
age: Sequence,
) -> Option<Result<SatisfiedConstraint, Error>> {
if age >= *n {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::RelativeTimelock { n: *n }))
} else {
Some(Err(Error::RelativeLocktimeNotMet(n.to_consensus_u32())))
}
}
pub(super) fn evaluate_sha256(
&mut self,
hash: &sha256::Hash,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(Element::Push(preimage)) = self.pop() {
if preimage.len() != 32 {
return Some(Err(Error::HashPreimageLengthMismatch));
}
if sha256::Hash::hash(preimage) == *hash {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::HashLock {
hash: HashLockType::Sha256(*hash),
preimage: preimage_from_sl(preimage),
}))
} else {
self.push(Element::Dissatisfied);
None
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub(super) fn evaluate_hash256(
&mut self,
hash: &hash256::Hash,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(Element::Push(preimage)) = self.pop() {
if preimage.len() != 32 {
return Some(Err(Error::HashPreimageLengthMismatch));
}
if hash256::Hash::hash(preimage) == *hash {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::HashLock {
hash: HashLockType::Hash256(*hash),
preimage: preimage_from_sl(preimage),
}))
} else {
self.push(Element::Dissatisfied);
None
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub(super) fn evaluate_hash160(
&mut self,
hash: &hash160::Hash,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(Element::Push(preimage)) = self.pop() {
if preimage.len() != 32 {
return Some(Err(Error::HashPreimageLengthMismatch));
}
if hash160::Hash::hash(preimage) == *hash {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::HashLock {
hash: HashLockType::Hash160(*hash),
preimage: preimage_from_sl(preimage),
}))
} else {
self.push(Element::Dissatisfied);
None
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub(super) fn evaluate_ripemd160(
&mut self,
hash: &ripemd160::Hash,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(Element::Push(preimage)) = self.pop() {
if preimage.len() != 32 {
return Some(Err(Error::HashPreimageLengthMismatch));
}
if ripemd160::Hash::hash(preimage) == *hash {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::HashLock {
hash: HashLockType::Ripemd160(*hash),
preimage: preimage_from_sl(preimage),
}))
} else {
self.push(Element::Dissatisfied);
None
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub(super) fn evaluate_multi<'intp>(
&mut self,
verify_sig: &mut Box<dyn FnMut(&KeySigPair) -> bool + 'intp>,
pk: &'intp BitcoinKey,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(witness_sig) = self.pop() {
if let Element::Push(sigser) = witness_sig {
let key_sig = verify_sersig(verify_sig, pk, sigser);
match key_sig {
Ok(key_sig) => Some(Ok(SatisfiedConstraint::PublicKey { key_sig })),
Err(..) => {
self.push(witness_sig);
None
}
}
} else {
Some(Err(Error::UnexpectedStackBoolean))
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
}
fn preimage_from_sl(sl: &[u8]) -> [u8; 32] {
if sl.len() != 32 {
unreachable!("Internal: Preimage length checked to be 32")
} else {
let mut preimage = [0u8; 32];
preimage.copy_from_slice(sl);
preimage
}
}