#ifndef BITCOIN_SCRIPT_SCRIPT_H
#define BITCOIN_SCRIPT_SCRIPT_H
#include <attributes.h>
#include <crypto/common.h>
#include <prevector.h>
#include <serialize.h>
#include <uint256.h>
#include <util/hash_type.h>
#include <cassert>
#include <cstdint>
#include <cstring>
#include <limits>
#include <span>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520;
static const int MAX_OPS_PER_SCRIPT = 201;
static const int MAX_PUBKEYS_PER_MULTISIG = 20;
static constexpr unsigned int MAX_PUBKEYS_PER_MULTI_A = 999;
static const int MAX_SCRIPT_SIZE = 10000;
static const int MAX_STACK_SIZE = 1000;
static const unsigned int LOCKTIME_THRESHOLD = 500000000;
static const uint32_t LOCKTIME_MAX = 0xFFFFFFFFU;
static constexpr unsigned int ANNEX_TAG = 0x50;
static constexpr int64_t VALIDATION_WEIGHT_PER_SIGOP_PASSED{50};
static constexpr int64_t VALIDATION_WEIGHT_OFFSET{50};
template <typename T>
std::vector<unsigned char> ToByteVector(const T& in)
{
return std::vector<unsigned char>(in.begin(), in.end());
}
enum opcodetype
{
OP_0 = 0x00,
OP_FALSE = OP_0,
OP_PUSHDATA1 = 0x4c,
OP_PUSHDATA2 = 0x4d,
OP_PUSHDATA4 = 0x4e,
OP_1NEGATE = 0x4f,
OP_RESERVED = 0x50,
OP_1 = 0x51,
OP_TRUE=OP_1,
OP_2 = 0x52,
OP_3 = 0x53,
OP_4 = 0x54,
OP_5 = 0x55,
OP_6 = 0x56,
OP_7 = 0x57,
OP_8 = 0x58,
OP_9 = 0x59,
OP_10 = 0x5a,
OP_11 = 0x5b,
OP_12 = 0x5c,
OP_13 = 0x5d,
OP_14 = 0x5e,
OP_15 = 0x5f,
OP_16 = 0x60,
OP_NOP = 0x61,
OP_VER = 0x62,
OP_IF = 0x63,
OP_NOTIF = 0x64,
OP_VERIF = 0x65,
OP_VERNOTIF = 0x66,
OP_ELSE = 0x67,
OP_ENDIF = 0x68,
OP_VERIFY = 0x69,
OP_RETURN = 0x6a,
OP_TOALTSTACK = 0x6b,
OP_FROMALTSTACK = 0x6c,
OP_2DROP = 0x6d,
OP_2DUP = 0x6e,
OP_3DUP = 0x6f,
OP_2OVER = 0x70,
OP_2ROT = 0x71,
OP_2SWAP = 0x72,
OP_IFDUP = 0x73,
OP_DEPTH = 0x74,
OP_DROP = 0x75,
OP_DUP = 0x76,
OP_NIP = 0x77,
OP_OVER = 0x78,
OP_PICK = 0x79,
OP_ROLL = 0x7a,
OP_ROT = 0x7b,
OP_SWAP = 0x7c,
OP_TUCK = 0x7d,
OP_CAT = 0x7e,
OP_SUBSTR = 0x7f,
OP_LEFT = 0x80,
OP_RIGHT = 0x81,
OP_SIZE = 0x82,
OP_INVERT = 0x83,
OP_AND = 0x84,
OP_OR = 0x85,
OP_XOR = 0x86,
OP_EQUAL = 0x87,
OP_EQUALVERIFY = 0x88,
OP_RESERVED1 = 0x89,
OP_RESERVED2 = 0x8a,
OP_1ADD = 0x8b,
OP_1SUB = 0x8c,
OP_2MUL = 0x8d,
OP_2DIV = 0x8e,
OP_NEGATE = 0x8f,
OP_ABS = 0x90,
OP_NOT = 0x91,
OP_0NOTEQUAL = 0x92,
OP_ADD = 0x93,
OP_SUB = 0x94,
OP_MUL = 0x95,
OP_DIV = 0x96,
OP_MOD = 0x97,
OP_LSHIFT = 0x98,
OP_RSHIFT = 0x99,
OP_BOOLAND = 0x9a,
OP_BOOLOR = 0x9b,
OP_NUMEQUAL = 0x9c,
OP_NUMEQUALVERIFY = 0x9d,
OP_NUMNOTEQUAL = 0x9e,
OP_LESSTHAN = 0x9f,
OP_GREATERTHAN = 0xa0,
OP_LESSTHANOREQUAL = 0xa1,
OP_GREATERTHANOREQUAL = 0xa2,
OP_MIN = 0xa3,
OP_MAX = 0xa4,
OP_WITHIN = 0xa5,
OP_RIPEMD160 = 0xa6,
OP_SHA1 = 0xa7,
OP_SHA256 = 0xa8,
OP_HASH160 = 0xa9,
OP_HASH256 = 0xaa,
OP_CODESEPARATOR = 0xab,
OP_CHECKSIG = 0xac,
OP_CHECKSIGVERIFY = 0xad,
OP_CHECKMULTISIG = 0xae,
OP_CHECKMULTISIGVERIFY = 0xaf,
OP_CHECKSIGFROMSTACK = 0xcc,
OP_NOP1 = 0xb0,
OP_CHECKLOCKTIMEVERIFY = 0xb1,
OP_NOP2 = OP_CHECKLOCKTIMEVERIFY,
OP_CHECKSEQUENCEVERIFY = 0xb2,
OP_NOP3 = OP_CHECKSEQUENCEVERIFY,
OP_CHECKTEMPLATEVERIFY = 0xb3,
OP_NOP4 = OP_CHECKTEMPLATEVERIFY,
OP_NOP5 = 0xb4,
OP_NOP6 = 0xb5,
OP_NOP7 = 0xb6,
OP_NOP8 = 0xb7,
OP_NOP9 = 0xb8,
OP_NOP10 = 0xb9,
OP_CHECKSIGADD = 0xba,
OP_INVALIDOPCODE = 0xff,
};
static const unsigned int MAX_OPCODE = OP_NOP10;
std::string GetOpName(opcodetype opcode);
class scriptnum_error : public std::runtime_error
{
public:
explicit scriptnum_error(const std::string& str) : std::runtime_error(str) {}
};
class CScriptNum
{
public:
explicit CScriptNum(const int64_t& n)
{
m_value = n;
}
static const size_t nDefaultMaxNumSize = 4;
explicit CScriptNum(const std::vector<unsigned char>& vch, bool fRequireMinimal,
const size_t nMaxNumSize = nDefaultMaxNumSize)
{
if (vch.size() > nMaxNumSize) {
throw scriptnum_error("script number overflow");
}
if (fRequireMinimal && vch.size() > 0) {
if ((vch.back() & 0x7f) == 0) {
if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) {
throw scriptnum_error("non-minimally encoded script number");
}
}
}
m_value = set_vch(vch);
}
inline bool operator==(const int64_t& rhs) const { return m_value == rhs; }
inline bool operator!=(const int64_t& rhs) const { return m_value != rhs; }
inline bool operator<=(const int64_t& rhs) const { return m_value <= rhs; }
inline bool operator< (const int64_t& rhs) const { return m_value < rhs; }
inline bool operator>=(const int64_t& rhs) const { return m_value >= rhs; }
inline bool operator> (const int64_t& rhs) const { return m_value > rhs; }
inline bool operator==(const CScriptNum& rhs) const { return operator==(rhs.m_value); }
inline bool operator!=(const CScriptNum& rhs) const { return operator!=(rhs.m_value); }
inline bool operator<=(const CScriptNum& rhs) const { return operator<=(rhs.m_value); }
inline bool operator< (const CScriptNum& rhs) const { return operator< (rhs.m_value); }
inline bool operator>=(const CScriptNum& rhs) const { return operator>=(rhs.m_value); }
inline bool operator> (const CScriptNum& rhs) const { return operator> (rhs.m_value); }
inline CScriptNum operator+( const int64_t& rhs) const { return CScriptNum(m_value + rhs);}
inline CScriptNum operator-( const int64_t& rhs) const { return CScriptNum(m_value - rhs);}
inline CScriptNum operator+( const CScriptNum& rhs) const { return operator+(rhs.m_value); }
inline CScriptNum operator-( const CScriptNum& rhs) const { return operator-(rhs.m_value); }
inline CScriptNum& operator+=( const CScriptNum& rhs) { return operator+=(rhs.m_value); }
inline CScriptNum& operator-=( const CScriptNum& rhs) { return operator-=(rhs.m_value); }
inline CScriptNum operator&( const int64_t& rhs) const { return CScriptNum(m_value & rhs);}
inline CScriptNum operator&( const CScriptNum& rhs) const { return operator&(rhs.m_value); }
inline CScriptNum& operator&=( const CScriptNum& rhs) { return operator&=(rhs.m_value); }
inline CScriptNum operator-() const
{
assert(m_value != std::numeric_limits<int64_t>::min());
return CScriptNum(-m_value);
}
inline CScriptNum& operator=( const int64_t& rhs)
{
m_value = rhs;
return *this;
}
inline CScriptNum& operator+=( const int64_t& rhs)
{
assert(rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) ||
(rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs));
m_value += rhs;
return *this;
}
inline CScriptNum& operator-=( const int64_t& rhs)
{
assert(rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) ||
(rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs));
m_value -= rhs;
return *this;
}
inline CScriptNum& operator&=( const int64_t& rhs)
{
m_value &= rhs;
return *this;
}
int getint() const
{
if (m_value > std::numeric_limits<int>::max())
return std::numeric_limits<int>::max();
else if (m_value < std::numeric_limits<int>::min())
return std::numeric_limits<int>::min();
return m_value;
}
int64_t GetInt64() const { return m_value; }
std::vector<unsigned char> getvch() const
{
return serialize(m_value);
}
static std::vector<unsigned char> serialize(const int64_t& value)
{
if(value == 0)
return std::vector<unsigned char>();
std::vector<unsigned char> result;
const bool neg = value < 0;
uint64_t absvalue = neg ? ~static_cast<uint64_t>(value) + 1 : static_cast<uint64_t>(value);
while(absvalue)
{
result.push_back(absvalue & 0xff);
absvalue >>= 8;
}
if (result.back() & 0x80)
result.push_back(neg ? 0x80 : 0);
else if (neg)
result.back() |= 0x80;
return result;
}
private:
static int64_t set_vch(const std::vector<unsigned char>& vch)
{
if (vch.empty())
return 0;
int64_t result = 0;
for (size_t i = 0; i != vch.size(); ++i)
result |= static_cast<int64_t>(vch[i]) << 8*i;
if (vch.back() & 0x80)
return -((int64_t)(result & ~(0x80ULL << (8 * (vch.size() - 1)))));
return result;
}
int64_t m_value;
};
typedef prevector<28, unsigned char> CScriptBase;
bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet);
class CScript : public CScriptBase
{
private:
inline void AppendDataSize(const uint32_t size)
{
if (size < OP_PUSHDATA1) {
insert(end(), static_cast<value_type>(size));
} else if (size <= 0xff) {
insert(end(), OP_PUSHDATA1);
insert(end(), static_cast<value_type>(size));
} else if (size <= 0xffff) {
insert(end(), OP_PUSHDATA2);
value_type data[2];
WriteLE16(data, size);
insert(end(), std::cbegin(data), std::cend(data));
} else {
insert(end(), OP_PUSHDATA4);
value_type data[4];
WriteLE32(data, size);
insert(end(), std::cbegin(data), std::cend(data));
}
}
void AppendData(std::span<const value_type> data)
{
insert(end(), data.begin(), data.end());
}
protected:
CScript& push_int64(int64_t n)
{
if (n == -1 || (n >= 1 && n <= 16))
{
push_back(n + (OP_1 - 1));
}
else if (n == 0)
{
push_back(OP_0);
}
else
{
*this << CScriptNum::serialize(n);
}
return *this;
}
public:
CScript() = default;
template <std::input_iterator InputIterator>
CScript(InputIterator first, InputIterator last) : CScriptBase{first, last} { }
SERIALIZE_METHODS(CScript, obj) { READWRITE(AsBase<CScriptBase>(obj)); }
explicit CScript(int64_t b) { operator<<(b); }
explicit CScript(opcodetype b) { operator<<(b); }
explicit CScript(const CScriptNum& b) { operator<<(b); }
explicit CScript(const std::vector<unsigned char>& b) = delete;
CScript& operator<<(const CScript& b) = delete;
CScript& operator<<(int64_t b) LIFETIMEBOUND { return push_int64(b); }
CScript& operator<<(opcodetype opcode) LIFETIMEBOUND
{
if (opcode < 0 || opcode > 0xff)
throw std::runtime_error("CScript::operator<<(): invalid opcode");
insert(end(), (unsigned char)opcode);
return *this;
}
CScript& operator<<(const CScriptNum& b) LIFETIMEBOUND
{
*this << b.getvch();
return *this;
}
CScript& operator<<(std::span<const std::byte> b) LIFETIMEBOUND
{
AppendDataSize(b.size());
AppendData({reinterpret_cast<const value_type*>(b.data()), b.size()});
return *this;
}
CScript& operator<<(std::span<const value_type> b) LIFETIMEBOUND
{
return *this << std::as_bytes(b);
}
bool GetOp(const_iterator& pc, opcodetype& opcodeRet, std::vector<unsigned char>& vchRet) const
{
return GetScriptOp(pc, end(), opcodeRet, &vchRet);
}
bool GetOp(const_iterator& pc, opcodetype& opcodeRet) const
{
return GetScriptOp(pc, end(), opcodeRet, nullptr);
}
static int DecodeOP_N(opcodetype opcode)
{
if (opcode == OP_0)
return 0;
assert(opcode >= OP_1 && opcode <= OP_16);
return (int)opcode - (int)(OP_1 - 1);
}
static opcodetype EncodeOP_N(int n)
{
assert(n >= 0 && n <= 16);
if (n == 0)
return OP_0;
return (opcodetype)(OP_1+n-1);
}
unsigned int GetSigOpCount(bool fAccurate) const;
unsigned int GetSigOpCount(const CScript& scriptSig) const;
bool IsPayToAnchor() const;
static bool IsPayToAnchor(int version, const std::vector<unsigned char>& program);
bool IsPayToBareDefaultCheckTemplateVerifyHash() const;
bool IsPayToScriptHash() const;
bool IsPayToWitnessScriptHash() const;
bool IsWitnessProgram(int& version, std::vector<unsigned char>& program) const;
bool IsPayToTaproot() const;
bool IsPushOnly(const_iterator pc) const;
bool IsPushOnly() const;
bool HasValidOps() const;
bool IsUnspendable() const
{
return (size() > 0 && *begin() == OP_RETURN) || (size() > MAX_SCRIPT_SIZE);
}
void clear()
{
CScriptBase::clear();
shrink_to_fit();
}
};
struct CScriptWitness
{
std::vector<std::vector<unsigned char> > stack;
CScriptWitness() = default;
bool IsNull() const { return stack.empty(); }
void SetNull() { stack.clear(); stack.shrink_to_fit(); }
std::string ToString() const;
};
class CScriptID : public BaseHash<uint160>
{
public:
CScriptID() : BaseHash() {}
explicit CScriptID(const CScript& in);
explicit CScriptID(const uint160& in) : BaseHash(in) {}
};
bool IsOpSuccess(const opcodetype& opcode);
bool CheckMinimalPush(const std::vector<unsigned char>& data, opcodetype opcode);
template<typename... Ts>
CScript BuildScript(Ts&&... inputs)
{
CScript ret;
int cnt{0};
([&ret, &cnt] (Ts&& input) {
if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<Ts>>, CScript>) {
if (cnt == 0) {
ret = std::forward<Ts>(input);
} else {
ret.insert(ret.end(), input.begin(), input.end());
}
} else {
ret << input;
}
cnt++;
} (std::forward<Ts>(inputs)), ...);
return ret;
}
#endif