use bitcoin;
use bitcoin::blockdata::{opcodes, script};
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
use super::error::PkEvalErrInner;
use super::{
verify_sersig, BitcoinKey, Error, HashLockType, KeySigPair, SatisfiedConstraint, TypedHash160,
};
#[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, 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> Default for Stack<'txin> {
fn default() -> Self {
Stack(vec![])
}
}
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: &'intp 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) => return Some(Err(e)),
}
}
Element::Satisfied => {
return 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: &'intp TypedHash160,
) -> Option<Result<SatisfiedConstraint, Error>> {
fn bitcoin_key_from_slice(sl: &[u8], tag: TypedHash160) -> Option<BitcoinKey> {
let key: BitcoinKey = match tag {
TypedHash160::XonlyKey(_) => bitcoin::XOnlyPublicKey::from_slice(sl).ok()?.into(),
TypedHash160::FullKey(_) => 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.hash160() {
return Some(Err(Error::PkHashVerifyFail(pkh.hash160())));
}
match bitcoin_key_from_slice(pk, *pkh) {
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.hash160(),
key_sig: key_sig,
}))
}
Err(e) => return Some(Err(e)),
}
}
Element::Satisfied => {
return 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<'intp>(
&mut self,
n: &'intp u32,
age: u32,
) -> Option<Result<SatisfiedConstraint, Error>> {
if age >= *n {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::AbsoluteTimeLock { time: *n }))
} else {
Some(Err(Error::AbsoluteLocktimeNotMet(*n)))
}
}
pub(super) fn evaluate_older<'intp>(
&mut self,
n: &'intp u32,
height: u32,
) -> Option<Result<SatisfiedConstraint, Error>> {
if height >= *n {
self.push(Element::Satisfied);
Some(Ok(SatisfiedConstraint::RelativeTimeLock { time: *n }))
} else {
Some(Err(Error::RelativeLocktimeNotMet(*n)))
}
}
pub(super) fn evaluate_sha256<'intp>(
&mut self,
hash: &'intp 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<'intp>(
&mut self,
hash: &'intp sha256d::Hash,
) -> Option<Result<SatisfiedConstraint, Error>> {
if let Some(Element::Push(preimage)) = self.pop() {
if preimage.len() != 32 {
return Some(Err(Error::HashPreimageLengthMismatch));
}
if sha256d::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<'intp>(
&mut self,
hash: &'intp 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<'intp>(
&mut self,
hash: &'intp 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) => return Some(Ok(SatisfiedConstraint::PublicKey { key_sig })),
Err(..) => {
self.push(witness_sig);
return None;
}
}
} else {
Some(Err(Error::UnexpectedStackBoolean))
}
} else {
Some(Err(Error::UnexpectedStackEnd))
}
}
pub fn evaluate_txtemplate<'intp>(
&mut self,
given: &sha256::Hash,
expected: &'intp sha256::Hash,
) -> Option<Result<SatisfiedConstraint, Error>> {
Some(if *given == *expected {
Ok(SatisfiedConstraint::TxTemplate { hash: *expected })
} else {
Err(Error::TxTemplateHashWrong)
})
}
}
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
}
}