Files

1016 lines
32 KiB
C++
Raw Permalink Normal View History

2017-11-09 19:34:51 +01:00
/*
* This file is part of the Flowee project
* Copyright (C) 2009-2010 Satoshi Nakamoto
* Copyright (C) 2009-2015 The Bitcoin Core developers
2018-04-16 11:59:19 +02:00
* Copyright (C) 2018 Jason B. Cox <contact@jasonbcox.com>
2026-05-09 23:50:04 +02:00
* Copyright (C) 2018-2026 Tom Zander <tom@flowee.org>
2017-11-09 19:34:51 +01:00
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "script.h"
2026-05-07 21:10:10 +02:00
#include "ScriptBigNum_p.h"
#include "FastBigNum_p.h"
2023-11-24 18:01:36 +01:00
#include "PublicKeyUtils.h"
#include <hash.h>
#include <tinyformat.h>
2019-06-06 21:37:49 +02:00
#include <uint256.h>
#include <utilstrencodings.h>
2019-06-06 21:37:49 +02:00
2019-06-07 09:52:42 +02:00
#include <map>
2026-05-07 17:51:05 +02:00
#include <utility>
2019-06-07 09:52:42 +02:00
const char* GetOpName(opcodetype opcode)
{
switch (opcode)
{
// push value
case OP_0 : return "0";
case OP_PUSHDATA1 : return "OP_PUSHDATA1";
case OP_PUSHDATA2 : return "OP_PUSHDATA2";
case OP_PUSHDATA4 : return "OP_PUSHDATA4";
case OP_1NEGATE : return "-1";
case OP_RESERVED : return "OP_RESERVED";
case OP_1 : return "1";
case OP_2 : return "2";
case OP_3 : return "3";
case OP_4 : return "4";
case OP_5 : return "5";
case OP_6 : return "6";
case OP_7 : return "7";
case OP_8 : return "8";
case OP_9 : return "9";
case OP_10 : return "10";
case OP_11 : return "11";
case OP_12 : return "12";
case OP_13 : return "13";
case OP_14 : return "14";
case OP_15 : return "15";
case OP_16 : return "16";
// control
case OP_NOP : return "OP_NOP";
case OP_VER : return "OP_VER";
case OP_IF : return "OP_IF";
case OP_NOTIF : return "OP_NOTIF";
2026-05-14 14:19:14 +02:00
case OP_BEGIN : return "OP_BEGIN";
case OP_UNTIL : return "OP_UNTIL";
case OP_ELSE : return "OP_ELSE";
case OP_ENDIF : return "OP_ENDIF";
case OP_VERIFY : return "OP_VERIFY";
case OP_RETURN : return "OP_RETURN";
// stack ops
case OP_TOALTSTACK : return "OP_TOALTSTACK";
case OP_FROMALTSTACK : return "OP_FROMALTSTACK";
case OP_2DROP : return "OP_2DROP";
case OP_2DUP : return "OP_2DUP";
case OP_3DUP : return "OP_3DUP";
case OP_2OVER : return "OP_2OVER";
case OP_2ROT : return "OP_2ROT";
case OP_2SWAP : return "OP_2SWAP";
case OP_IFDUP : return "OP_IFDUP";
case OP_DEPTH : return "OP_DEPTH";
case OP_DROP : return "OP_DROP";
case OP_DUP : return "OP_DUP";
case OP_NIP : return "OP_NIP";
case OP_OVER : return "OP_OVER";
case OP_PICK : return "OP_PICK";
case OP_ROLL : return "OP_ROLL";
case OP_ROT : return "OP_ROT";
case OP_SWAP : return "OP_SWAP";
case OP_TUCK : return "OP_TUCK";
// splice ops
case OP_CAT : return "OP_CAT";
2018-04-16 11:59:19 +02:00
case OP_SPLIT : return "OP_SPLIT";
case OP_NUM2BIN : return "OP_NUM2BIN";
case OP_BIN2NUM : return "OP_BIN2NUM";
case OP_SIZE : return "OP_SIZE";
// bit logic
case OP_INVERT : return "OP_INVERT";
case OP_AND : return "OP_AND";
case OP_OR : return "OP_OR";
case OP_XOR : return "OP_XOR";
case OP_EQUAL : return "OP_EQUAL";
case OP_EQUALVERIFY : return "OP_EQUALVERIFY";
2026-05-14 14:19:14 +02:00
case OP_DEFINE : return "OP_DEFINE";
case OP_INVOKE : return "OP_INVOKE";
// numeric
case OP_1ADD : return "OP_1ADD";
case OP_1SUB : return "OP_1SUB";
2026-05-14 14:19:14 +02:00
case OP_LSHIFTNUM : return "OP_LSHIFTNUM";
case OP_RSHIFTNUM : return "OP_RSHIFTNUM";
case OP_NEGATE : return "OP_NEGATE";
case OP_ABS : return "OP_ABS";
case OP_NOT : return "OP_NOT";
case OP_0NOTEQUAL : return "OP_0NOTEQUAL";
case OP_ADD : return "OP_ADD";
case OP_SUB : return "OP_SUB";
case OP_MUL : return "OP_MUL";
case OP_DIV : return "OP_DIV";
case OP_MOD : return "OP_MOD";
2026-05-14 14:19:14 +02:00
case OP_LSHIFTBIN : return "OP_LSHIFTBIN";
case OP_RSHIFTBIN : return "OP_RSHIFTBIN";
case OP_BOOLAND : return "OP_BOOLAND";
case OP_BOOLOR : return "OP_BOOLOR";
case OP_NUMEQUAL : return "OP_NUMEQUAL";
case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY";
case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL";
case OP_LESSTHAN : return "OP_LESSTHAN";
case OP_GREATERTHAN : return "OP_GREATERTHAN";
case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL";
case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL";
case OP_MIN : return "OP_MIN";
case OP_MAX : return "OP_MAX";
case OP_WITHIN : return "OP_WITHIN";
// crypto
case OP_RIPEMD160 : return "OP_RIPEMD160";
case OP_SHA1 : return "OP_SHA1";
case OP_SHA256 : return "OP_SHA256";
case OP_HASH160 : return "OP_HASH160";
case OP_HASH256 : return "OP_HASH256";
case OP_CODESEPARATOR : return "OP_CODESEPARATOR";
case OP_CHECKSIG : return "OP_CHECKSIG";
case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY";
case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG";
case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY";
2018-11-04 12:07:17 +01:00
// soft-forkable expansion
case OP_NOP1 : return "OP_NOP1";
2015-12-14 14:21:34 -05:00
case OP_CHECKLOCKTIMEVERIFY : return "OP_CHECKLOCKTIMEVERIFY";
case OP_NOP3 : return "OP_NOP3";
case OP_NOP4 : return "OP_NOP4";
case OP_NOP5 : return "OP_NOP5";
case OP_NOP6 : return "OP_NOP6";
case OP_NOP7 : return "OP_NOP7";
case OP_NOP8 : return "OP_NOP8";
case OP_NOP9 : return "OP_NOP9";
case OP_NOP10 : return "OP_NOP10";
2018-11-04 12:07:17 +01:00
// new opcodes hard-forked in.
case OP_CHECKDATASIG: return "OP_CHECKDATASIG";
case OP_CHECKDATASIGVERIFY: return "OP_CHECKDATASIGVERIFY";
2020-04-10 14:14:42 +02:00
case OP_REVERSEBYTES: return "OP_REVERSEBYTES";
case OP_INPUTINDEX: return "OP_INPUTINDEX";
case OP_ACTIVEBYTECODE: return "OP_ACTIVEBYTECODE";
case OP_TXVERSION: return "OP_TXVERSION";
case OP_TXINPUTCOUNT: return "OP_TXINPUTCOUNT";
case OP_TXOUTPUTCOUNT: return "OP_TXOUTPUTCOUNT";
case OP_TXLOCKTIME: return "OP_TXLOCKTIME";
case OP_UTXOVALUE: return "OP_UTXOVALUE";
case OP_UTXOBYTECODE: return "OP_UTXOBYTECODE";
case OP_OUTPOINTTXHASH: return "OP_OUTPOINTTXHASH";
case OP_OUTPOINTINDEX: return "OP_OUTPOINTINDEX";
case OP_INPUTBYTECODE: return "OP_INPUTBYTECODE";
case OP_INPUTSEQUENCENUMBER: return "OP_INPUTSEQUENCENUMBER";
case OP_OUTPUTVALUE: return "OP_OUTPUTVALUE";
case OP_OUTPUTBYTECODE: return "OP_OUTPUTBYTECODE";
2026-05-14 08:15:38 +02:00
case OP_UTXOTOKENCATEGORY: return "OP_UTXOTOKENCATEGORY";
case OP_UTXOTOKENCOMMITMENT: return "OP_UTXOTOKENCOMMITMENT";
case OP_UTXOTOKENAMOUNT: return "OP_UTXOTOKENAMOUNT";
case OP_OUTPUTTOKENCATEGORY: return "OP_OUTPUTTOKENCATEGORY";
case OP_OUTPUTTOKENCOMMITMENT: return "OP_OUTPUTTOKENCOMMITMENT";
case OP_OUTPUTTOKENAMOUNT: return "OP_OUTPUTTOKENAMOUNT";
2018-11-04 12:07:17 +01:00
// Note:
// The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum
// as kind of implementation hack, they are *NOT* real opcodes. If found in real
// Script, just let the default: case deal with them.
default:
return "OP_UNKNOWN";
}
}
bool CheckMinimalPush(const std::vector<uint8_t> &data, opcodetype opcode)
{
// Excludes OP_1NEGATE, OP_1-16 since they are by definition minimal.
if (opcode <= OP_0 || opcode > OP_PUSHDATA4)
// Anything outside of this range has nothing to do with data push
return true;
if (data.size() == 0) // Should use OP_0.
return opcode == OP_0;
if (data.size() == 1 && data[0] >= 1 && data[0] <= 16) // Should use OP_1 .. OP_16.
return false;
if (data.size() == 1 && data[0] == 0x81) // Should use OP_1NEGATE.
return false;
if (data.size() <= 75)
// Should use a direct push (opcode indicating number of bytes pushed + those bytes).
return opcode == data.size();
if (data.size() <= 255) // Should use OP_PUSHDATA.
return opcode == OP_PUSHDATA1;
if (data.size() <= 65535) // Should use OP_PUSHDATA2.
return opcode == OP_PUSHDATA2;
return true;
}
2026-05-07 21:10:10 +02:00
CScript &CScript::operator<<(const ScriptBigNum &b) {
*this << b.getvch();
return *this;
}
2026-05-07 15:55:57 +02:00
bool CScript::IsPayToScriptHash(std::vector<unsigned char> *hashBytesOut) const
{
2026-05-07 15:55:57 +02:00
if (this->size() == 23
&& (*this)[0] == OP_HASH160
&& (*this)[1] == 0x14
&& (*this)[22] == OP_EQUAL) {
2026-05-07 13:50:12 +02:00
if (hashBytesOut)
hashBytesOut->assign(begin() + 2, begin() + 22);
return true;
}
2026-05-07 15:55:57 +02:00
if (this->size() == 35
&& (*this)[0] == OP_HASH256
&& (*this)[1] == 0x20
&& (*this)[34] == OP_EQUAL) {
2026-05-07 13:50:12 +02:00
if (hashBytesOut)
hashBytesOut->assign(begin() + 2, begin() + 34);
return true;
}
return false;
}
2024-09-05 11:52:24 +02:00
// A witness program is a BTC concept based on segwit, that is not available on BitcoinCash.
// We can recognize it on it being any valid CScript that consists of a 1-byte push opcode
2019-04-17 16:20:05 +02:00
// followed by a data push between 2 and 40 bytes.
bool CScript::IsWitnessProgram() const
{
if (this->size() < 4 || this->size() > 42)
return false;
if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16))
return false;
if (size_t((*this)[1] + 2) == this->size())
return true;
return false;
}
2014-11-04 12:38:56 -05:00
bool CScript::IsPushOnly(const_iterator pc) const
{
while (pc < end())
{
opcodetype opcode;
if (!GetOp(pc, opcode))
return false;
// Note that IsPushOnly() *does* consider OP_RESERVED to be a
// push-type opcode, however execution of OP_RESERVED fails, so
// it's not relevant to P2SH/BIP62 as the scriptSig would fail prior to
// the P2SH special validation code being executed.
if (opcode > OP_16)
return false;
}
return true;
}
2014-11-04 12:38:56 -05:00
bool CScript::IsPushOnly() const
{
return this->IsPushOnly(begin());
}
2018-04-16 11:59:19 +02:00
std::vector<uint8_t>
MinimalizeBigEndianArray(const std::vector<uint8_t> &data)
{
std::vector<uint8_t> answer;
// Can't encode more than this, go ahead and grab as much room as we could
// possibly need
answer.reserve(data.size());
if (data.empty()) // Ensure we have a byte to work with.
return answer;
// Store the MSB
uint8_t neg = data[0] & 0x80;
bool havePushed = false;
for (size_t i = 0; i < data.size(); ++i) {
uint8_t x = data[i];
if (i == 0) // Remove any MSB that might exist
x &= 0x7f;
if (!havePushed && x == 0) // If we haven't pushed anything, and the current value is zero, keep ignoring bytes.
continue;
// Record that we have begun pushing, and store the current value.
havePushed = true;
answer.push_back(x);
}
if (answer.size() == 0) // Give us at least one byte
return answer;
// Only add back the sign if a value has been pushed. This implies the
// result is non-zero.
if (havePushed) {
// If the MSB is currently occupied, we need one extra byte.
if ((answer[0] & 0x80) != 0) {
answer.insert(answer.begin(), 0);
}
answer[0] |= neg;
}
return answer;
}
bool CScriptNum::isSmallestFormat(const std::vector<uint8_t> &vch, const size_t nMaxNumSize)
{
if (vch.size() > nMaxNumSize)
return false;
if (vch.size() > 0) {
// If the most-significant-byte - excluding the sign bit - is zero
// then we're not minimal. Note how this test also rejects the
// negative-zero encoding, 0x80.
if ((vch.back() & 0x7f) == 0) {
// One exception: if there's more than one byte and the most
// significant bit of the second-most-significant-byte is set it
// would conflict with the sign bit. An example of this case is
// +-255, which encode to 0xff00 and 0xff80 respectively.
// (big-endian).
if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) {
return false;
}
}
}
return true;
}
bool CScriptNum::createSmallestFormat(std::vector<uint8_t> &data)
{
if (data.empty())
return false;
// If the last byte is not 0x00 or 0x80, we are minimally encoded.
uint8_t last = data.back();
if (last & 0x7f)
return false;
// If the script is one byte long, then we have a zero, which encodes as an
// empty array.
if (data.size() == 1) {
data = {};
return true;
}
// If the next byte has it sign bit set, then we are minimaly encoded.
if (data[data.size() - 2] & 0x80)
return false;
// We are not minimally encoded, we need to figure out how much to trim.
for (size_t i = data.size() - 1; i > 0; i--) {
// We found a non zero byte, time to encode.
if (data[i - 1] != 0) {
if (data[i - 1] & 0x80) {
// We found a byte with it sign bit set so we need one more
// byte.
data[i++] = last;
} else {
// the sign bit is clear, we can use it.
data[i - 1] |= last;
}
data.resize(i);
return true;
}
}
// If we the whole thing is zeros, then we have a zero.
data = {};
return true;
}
2019-06-06 21:37:49 +02:00
2026-05-07 17:51:05 +02:00
namespace {
using ScriptBigInt = ScriptBigNum::BigInt;
size_t GetAbsBitCount(const ScriptBigInt &value)
{
if (value == 0)
return 1;
const ScriptBigInt magnitude = value < 0 ? -value : value;
return boost::multiprecision::msb(magnitude) + 1;
}
} // namespace
ScriptBigNum::ScriptBigNum(int64_t value)
: m_value(value)
{
}
ScriptBigNum::ScriptBigNum(const BigInt &value)
: m_value(value)
{
}
ScriptBigNum::ScriptBigNum(BigInt &&value)
: m_value(std::move(value))
{
}
ScriptBigNum::ScriptBigNum(const std::vector<unsigned char> &vch, bool fRequireMinimal,
size_t maxIntegerSize)
{
if (vch.size() > maxIntegerSize)
throw scriptnum_error("script number overflow",
maxIntegerSize > CScriptNum::MAXIMUM_ELEMENT_SIZE_64_BIT
? SCRIPT_ERR_INVALID_NUMBER_RANGE_BIG_INT
: SCRIPT_ERR_INVALID_NUMBER_RANGE);
if (fRequireMinimal && !CScriptNum::isSmallestFormat(vch, maxIntegerSize))
throw scriptnum_error("non-minimally encoded script number", SCRIPT_ERR_MINIMALDATA);
m_value = set_vch(vch);
}
size_t ScriptBigNum::absValNumBits(const BigInt &value)
{
return GetAbsBitCount(value);
}
bool ScriptBigNum::validRange(const BigInt &value)
{
return absValNumBits(value) <= MAX_BIG_INT_BITS;
}
bool ScriptBigNum::safeIncr()
{
return safeAddInPlace(1);
}
bool ScriptBigNum::safeDecr()
{
return safeSubInPlace(1);
}
bool ScriptBigNum::safeAddInPlace(const ScriptBigNum &rhs)
{
const BigInt previous = m_value;
m_value += rhs.m_value;
if (!validRange(m_value)) {
m_value = previous;
return false;
}
return true;
}
bool ScriptBigNum::safeAddInPlace(int64_t rhs)
{
const BigInt previous = m_value;
m_value += rhs;
if (!validRange(m_value)) {
m_value = previous;
return false;
}
return true;
}
bool ScriptBigNum::safeSubInPlace(const ScriptBigNum &rhs)
{
const BigInt previous = m_value;
m_value -= rhs.m_value;
if (!validRange(m_value)) {
m_value = previous;
return false;
}
return true;
}
bool ScriptBigNum::safeSubInPlace(int64_t rhs)
{
const BigInt previous = m_value;
m_value -= rhs;
if (!validRange(m_value)) {
m_value = previous;
return false;
}
return true;
}
bool ScriptBigNum::safeMulInPlace(const ScriptBigNum &rhs)
{
const BigInt previous = m_value;
m_value *= rhs.m_value;
if (!validRange(m_value)) {
m_value = previous;
return false;
}
return true;
}
bool ScriptBigNum::safeMulInPlace(int64_t rhs)
{
const BigInt previous = m_value;
m_value *= rhs;
if (!validRange(m_value)) {
m_value = previous;
return false;
}
return true;
}
ScriptBigNum& ScriptBigNum::operator/=(const ScriptBigNum &rhs)
{
m_value /= rhs.m_value;
return *this;
}
ScriptBigNum& ScriptBigNum::operator%=(const ScriptBigNum &rhs)
{
2026-05-09 23:50:04 +02:00
#if BOOST_VERSION < 108600
// a mod by zero was not caught prior to boost 1.86, so we handle it here.
if (rhs.m_value == 0)
throw std::overflow_error("Mod by zero.");
#endif
2026-05-07 17:51:05 +02:00
m_value %= rhs.m_value;
return *this;
}
ScriptBigNum& ScriptBigNum::negate()
{
m_value = -m_value;
return *this;
}
bool ScriptBigNum::checkedLeftShift(unsigned bitcount)
{
if (m_value == 0)
return true;
if (bitcount + absValNumBits() > MAX_BIG_INT_BITS)
return false;
m_value <<= bitcount;
return validRange(m_value);
}
bool ScriptBigNum::checkedRightShift(unsigned bitcount)
{
if (m_value == 0)
return true;
if (bitcount >= absValNumBits()) {
m_value = m_value < 0 ? -1 : 0;
return true;
}
if (m_value > 0) {
m_value >>= bitcount;
return validRange(m_value);
}
2026-05-07 22:05:11 +02:00
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
2026-05-07 17:51:05 +02:00
const BigInt divisor = BigInt(1) << bitcount;
2026-05-07 22:05:11 +02:00
#pragma GCC diagnostic pop
2026-05-07 17:51:05 +02:00
BigInt magnitude = -m_value;
BigInt quotient = magnitude / divisor;
const BigInt remainder = magnitude % divisor;
if (remainder != 0)
++quotient;
m_value = -quotient;
return validRange(m_value);
}
bool ScriptBigNum::isZero() const
{
return m_value == 0;
}
int32_t ScriptBigNum::getint32() const
{
static const BigInt kMin = std::numeric_limits<int32_t>::min();
static const BigInt kMax = std::numeric_limits<int32_t>::max();
if (m_value < kMin)
return std::numeric_limits<int32_t>::min();
if (m_value > kMax)
return std::numeric_limits<int32_t>::max();
return m_value.convert_to<int32_t>();
}
std::optional<int64_t> ScriptBigNum::getint64() const
{
static const BigInt kMin = std::numeric_limits<int64_t>::min();
static const BigInt kMax = std::numeric_limits<int64_t>::max();
if (m_value < kMin || m_value > kMax)
return std::nullopt;
return m_value.convert_to<int64_t>();
}
std::vector<unsigned char> ScriptBigNum::getvch() const
{
return serialize(m_value);
}
size_t ScriptBigNum::absValNumBits() const
{
return absValNumBits(m_value);
}
int ScriptBigNum::compare(const ScriptBigNum &rhs) const
{
if (m_value < rhs.m_value)
return -1;
if (m_value > rhs.m_value)
return 1;
return 0;
}
ScriptBigNum::BigInt ScriptBigNum::set_vch(const std::vector<unsigned char> &vch)
{
if (vch.empty())
return 0;
BigInt result = 0;
const bool neg = (vch.back() & 0x80) != 0;
for (size_t i = vch.size(); i-- > 0;) {
uint8_t byte = vch[i];
if (neg && i == vch.size() - 1)
byte &= 0x7f;
result <<= 8;
result += byte;
}
if (neg)
result = -result;
return result;
}
std::vector<unsigned char> ScriptBigNum::serialize(const BigInt &value)
{
if (value == 0)
return {};
std::vector<unsigned char> result;
const bool neg = value < 0;
BigInt magnitude = neg ? -value : value;
while (magnitude != 0) {
result.push_back((magnitude & 0xff).convert_to<unsigned char>());
magnitude >>= 8;
}
if (result.back() & 0x80)
result.push_back(neg ? 0x80 : 0x00);
else if (neg)
result.back() |= 0x80;
return result;
}
FastBigNum::FastBigNum(const std::vector<unsigned char> &vch, bool fRequireMinimal, size_t maxIntegerSize)
: m_value(CScriptNum(0))
{
if (maxIntegerSize > CScriptNum::MAXIMUM_ELEMENT_SIZE_64_BIT)
m_value = ScriptBigNum(vch, fRequireMinimal, maxIntegerSize);
else
m_value = CScriptNum(vch, fRequireMinimal, maxIntegerSize);
}
FastBigNum::FastBigNum(const CScriptNum &value)
: m_value(value)
{
}
FastBigNum::FastBigNum(const ScriptBigNum &value)
: m_value(value)
{
}
FastBigNum FastBigNum::fromIntUnchecked(int64_t value, bool useBigInt)
{
if (useBigInt)
return FastBigNum(ScriptBigNum(value));
return FastBigNum(CScriptNum(value));
}
bool FastBigNum::usesNative() const
{
return std::holds_alternative<CScriptNum>(m_value);
}
int32_t FastBigNum::getint32() const
{
if (usesNative())
return std::get<CScriptNum>(m_value).getint();
return std::get<ScriptBigNum>(m_value).getint32();
}
std::optional<int64_t> FastBigNum::getint64() const
{
if (usesNative())
return std::get<CScriptNum>(m_value).getint64();
return std::get<ScriptBigNum>(m_value).getint64();
}
std::vector<unsigned char> FastBigNum::getvch() const
{
if (usesNative())
return std::get<CScriptNum>(m_value).getvch();
return std::get<ScriptBigNum>(m_value).getvch();
}
size_t FastBigNum::absValNumBits() const
{
if (usesNative()) {
const int64_t value = std::get<CScriptNum>(m_value).getint64();
const uint64_t magnitude = value < 0 ? -static_cast<uint64_t>(value) : static_cast<uint64_t>(value);
if (magnitude == 0)
return 1;
size_t bits = 0;
uint64_t remaining = magnitude;
while (remaining != 0) {
++bits;
remaining >>= 1;
}
return bits;
}
return std::get<ScriptBigNum>(m_value).absValNumBits();
}
bool FastBigNum::safeAddInPlace(const FastBigNum &rhs)
{
if (usesNative() && rhs.usesNative())
return std::get<CScriptNum>(m_value).safeAddInPlace(std::get<CScriptNum>(rhs.m_value));
ScriptBigNum value = asBigNum();
if (!value.safeAddInPlace(rhs.asBigNum()))
return false;
m_value = std::move(value);
return true;
}
bool FastBigNum::safeIncr()
{
if (usesNative())
return std::get<CScriptNum>(m_value).safeIncr();
return std::get<ScriptBigNum>(m_value).safeIncr();
}
bool FastBigNum::safeSubInPlace(const FastBigNum &rhs)
{
if (usesNative() && rhs.usesNative())
return std::get<CScriptNum>(m_value).safeSubInPlace(std::get<CScriptNum>(rhs.m_value));
ScriptBigNum value = asBigNum();
if (!value.safeSubInPlace(rhs.asBigNum()))
return false;
m_value = std::move(value);
return true;
}
bool FastBigNum::safeDecr()
{
if (usesNative())
return std::get<CScriptNum>(m_value).safeDecr();
return std::get<ScriptBigNum>(m_value).safeDecr();
}
bool FastBigNum::safeMulInPlace(const FastBigNum &rhs)
{
if (usesNative() && rhs.usesNative())
return std::get<CScriptNum>(m_value).safeMulInPlace(std::get<CScriptNum>(rhs.m_value));
ScriptBigNum value = asBigNum();
if (!value.safeMulInPlace(rhs.asBigNum()))
return false;
m_value = std::move(value);
return true;
}
FastBigNum& FastBigNum::operator/=(const FastBigNum &rhs)
{
if (usesNative() && rhs.usesNative()) {
m_value = std::get<CScriptNum>(m_value) / std::get<CScriptNum>(rhs.m_value);
return *this;
}
ScriptBigNum value = asBigNum();
value /= rhs.asBigNum();
m_value = std::move(value);
return *this;
}
FastBigNum& FastBigNum::operator%=(const FastBigNum &rhs)
{
if (usesNative() && rhs.usesNative()) {
m_value = std::get<CScriptNum>(m_value) % std::get<CScriptNum>(rhs.m_value);
return *this;
}
ScriptBigNum value = asBigNum();
value %= rhs.asBigNum();
m_value = std::move(value);
return *this;
}
FastBigNum& FastBigNum::negate()
{
if (usesNative())
std::get<CScriptNum>(m_value).negate();
else
std::get<ScriptBigNum>(m_value).negate();
return *this;
}
bool FastBigNum::checkedLeftShift(unsigned bitcount)
{
if (!usesNative())
return std::get<ScriptBigNum>(m_value).checkedLeftShift(bitcount);
ScriptBigNum value = asBigNum();
if (!value.checkedLeftShift(bitcount))
return false;
m_value = std::move(value);
return true;
}
bool FastBigNum::checkedRightShift(unsigned bitcount)
{
if (!usesNative())
return std::get<ScriptBigNum>(m_value).checkedRightShift(bitcount);
ScriptBigNum value = asBigNum();
if (!value.checkedRightShift(bitcount))
return false;
m_value = std::move(value);
return true;
}
bool FastBigNum::isZero() const
{
if (usesNative())
return std::get<CScriptNum>(m_value).getint64() == 0;
return std::get<ScriptBigNum>(m_value).isZero();
}
int FastBigNum::compare(const FastBigNum &rhs) const
{
if (usesNative() && rhs.usesNative()) {
const CScriptNum &lhsNum = std::get<CScriptNum>(m_value);
const CScriptNum &rhsNum = std::get<CScriptNum>(rhs.m_value);
if (lhsNum < rhsNum)
return -1;
if (lhsNum > rhsNum)
return 1;
return 0;
}
return asBigNum().compare(rhs.asBigNum());
}
ScriptBigNum FastBigNum::asBigNum() const
{
if (usesNative())
return ScriptBigNum(std::get<CScriptNum>(m_value).getint64());
return std::get<ScriptBigNum>(m_value);
}
2019-06-06 21:37:49 +02:00
const char* Script::getTxnOutputType(Script::TxnOutType t)
{
switch (t)
{
case TX_NONSTANDARD: return "nonstandard";
case TX_PUBKEY: return "pubkey";
case TX_PUBKEYHASH: return "pubkeyhash";
case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig";
case TX_NULL_DATA: return "nulldata";
2026-05-14 14:37:19 +02:00
case TX_SCRIPT: return "script";
2019-06-06 21:37:49 +02:00
}
return nullptr;
}
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
2019-06-06 21:37:49 +02:00
struct SolverHelper {
std::multimap<Script::TxnOutType, CScript> templates;
SolverHelper() {
// Standard tx, sender provides pubkey, receiver adds signature
2021-03-06 14:30:20 +01:00
templates.insert(std::make_pair(Script::TX_PUBKEY, CScript() << PUBKEY_PLACEHOLDER << OP_CHECKSIG));
2019-06-06 21:37:49 +02:00
// Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey
templates.insert(std::make_pair(Script::TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160
2021-03-06 14:30:20 +01:00
<< PUBKEYHASH_PLACEHOLDER << OP_EQUALVERIFY << OP_CHECKSIG));
2019-06-06 21:37:49 +02:00
// Sender provides N pubkeys, receivers provides M signatures
2021-03-06 14:30:20 +01:00
templates.insert(std::make_pair(Script::TX_MULTISIG, CScript() << SMALLINTEGER_PLACEHOLDER << PUBKEYS_PLACEHOLDER
<< SMALLINTEGER_PLACEHOLDER << OP_CHECKMULTISIG));
2019-06-06 21:37:49 +02:00
}
};
/**
* Return public keys or hashes from scriptPubKey, for 'standard' transaction types.
*/
2026-05-07 15:55:57 +02:00
bool Script::solver(const CScript &scriptPubKey, Script::TxnOutType &typeRet, std::vector<std::vector<unsigned char> > &vSolutionsRet)
2019-06-06 21:37:49 +02:00
{
static SolverHelper sh;
vSolutionsRet.clear();
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
2026-05-07 13:50:12 +02:00
// it is always OP_HASH160/OP_HASH256, direct hash push, OP_EQUAL
std::vector<unsigned char> hashBytes;
2026-05-07 15:55:57 +02:00
if (scriptPubKey.IsPayToScriptHash(&hashBytes)) {
2019-06-06 21:37:49 +02:00
typeRet = TX_SCRIPTHASH;
vSolutionsRet.push_back(hashBytes);
return true;
}
// Provably prunable, data-carrying output
//
// So long as script passes the IsUnspendable() test and all but the first
// byte passes the IsPushOnly() test we don't care what exactly is in the
// script.
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
typeRet = TX_NULL_DATA;
return true;
}
// Scan templates
const CScript& script1 = scriptPubKey;
2021-01-05 21:29:44 +01:00
for (const std::pair<const Script::TxnOutType, CScript> &tplate : sh.templates) {
2019-06-06 21:37:49 +02:00
const CScript& script2 = tplate.second;
vSolutionsRet.clear();
opcodetype opcode1, opcode2;
std::vector<unsigned char> vch1, vch2;
// Compare
CScript::const_iterator pc1 = script1.begin();
CScript::const_iterator pc2 = script2.begin();
while (true) {
if (pc1 == script1.end() && pc2 == script2.end()) {
// Found a match
typeRet = tplate.first;
if (typeRet == TX_MULTISIG) {
// Additional checks for TX_MULTISIG:
unsigned char m = vSolutionsRet.front()[0];
unsigned char n = vSolutionsRet.back()[0];
if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n)
break;
2019-06-06 21:37:49 +02:00
}
return true;
}
if (!script1.GetOp(pc1, opcode1, vch1))
break;
if (!script2.GetOp(pc2, opcode2, vch2))
break;
// Template matching opcodes:
2021-03-06 14:30:20 +01:00
if (opcode2 == PUBKEYS_PLACEHOLDER) {
2023-11-24 18:01:36 +01:00
while (PublicKeyUtils::isValidSize(vch1)) {
2019-06-06 21:37:49 +02:00
vSolutionsRet.push_back(vch1);
if (!script1.GetOp(pc1, opcode1, vch1))
break;
}
if (!script2.GetOp(pc2, opcode2, vch2))
break;
// Normal situation is to fall through
// to other if/else statements
}
2021-03-06 14:30:20 +01:00
if (opcode2 == PUBKEY_PLACEHOLDER) {
2023-11-24 18:01:36 +01:00
if (!PublicKeyUtils::isValidSize(vch1))
2019-06-06 21:37:49 +02:00
break;
vSolutionsRet.push_back(vch1);
}
2021-03-06 14:30:20 +01:00
else if (opcode2 == PUBKEYHASH_PLACEHOLDER) {
2019-06-06 21:37:49 +02:00
if (vch1.size() != sizeof(uint160))
break;
vSolutionsRet.push_back(vch1);
}
2021-03-06 14:30:20 +01:00
else if (opcode2 == SMALLINTEGER_PLACEHOLDER) { // Single-byte small integer pushed onto vSolutions
2019-06-06 21:37:49 +02:00
if (opcode1 == OP_0 || (opcode1 >= OP_1 && opcode1 <= OP_16)) {
unsigned char n = static_cast<unsigned char>(CScript::DecodeOP_N(opcode1));
2019-06-06 21:37:49 +02:00
vSolutionsRet.push_back(std::vector<unsigned char>(1, n));
}
else
break;
}
else if (opcode1 != opcode2 || vch1 != vch2) {
// Others must match exactly
break;
}
}
}
vSolutionsRet.clear();
2026-05-14 14:37:19 +02:00
if (scriptPubKey.size() <= MAX_P2S_SCRIPT_SIZE) {
typeRet = TX_SCRIPT;
return true;
}
2019-06-06 21:37:49 +02:00
typeRet = TX_NONSTANDARD;
return false;
}