Skip to content

Commit

Permalink
script: use CScriptNum instead of CBigNum
Browse files Browse the repository at this point in the history
  • Loading branch information
div72 committed Oct 8, 2023
1 parent 81b4123 commit 5ed8799
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 35 deletions.
46 changes: 17 additions & 29 deletions src/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "script.h"
#include <crypto/sha1.h>
#include "keystore.h"
#include "bignum.h"
#include "key.h"
#include "main.h"
#include "random.h"
Expand All @@ -26,20 +25,10 @@ bool CheckSig(vector<unsigned char> vchSig, vector<unsigned char> vchPubKey, CSc
static const valtype vchFalse(0);
static const valtype vchZero(0);
static const valtype vchTrue(1, 1);
static const CBigNum bnZero(0);
static const CBigNum bnOne(1);
static const CBigNum bnFalse(0);
static const CBigNum bnTrue(1);
static const size_t nMaxNumSize = 4;


CBigNum CastToBigNum(const valtype& vch)
{
if (vch.size() > nMaxNumSize)
throw runtime_error("CastToBigNum() : overflow");
// Get rid of extra leading zeros
return CBigNum(CBigNum(vch).getvch());
}
static const CScriptNum bnZero(0);
static const CScriptNum bnOne(1);
static const CScriptNum bnFalse(0);
static const CScriptNum bnTrue(1);

bool CastToBool(const valtype& vch)
{
Expand Down Expand Up @@ -332,7 +321,6 @@ static bool IsCanonicalSignature(const valtype &vchSig) {

bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType)
{
CAutoBN_CTX pctx;
CScript::const_iterator pc = script.begin();
CScript::const_iterator pend = script.end();
CScript::const_iterator pbegincodehash = script.begin();
Expand Down Expand Up @@ -405,7 +393,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
case OP_16:
{
// ( -- value)
CBigNum bn((int)opcode - (int)(OP_1 - 1));
CScriptNum bn((int)opcode - (int)(OP_1 - 1));
stack.push_back(bn.getvch());
}
break;
Expand Down Expand Up @@ -581,7 +569,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
case OP_DEPTH:
{
// -- stacksize
CBigNum bn(stack.size());
CScriptNum bn(stack.size());
stack.push_back(bn.getvch());
}
break;
Expand Down Expand Up @@ -631,7 +619,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
if (stack.size() < 2)
return false;
int n = CastToBigNum(stacktop(-1)).getint();
int n = CScriptNum(stacktop(-1), false).getint();
popstack(stack);
if (n < 0 || n >= (int)stack.size())
return false;
Expand Down Expand Up @@ -678,7 +666,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (in -- in size)
if (stack.size() < 1)
return false;
CBigNum bn(stacktop(-1).size());
CScriptNum bn(stacktop(-1).size());
stack.push_back(bn.getvch());
}
break;
Expand Down Expand Up @@ -725,7 +713,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (in -- out)
if (stack.size() < 1)
return false;
CBigNum bn = CastToBigNum(stacktop(-1));
CScriptNum bn(stacktop(-1), false);
switch (opcode)
{
case OP_1ADD: bn += bnOne; break;
Expand Down Expand Up @@ -758,9 +746,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (x1 x2 -- out)
if (stack.size() < 2)
return false;
CBigNum bn1 = CastToBigNum(stacktop(-2));
CBigNum bn2 = CastToBigNum(stacktop(-1));
CBigNum bn;
CScriptNum bn1(stacktop(-2), false);
CScriptNum bn2(stacktop(-1), false);
CScriptNum bn(0);
switch (opcode)
{
case OP_ADD:
Expand Down Expand Up @@ -803,9 +791,9 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
// (x min max -- out)
if (stack.size() < 3)
return false;
CBigNum bn1 = CastToBigNum(stacktop(-3));
CBigNum bn2 = CastToBigNum(stacktop(-2));
CBigNum bn3 = CastToBigNum(stacktop(-1));
CScriptNum bn1(stacktop(-3), false);
CScriptNum bn2(stacktop(-2), false);
CScriptNum bn3(stacktop(-1), false);
bool fValue = (bn2 <= bn1 && bn1 < bn3);
popstack(stack);
popstack(stack);
Expand Down Expand Up @@ -898,7 +886,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if ((int)stack.size() < i)
return false;

int nKeysCount = CastToBigNum(stacktop(-i)).getint();
int nKeysCount = CScriptNum(stacktop(-i), false).getint();
if (nKeysCount < 0 || nKeysCount > 20)
return false;
nOpCount += nKeysCount;
Expand All @@ -909,7 +897,7 @@ bool EvalScript(vector<vector<unsigned char> >& stack, const CScript& script, co
if ((int)stack.size() < i)
return false;

int nSigsCount = CastToBigNum(stacktop(-i)).getint();
int nSigsCount = CScriptNum(stacktop(-i), false).getint();
if (nSigsCount < 0 || nSigsCount > nKeysCount)
return false;
int isig = ++i;
Expand Down
193 changes: 187 additions & 6 deletions src/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@


#include "keystore.h"
#include "bignum.h"
#include "prevector.h"
#include <util/hash_type.h>
#include "wallet/ismine.h"
Expand Down Expand Up @@ -246,11 +245,193 @@ enum opcodetype
const char* GetOpName(opcodetype opcode);


class scriptnum_error : public std::runtime_error
{
public:
explicit scriptnum_error(const std::string& str) : std::runtime_error(str) {}
};

class CScriptNum
{
/**
* Numeric opcodes (OP_1ADD, etc) are restricted to operating on 4-byte integers.
* The semantics are subtle, though: operands must be in the range [-2^31 +1...2^31 -1],
* but results may overflow (and are valid as long as they are not used in a subsequent
* numeric operation). CScriptNum enforces those semantics by storing results as
* an int64 and allowing out-of-range values to be returned as a vector of bytes but
* throwing an exception if arithmetic is done or the result is interpreted as an integer.
*/
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) {
// Check that the number is encoded with the minimum possible
// number of bytes.
//
// 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) {
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 the most significant byte is >= 0x80 and the value is positive, push a
// new zero-byte to make the significant byte < 0x80 again.

// - If the most significant byte is >= 0x80 and the value is negative, push a
// new 0x80 byte that will be popped off when converting to an integral.

// - If the most significant byte is < 0x80 and the value is negative, add
// 0x80 to it, since it will be subtracted and interpreted as a negative when
// converting to an integral.

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 the input vector's most significant byte is 0x80, remove it from
// the result's msb and return a negative.
if (vch.back() & 0x80)
return -((int64_t)(result & ~(0x80ULL << (8 * (vch.size() - 1)))));

return result;
}

int64_t m_value;
};

inline std::string ValueString(const std::vector<unsigned char>& vch)
{
if (vch.size() <= 4)
return strprintf("%d", CBigNum(vch).getint());
return strprintf("%d", CScriptNum(vch, false).getint());
else
return HexStr(vch);
}
Expand All @@ -269,7 +450,7 @@ class CScript : public CScriptBase
}
else
{
CBigNum bn(n);
CScriptNum bn(n);
*this << bn.getvch();
}
return *this;
Expand All @@ -283,7 +464,7 @@ class CScript : public CScriptBase
}
else
{
CBigNum bn(n);
CScriptNum bn(n);
*this << bn.getvch();
}
return *this;
Expand Down Expand Up @@ -334,7 +515,7 @@ class CScript : public CScriptBase

explicit CScript(opcodetype b) { operator<<(b); }
explicit CScript(const uint256& b) { operator<<(b); }
explicit CScript(const CBigNum& b) { operator<<(b); }
explicit CScript(const CScriptNum& b) { operator<<(b); }
explicit CScript(const std::vector<unsigned char>& b) { operator<<(b); }


Expand Down Expand Up @@ -378,7 +559,7 @@ class CScript : public CScriptBase
return (*this) << vchKey;
}

CScript& operator<<(const CBigNum& b)
CScript& operator<<(const CScriptNum& b)
{
*this << b.getvch();
return *this;
Expand Down

0 comments on commit 5ed8799

Please sign in to comment.