From 158ba26a8a6577357b6d07ecc1c2b9363475ea69 Mon Sep 17 00:00:00 2001 From: junderw Date: Tue, 19 Sep 2023 20:53:09 -0700 Subject: [PATCH] Feature: Count sigops for Transaction Co-authored-by: Tobin C. Harding --- bitcoin/src/blockdata/script/borrowed.rs | 57 +++++ bitcoin/src/blockdata/transaction.rs | 267 ++++++++++++++++++++++- 2 files changed, 323 insertions(+), 1 deletion(-) diff --git a/bitcoin/src/blockdata/script/borrowed.rs b/bitcoin/src/blockdata/script/borrowed.rs index 9add2a63..337eb89b 100644 --- a/bitcoin/src/blockdata/script/borrowed.rs +++ b/bitcoin/src/blockdata/script/borrowed.rs @@ -9,6 +9,7 @@ use core::ops::{Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, Ran use hashes::Hash; use secp256k1::{Secp256k1, Verification}; +use super::PushBytes; use crate::blockdata::opcodes::all::*; use crate::blockdata::opcodes::{self, Opcode}; use crate::blockdata::script::witness_version::WitnessVersion; @@ -207,6 +208,25 @@ impl Script { && self.0[24] == OP_CHECKSIG.to_u8() } + /// Checks whether a script is push only. + /// + /// Note: `OP_RESERVED` (`0x50`) and all the OP_PUSHNUM operations + /// are considered push operations. + #[inline] + pub fn is_push_only(&self) -> bool { + for inst in self.instructions() { + match inst { + Err(_) => return false, + Ok(Instruction::PushBytes(_)) => {} + Ok(Instruction::Op(op)) if op.to_u8() <= 0x60 => {} + // From Bitcoin Core + // if (opcode > OP_PUSHNUM_16 (0x60)) return false + Ok(Instruction::Op(_)) => return false, + } + } + true + } + /// Checks whether a script pubkey is a P2PK output. /// /// You can obtain the public key, if its valid, @@ -459,6 +479,7 @@ impl Script { match inst { Ok(Instruction::Op(opcode)) => { match opcode { + // p2pk, p2pkh OP_CHECKSIG | OP_CHECKSIGVERIFY => { n += 1; } @@ -567,6 +588,29 @@ impl Script { } } + /// Iterates the script to find the last pushdata. + /// + /// Returns `None` if the instruction is an opcode or if the script is empty. + pub(crate) fn last_pushdata(&self) -> Option { + match self.instructions().last() { + // Handles op codes up to (but excluding) OP_PUSHNUM_NEG. + Some(Ok(Instruction::PushBytes(bytes))) => Some(Push::Data(bytes)), + // OP_16 (0x60) and lower are considered "pushes" by Bitcoin Core (excl. OP_RESERVED). + // By here we know that op is between OP_PUSHNUM_NEG AND OP_PUSHNUM_16 inclusive. + Some(Ok(Instruction::Op(op))) if op.to_u8() <= 0x60 => { + if op == OP_PUSHNUM_NEG1 { + Some(Push::Num(-1)) + } else if op == OP_RESERVED { + Some(Push::Reserved) + } else { + let num = (op.to_u8() - 0x50) as i8; // cast ok, num is [1, 16]. + Some(Push::Num(num)) + } + } + _ => None, + } + } + /// Converts a [`Box