use alloc::vec::Vec;
use core::num::TryFromIntError;
use core::slice::Iter;
use thiserror::Error;
#[cfg(feature = "signature-validation")]
use crate::external::pubkey::PubKey;
use crate::{num, script, signature};
#[allow(missing_docs)]
#[derive(Clone, PartialEq, Eq, Debug, Error)]
pub enum Error {
#[error("OP_RETURN encountered")]
OpReturn,
#[error("operation count exceeded maxmimum of {}", MAX_OP_COUNT)]
OpCount,
#[error("stack depth exceeded maxmimum of {} entries", MAX_STACK_DEPTH)]
StackSize(Option<TryFromIntError>),
#[error(
"signature count wasn’t in the range 1..={}{}",
MAX_PUBKEY_COUNT,
.0.map_or("", |e| ": {e}")
)]
SigCount(Option<TryFromIntError>),
#[error(
"public key count wasn’t in the range 1..={}{}",
MAX_PUBKEY_COUNT,
.0.map_or("", |e| ": {e}")
)]
PubKeyCount(Option<TryFromIntError>),
#[error("verify operation failed")]
Verify,
#[error("bad opcode encountered")]
BadOpcode,
#[error("{}", .0.map_or("invalid stack operation encountered", |(elem, max)| "tried to retrieve element {elem} from a stack with {max} elements"))]
InvalidStackOperation(Option<(usize, usize)>),
#[error("unbalanced conditional encountered")]
UnbalancedConditional,
#[error("negative lock time encountered")]
NegativeLockTime,
#[error("unsatisfied locktime condition")]
UnsatisfiedLockTime,
#[error("signature encoding error: {0}")]
SignatureEncoding(signature::Error),
#[error("non-minimal data encountered when minimal data required")]
MinimalData,
#[error("signature null dummy error")]
SigNullDummy,
#[error("public key type error")]
PubKeyType,
#[error("discouraged upgradable NOP encountered")]
DiscourageUpgradableNOPs,
#[error("script number error: {0}")]
Num(num::Error),
}
impl Error {
pub(crate) fn normalize(&self) -> Self {
match self {
Self::InvalidStackOperation(Some(_)) => Self::InvalidStackOperation(None),
Self::SignatureEncoding(sig_err) => match sig_err {
signature::Error::SigHashType(Some(_)) => {
Self::from(signature::Error::SigHashType(None))
}
signature::Error::SigDER(Some(_)) => Self::from(signature::Error::SigDER(None)),
_ => self.clone(),
},
Self::StackSize(Some(_)) => Self::StackSize(None),
Self::SigCount(Some(_)) => Self::SigCount(None),
Self::PubKeyCount(Some(_)) => Self::PubKeyCount(None),
_ => self.clone(),
}
}
}
impl From<num::Error> for Error {
fn from(value: num::Error) -> Self {
Error::Num(value)
}
}
impl From<signature::Error> for Error {
fn from(value: signature::Error) -> Self {
Error::SignatureEncoding(value)
}
}
#[cfg(feature = "signature-validation")]
const LOCKTIME_THRESHOLD: i64 = 500_000_000;
pub(crate) const MAX_OP_COUNT: u8 = 201;
pub(crate) const MAX_PUBKEY_COUNT: u8 = 20;
bitflags::bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Flags: u32 {
const P2SH = 1 << 0;
const StrictEnc = 1 << 1;
const LowS = 1 << 3;
const NullDummy = 1 << 4;
const SigPushOnly = 1 << 5;
const MinimalData = 1 << 6;
const DiscourageUpgradableNOPs = 1 << 7;
const CleanStack = 1 << 8;
const CHECKLOCKTIMEVERIFY = 1 << 9;
}
}
pub trait SignatureChecker {
fn check_sig(
&self,
script_sig: &signature::Decoded,
vch_pub_key: &[u8],
script_code: &script::Code,
) -> bool;
fn check_lock_time(&self, lock_time: i64) -> bool;
}
pub struct NullSignatureChecker();
impl SignatureChecker for NullSignatureChecker {
fn check_sig(
&self,
_script_sig: &signature::Decoded,
_vch_pub_key: &[u8],
_script_code: &script::Code,
) -> bool {
false
}
fn check_lock_time(&self, _lock_time: i64) -> bool {
false
}
}
#[cfg(feature = "signature-validation")]
#[derive(Copy, Clone)]
pub struct CallbackTransactionSignatureChecker<'a> {
pub sighash: SighashCalculator<'a>,
pub lock_time: i64,
pub is_final: bool,
}
pub(crate) fn cast_to_bool(vch: &[u8]) -> bool {
for i in 0..vch.len() {
if vch[i] != 0 {
if i == vch.len() - 1 && vch[i] == 0x80 {
return false;
}
return true;
}
}
false
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Stack<T>(Vec<T>);
pub(crate) const MAX_STACK_DEPTH: usize = 1000;
impl<T> Stack<T> {
pub(crate) fn new() -> Self {
Stack(vec![])
}
pub(crate) fn check_len(&self, min: usize) -> Result<(), Error> {
let len = self.0.len();
if min <= len {
Ok(())
} else {
Err(Error::InvalidStackOperation(Some((min - 1, len))))
}
}
fn rindex(&self, i: usize) -> Result<usize, Error> {
let len = self.0.len();
if i < len {
Ok(len - i - 1)
} else {
Err(Error::InvalidStackOperation(Some((i, len))))
}
}
pub(crate) fn rget(&self, i: usize) -> Result<&T, Error> {
let idx = self.rindex(i)?;
self.0.get(idx).ok_or(Error::InvalidStackOperation(None))
}
pub(crate) fn pop(&mut self) -> Result<T, Error> {
self.0
.pop()
.ok_or(Error::InvalidStackOperation(Some((0, self.0.len()))))
}
pub(crate) fn push(&mut self, value: T) {
self.0.push(value)
}
pub(crate) fn len(&self) -> usize {
self.0.len()
}
fn iter(&self) -> Iter<'_, T> {
self.0.iter()
}
pub(crate) fn last_mut(&mut self) -> Result<&mut T, Error> {
let len = self.0.len();
self.0
.last_mut()
.ok_or(Error::InvalidStackOperation(Some((0, len))))
}
pub(crate) fn last(&self) -> Result<&T, Error> {
self.0
.last()
.ok_or(Error::InvalidStackOperation(Some((0, self.0.len()))))
}
pub(crate) fn rremove(&mut self, start: usize) -> Result<T, Error> {
self.rindex(start).map(|rstart| self.0.remove(rstart))
}
pub(crate) fn rinsert(&mut self, i: usize, element: T) -> Result<(), Error> {
let ri = self.rindex(i)?;
self.0.insert(ri, element);
Ok(())
}
pub(crate) fn unop(&mut self, op: impl FnOnce(T) -> Result<T, Error>) -> Result<(), Error> {
self.pop().and_then(op).map(|res| self.push(res))
}
pub(crate) fn binfn<R>(
&mut self,
op: impl FnOnce(T, T) -> Result<R, Error>,
) -> Result<R, Error> {
let x2 = self.pop()?;
let x1 = self.pop()?;
op(x1, x2)
}
pub(crate) fn binop(&mut self, op: impl FnOnce(T, T) -> Result<T, Error>) -> Result<(), Error> {
self.binfn(op).map(|res| self.push(res))
}
}
impl<T: Clone> Stack<T> {
pub(crate) fn split_last(&self) -> Result<(&T, Stack<T>), Error> {
self.0
.split_last()
.ok_or(Error::InvalidStackOperation(Some((0, self.0.len()))))
.map(|(last, rem)| (last, Stack(rem.to_vec())))
}
pub(crate) fn repush(&mut self, i: usize) -> Result<(), Error> {
self.rget(i).cloned().map(|v| self.push(v))
}
pub(crate) fn move_to_top(&mut self, i: usize) -> Result<(), Error> {
self.rremove(i).map(|v| self.push(v.clone()))
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct State {
pub(crate) stack: Stack<Vec<u8>>,
pub(crate) altstack: Stack<Vec<u8>>,
op_count: u8,
pub(crate) vexec: Stack<bool>,
}
impl State {
pub(crate) fn initial(stack: Stack<Vec<u8>>) -> Self {
Self::from_parts(stack, Stack::new(), 0, Stack::new())
}
pub(crate) fn increment_op_count(&mut self, by: u8) -> Result<(), Error> {
self.op_count += by;
if self.op_count <= MAX_OP_COUNT {
Ok(())
} else {
Err(Error::OpCount)
}
}
pub(crate) fn from_parts(
stack: Stack<Vec<u8>>,
altstack: Stack<Vec<u8>>,
op_count: u8,
vexec: Stack<bool>,
) -> Self {
State {
stack,
altstack,
op_count,
vexec,
}
}
pub(crate) fn altstack(&self) -> &Stack<Vec<u8>> {
&self.altstack
}
}
pub(crate) fn should_exec(vexec: &Stack<bool>) -> bool {
vexec.iter().all(|value| *value)
}
pub(crate) const SIGHASH_SIZE: usize = 32;
pub type SighashCalculator<'a> =
&'a dyn Fn(&script::Code, &signature::HashType) -> Option<[u8; SIGHASH_SIZE]>;
#[cfg(feature = "signature-validation")]
impl SignatureChecker for CallbackTransactionSignatureChecker<'_> {
fn check_sig(
&self,
sig: &signature::Decoded,
vch_pub_key: &[u8],
script_code: &script::Code,
) -> bool {
let pubkey = PubKey(vch_pub_key);
pubkey.is_valid()
&& (self.sighash)(script_code, sig.sighash_type())
.map(|sighash| pubkey.verify(&sighash, sig.sig()))
.unwrap_or(false)
}
fn check_lock_time(&self, lock_time: i64) -> bool {
if self.lock_time < LOCKTIME_THRESHOLD && lock_time >= LOCKTIME_THRESHOLD
|| self.lock_time >= LOCKTIME_THRESHOLD && lock_time < LOCKTIME_THRESHOLD
|| lock_time > self.lock_time
{
false
} else {
!self.is_final
}
}
}