2018-12-28 12:10:28 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
|
|
|
|
* Copyright (C) 2012-2015 The Bitcoin Core developers
|
2026-05-07 22:10:35 +02:00
|
|
|
* Copyright (C) 2026 Tom Zander <tom@flowee.org>
|
2018-12-28 12:10:28 +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 "scriptnum_tests.h"
|
|
|
|
|
#include "scriptnum10.h"
|
2019-03-11 14:47:12 +01:00
|
|
|
#include "primitives/script.h"
|
2026-05-07 21:10:10 +02:00
|
|
|
#include "primitives/ScriptBigNum_p.h"
|
2026-05-07 17:51:05 +02:00
|
|
|
#include "script/interpreter.h"
|
2018-12-28 12:10:28 +01:00
|
|
|
|
|
|
|
|
static const int64_t values[] = \
|
|
|
|
|
{ 0, 1, CHAR_MIN, CHAR_MAX, UCHAR_MAX, SHRT_MIN, USHRT_MAX, INT_MIN, INT_MAX, UINT_MAX, LONG_MIN, LONG_MAX };
|
|
|
|
|
static const int64_t offsets[] = { 1, 0x79, 0x80, 0x81, 0xFF, 0x7FFF, 0x8000, 0xFFFF, 0x10000};
|
|
|
|
|
|
|
|
|
|
static bool verify(const CScriptNum10& bignum, const CScriptNum& scriptnum)
|
|
|
|
|
{
|
|
|
|
|
return bignum.getvch() == scriptnum.getvch() && bignum.getint() == scriptnum.getint();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 17:51:05 +02:00
|
|
|
typedef std::vector<unsigned char> valtype;
|
|
|
|
|
typedef std::vector<valtype> stacktype;
|
|
|
|
|
|
|
|
|
|
static bool EvalScript(uint32_t flags, stacktype &stack, const CScript &script, ScriptError &error)
|
|
|
|
|
{
|
|
|
|
|
BaseSignatureChecker checker;
|
|
|
|
|
Script::State state(flags);
|
|
|
|
|
const bool result = Script::eval(stack, script, checker, state);
|
|
|
|
|
error = state.error;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static valtype ToScriptNumVch(const ScriptBigNum::BigInt &value)
|
|
|
|
|
{
|
|
|
|
|
return ScriptBigNum(value).getvch();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static valtype ToBoolVch(bool value)
|
|
|
|
|
{
|
|
|
|
|
return CScriptNum(value ? 1 : 0).getvch();
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-28 12:10:28 +01:00
|
|
|
static void RunOperators(const int64_t& num1, const int64_t& num2)
|
|
|
|
|
{
|
|
|
|
|
const CScriptNum10 bignum1(num1);
|
|
|
|
|
const CScriptNum10 bignum2(num2);
|
|
|
|
|
const CScriptNum scriptnum1(num1);
|
|
|
|
|
const CScriptNum scriptnum2(num2);
|
|
|
|
|
CScriptNum10 bignum3(num1);
|
|
|
|
|
CScriptNum10 bignum4(num1);
|
|
|
|
|
CScriptNum scriptnum3(num1);
|
|
|
|
|
CScriptNum scriptnum4(num1);
|
|
|
|
|
|
|
|
|
|
// int64_t overflow is undefined.
|
|
|
|
|
bool invalid = (((num2 > 0) && (num1 > (std::numeric_limits<int64_t>::max() - num2))) ||
|
|
|
|
|
((num2 < 0) && (num1 < (std::numeric_limits<int64_t>::min() - num2))));
|
|
|
|
|
if (!invalid) {
|
|
|
|
|
QVERIFY(verify(bignum1 + bignum2, scriptnum1 + scriptnum2));
|
|
|
|
|
QVERIFY(verify(bignum1 + bignum2, scriptnum1 + num2));
|
|
|
|
|
QVERIFY(verify(bignum1 + bignum2, scriptnum2 + num1));
|
|
|
|
|
}
|
|
|
|
|
// CheckSubtract
|
|
|
|
|
// int64_t overflow is undefined.
|
|
|
|
|
invalid = ((num2 > 0 && num1 < std::numeric_limits<int64_t>::min() + num2) ||
|
|
|
|
|
(num2 < 0 && num1 > std::numeric_limits<int64_t>::max() + num2));
|
|
|
|
|
if (!invalid) {
|
|
|
|
|
QVERIFY(verify(bignum1 - bignum2, scriptnum1 - scriptnum2));
|
|
|
|
|
QVERIFY(verify(bignum1 - bignum2, scriptnum1 - num2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
invalid = ((num1 > 0 && num2 < std::numeric_limits<int64_t>::min() + num1) ||
|
|
|
|
|
(num1 < 0 && num2 > std::numeric_limits<int64_t>::max() + num1));
|
|
|
|
|
if (!invalid) {
|
|
|
|
|
QVERIFY(verify(bignum2 - bignum1, scriptnum2 - scriptnum1));
|
|
|
|
|
QVERIFY(verify(bignum2 - bignum1, scriptnum2 - num1));
|
|
|
|
|
}
|
|
|
|
|
// CheckNegate
|
|
|
|
|
// -INT64_MIN is undefined
|
|
|
|
|
if (num1 != std::numeric_limits<int64_t>::min())
|
|
|
|
|
QVERIFY(verify(-bignum1, -scriptnum1));
|
|
|
|
|
|
|
|
|
|
// CheckCompare
|
|
|
|
|
QVERIFY((bignum1 == bignum1) == (scriptnum1 == scriptnum1));
|
|
|
|
|
QVERIFY((bignum1 != bignum1) == (scriptnum1 != scriptnum1));
|
|
|
|
|
QVERIFY((bignum1 < bignum1) == (scriptnum1 < scriptnum1));
|
|
|
|
|
QVERIFY((bignum1 > bignum1) == (scriptnum1 > scriptnum1));
|
|
|
|
|
QVERIFY((bignum1 >= bignum1) == (scriptnum1 >= scriptnum1));
|
|
|
|
|
QVERIFY((bignum1 <= bignum1) == (scriptnum1 <= scriptnum1));
|
|
|
|
|
|
|
|
|
|
QVERIFY((bignum1 == bignum1) == (scriptnum1 == num1));
|
|
|
|
|
QVERIFY((bignum1 != bignum1) == (scriptnum1 != num1));
|
|
|
|
|
QVERIFY((bignum1 < bignum1) == (scriptnum1 < num1));
|
|
|
|
|
QVERIFY((bignum1 > bignum1) == (scriptnum1 > num1));
|
|
|
|
|
QVERIFY((bignum1 >= bignum1) == (scriptnum1 >= num1));
|
|
|
|
|
QVERIFY((bignum1 <= bignum1) == (scriptnum1 <= num1));
|
|
|
|
|
|
|
|
|
|
QVERIFY((bignum1 == bignum2) == (scriptnum1 == scriptnum2));
|
|
|
|
|
QVERIFY((bignum1 != bignum2) == (scriptnum1 != scriptnum2));
|
|
|
|
|
QVERIFY((bignum1 < bignum2) == (scriptnum1 < scriptnum2));
|
|
|
|
|
QVERIFY((bignum1 > bignum2) == (scriptnum1 > scriptnum2));
|
|
|
|
|
QVERIFY((bignum1 >= bignum2) == (scriptnum1 >= scriptnum2));
|
|
|
|
|
QVERIFY((bignum1 <= bignum2) == (scriptnum1 <= scriptnum2));
|
|
|
|
|
|
|
|
|
|
QVERIFY((bignum1 == bignum2) == (scriptnum1 == num2));
|
|
|
|
|
QVERIFY((bignum1 != bignum2) == (scriptnum1 != num2));
|
|
|
|
|
QVERIFY((bignum1 < bignum2) == (scriptnum1 < num2));
|
|
|
|
|
QVERIFY((bignum1 > bignum2) == (scriptnum1 > num2));
|
|
|
|
|
QVERIFY((bignum1 >= bignum2) == (scriptnum1 >= num2));
|
|
|
|
|
QVERIFY((bignum1 <= bignum2) == (scriptnum1 <= num2));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestScriptNum::creation_data()
|
|
|
|
|
{
|
|
|
|
|
QTest::addColumn<qint64>("num");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for(size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
|
|
|
|
|
for(size_t j = 0; j < sizeof(offsets) / sizeof(offsets[0]); ++j) {
|
|
|
|
|
qint64 num = values[i];
|
|
|
|
|
QTest::newRow(QString::number(num).toLatin1().constData()) << num;
|
|
|
|
|
num = values[i] + offsets[j];
|
|
|
|
|
QTest::newRow(QString::number(num).toLatin1().constData()) << num;
|
|
|
|
|
num = values[i] - offsets[j];
|
|
|
|
|
QTest::newRow(QString::number(num).toLatin1().constData()) << num;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestScriptNum::creation()
|
|
|
|
|
{
|
|
|
|
|
QFETCH(qint64, num);
|
|
|
|
|
|
|
|
|
|
// CheckCreateInt(num);
|
|
|
|
|
CScriptNum10 bignum(num);
|
|
|
|
|
CScriptNum scriptnum(num);
|
|
|
|
|
QVERIFY(verify(bignum, scriptnum));
|
|
|
|
|
QVERIFY(verify(CScriptNum10(bignum.getint()), CScriptNum(scriptnum.getint())));
|
|
|
|
|
QVERIFY(verify(CScriptNum10(scriptnum.getint()), CScriptNum(bignum.getint())));
|
|
|
|
|
QVERIFY(verify(CScriptNum10(CScriptNum10(scriptnum.getint()).getint()), CScriptNum(CScriptNum(bignum.getint()).getint())));
|
|
|
|
|
|
|
|
|
|
const bool expectException = (scriptnum.getvch().size() > CScriptNum::nDefaultMaxNumSize);
|
|
|
|
|
try {
|
|
|
|
|
CScriptNum10 bignum(num);
|
|
|
|
|
CScriptNum scriptnum(num);
|
|
|
|
|
QVERIFY(verify(bignum, scriptnum));
|
|
|
|
|
|
|
|
|
|
std::vector<unsigned char> vch = bignum.getvch();
|
|
|
|
|
|
|
|
|
|
CScriptNum10 bignum2(bignum.getvch(), false);
|
|
|
|
|
vch = scriptnum.getvch();
|
|
|
|
|
CScriptNum scriptnum2(scriptnum.getvch(), false);
|
|
|
|
|
QVERIFY(verify(bignum2, scriptnum2));
|
|
|
|
|
|
|
|
|
|
CScriptNum10 bignum3(scriptnum2.getvch(), false);
|
|
|
|
|
CScriptNum scriptnum3(bignum2.getvch(), false);
|
|
|
|
|
QVERIFY(verify(bignum3, scriptnum3));
|
|
|
|
|
|
|
|
|
|
if (expectException)
|
|
|
|
|
QFAIL("Expected exception");
|
|
|
|
|
} catch (scriptnum10_error &e) {
|
|
|
|
|
if (!expectException)
|
|
|
|
|
QFAIL("unexpected exception scriptnum10_error");
|
|
|
|
|
} catch (...) {
|
|
|
|
|
QFAIL("unexpected exception");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestScriptNum::operators()
|
|
|
|
|
{
|
|
|
|
|
for(size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
|
|
|
|
|
for(size_t j = 0; j < sizeof(offsets) / sizeof(offsets[0]); ++j) {
|
|
|
|
|
RunOperators(values[i], values[i]);
|
|
|
|
|
RunOperators(values[i], -values[i]);
|
|
|
|
|
RunOperators(values[i], values[j]);
|
|
|
|
|
RunOperators(values[i], -values[j]);
|
|
|
|
|
RunOperators(values[i] + values[j], values[j]);
|
|
|
|
|
RunOperators(values[i] + values[j], -values[j]);
|
|
|
|
|
RunOperators(values[i] - values[j], values[j]);
|
|
|
|
|
RunOperators(values[i] - values[j], -values[j]);
|
|
|
|
|
RunOperators(values[i] + values[j], values[i] + values[j]);
|
|
|
|
|
RunOperators(values[i] + values[j], values[i] - values[j]);
|
|
|
|
|
RunOperators(values[i] - values[j], values[i] + values[j]);
|
|
|
|
|
RunOperators(values[i] - values[j], values[i] - values[j]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-07 17:51:05 +02:00
|
|
|
|
2026-05-07 22:10:35 +02:00
|
|
|
void TestScriptNum::op_mul_basic()
|
2026-05-07 17:51:05 +02:00
|
|
|
{
|
|
|
|
|
const CScript script = CScript() << OP_MUL;
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
|
|
|
|
|
stacktype legacyStack{CScriptNum(6).getvch(), CScriptNum(7).getvch()};
|
|
|
|
|
QVERIFY(!EvalScript(0, legacyStack, script, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_DISABLED_OPCODE);
|
|
|
|
|
|
|
|
|
|
stacktype may2025Stack{CScriptNum(6).getvch(), CScriptNum(7).getvch()};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, may2025Stack, script, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(may2025Stack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(may2025Stack.back(), CScriptNum(42).getvch());
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 22:10:35 +02:00
|
|
|
void TestScriptNum::bigint_arithmetic()
|
2026-05-07 17:51:05 +02:00
|
|
|
{
|
|
|
|
|
const ScriptBigNum::BigInt twoToEighty = ScriptBigNum::BigInt(1) << 80;
|
|
|
|
|
const ScriptBigNum big(twoToEighty);
|
|
|
|
|
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
stacktype addStack{big.getvch()};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, addStack, CScript() << OP_1ADD, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(addStack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(addStack.back(), ScriptBigNum(twoToEighty + 1).getvch());
|
|
|
|
|
|
|
|
|
|
stacktype mulStack{big.getvch(), CScriptNum(3).getvch()};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, mulStack, CScript() << OP_MUL, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(mulStack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(mulStack.back(), ScriptBigNum(twoToEighty * 3).getvch());
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 22:10:35 +02:00
|
|
|
void TestScriptNum::bigint_opcode_coverage()
|
2026-05-07 17:51:05 +02:00
|
|
|
{
|
|
|
|
|
using BigInt = ScriptBigNum::BigInt;
|
|
|
|
|
const BigInt q = (BigInt(1) << 80) + 17;
|
|
|
|
|
const BigInt a = q * 3;
|
|
|
|
|
const BigInt b = 3;
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
|
|
|
|
|
struct UnaryCase {
|
|
|
|
|
opcodetype opcode;
|
|
|
|
|
BigInt input;
|
|
|
|
|
BigInt expected;
|
|
|
|
|
};
|
|
|
|
|
const UnaryCase unaryCases[] = {
|
|
|
|
|
{OP_1ADD, q, q + 1},
|
|
|
|
|
{OP_1SUB, q, q - 1},
|
|
|
|
|
{OP_NEGATE, q, -q},
|
|
|
|
|
{OP_ABS, -q, q},
|
|
|
|
|
{OP_NOT, q, 0},
|
|
|
|
|
{OP_NOT, 0, 1},
|
|
|
|
|
{OP_0NOTEQUAL, q, 1},
|
|
|
|
|
{OP_0NOTEQUAL, 0, 0},
|
|
|
|
|
};
|
|
|
|
|
for (const auto &test : unaryCases) {
|
|
|
|
|
stacktype stack{ToScriptNumVch(test.input)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, stack, CScript() << test.opcode, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(stack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(stack.back(), ToScriptNumVch(test.expected));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct BinaryCase {
|
|
|
|
|
opcodetype opcode;
|
|
|
|
|
BigInt lhs;
|
|
|
|
|
BigInt rhs;
|
|
|
|
|
valtype expected;
|
|
|
|
|
};
|
|
|
|
|
const BinaryCase binaryCases[] = {
|
|
|
|
|
{OP_ADD, a, b, ToScriptNumVch(a + b)},
|
|
|
|
|
{OP_SUB, a, b, ToScriptNumVch(a - b)},
|
|
|
|
|
{OP_MUL, q, b, ToScriptNumVch(q * b)},
|
|
|
|
|
{OP_DIV, a, b, ToScriptNumVch(q)},
|
|
|
|
|
{OP_MOD, a + 2, b, ToScriptNumVch(2)},
|
|
|
|
|
{OP_BOOLAND, a, 0, ToBoolVch(false)},
|
|
|
|
|
{OP_BOOLAND, a, b, ToBoolVch(true)},
|
|
|
|
|
{OP_BOOLOR, 0, b, ToBoolVch(true)},
|
|
|
|
|
{OP_NUMEQUAL, a, a, ToBoolVch(true)},
|
|
|
|
|
{OP_NUMEQUAL, a, b, ToBoolVch(false)},
|
|
|
|
|
{OP_NUMNOTEQUAL, a, b, ToBoolVch(true)},
|
|
|
|
|
{OP_LESSTHAN, b, a, ToBoolVch(true)},
|
|
|
|
|
{OP_GREATERTHAN, a, b, ToBoolVch(true)},
|
|
|
|
|
{OP_LESSTHANOREQUAL, a, a, ToBoolVch(true)},
|
|
|
|
|
{OP_GREATERTHANOREQUAL, a, a, ToBoolVch(true)},
|
|
|
|
|
{OP_MIN, a, b, ToScriptNumVch(b)},
|
|
|
|
|
{OP_MAX, a, b, ToScriptNumVch(a)},
|
|
|
|
|
};
|
|
|
|
|
for (const auto &test : binaryCases) {
|
|
|
|
|
stacktype stack{ToScriptNumVch(test.lhs), ToScriptNumVch(test.rhs)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, stack, CScript() << test.opcode, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(stack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(stack.back(), test.expected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stacktype equalVerifySuccess{ToScriptNumVch(a), ToScriptNumVch(a)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, equalVerifySuccess, CScript() << OP_NUMEQUALVERIFY, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(equalVerifySuccess.size(), size_t(0));
|
|
|
|
|
|
|
|
|
|
stacktype equalVerifyFailure{ToScriptNumVch(a), ToScriptNumVch(b)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, equalVerifyFailure, CScript() << OP_NUMEQUALVERIFY, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_NUMEQUALVERIFY);
|
|
|
|
|
|
|
|
|
|
stacktype divByZero{ToScriptNumVch(a), ToScriptNumVch(0)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, divByZero, CScript() << OP_DIV, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_DIV_BY_ZERO);
|
|
|
|
|
|
|
|
|
|
stacktype modByZero{ToScriptNumVch(a), ToScriptNumVch(0)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, modByZero, CScript() << OP_MOD, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_MOD_BY_ZERO);
|
|
|
|
|
|
|
|
|
|
stacktype withinTrue{ToScriptNumVch(q), ToScriptNumVch(q - 1), ToScriptNumVch(q + 1)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, withinTrue, CScript() << OP_WITHIN, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(withinTrue.size(), size_t(1));
|
|
|
|
|
QCOMPARE(withinTrue.back(), ToBoolVch(true));
|
|
|
|
|
|
|
|
|
|
stacktype withinFalse{ToScriptNumVch(q), ToScriptNumVch(q + 1), ToScriptNumVch(q + 2)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, withinFalse, CScript() << OP_WITHIN, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(withinFalse.size(), size_t(1));
|
|
|
|
|
QCOMPARE(withinFalse.back(), ToBoolVch(false));
|
|
|
|
|
|
|
|
|
|
const valtype qMinimal = ToScriptNumVch(q);
|
|
|
|
|
const size_t paddedSize = qMinimal.size() + 2;
|
|
|
|
|
stacktype padded{qMinimal, CScriptNum(paddedSize).getvch()};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, padded, CScript() << OP_NUM2BIN, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(padded.size(), size_t(1));
|
|
|
|
|
QCOMPARE(padded.back().size(), paddedSize);
|
|
|
|
|
|
|
|
|
|
stacktype normalized{padded.back()};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, normalized, CScript() << OP_BIN2NUM, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(normalized.size(), size_t(1));
|
|
|
|
|
QCOMPARE(normalized.back(), qMinimal);
|
|
|
|
|
|
|
|
|
|
const size_t maxBigIntBits = ScriptBigNum::MAXIMUM_ELEMENT_SIZE_BIG_INT * 8 - 1;
|
|
|
|
|
const BigInt maxBigInt = (BigInt(1) << maxBigIntBits) - 1;
|
|
|
|
|
stacktype overflow{ToScriptNumVch(maxBigInt)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, overflow, CScript() << OP_1ADD, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_NUMBER_RANGE_BIG_INT);
|
|
|
|
|
|
|
|
|
|
stacktype oversized{valtype(ScriptBigNum::MAXIMUM_ELEMENT_SIZE_BIG_INT + 1, 1)};
|
2026-05-17 13:09:18 +02:00
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_64_BIT_INTEGERS, oversized, CScript() << OP_BIN2NUM, error));
|
2026-05-07 17:51:05 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_NUMBER_RANGE_BIG_INT);
|
|
|
|
|
}
|
2026-05-14 15:01:57 +02:00
|
|
|
|
|
|
|
|
void TestScriptNum::may2025_vm_limit_accounting()
|
|
|
|
|
{
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
|
|
|
|
|
CScript opCostScript;
|
|
|
|
|
for (int i = 0; i < 329; ++i)
|
|
|
|
|
opCostScript << OP_NOP;
|
|
|
|
|
stacktype stack;
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_MAY2025, stack, opCostScript, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OP_COST);
|
|
|
|
|
|
|
|
|
|
CScript hashScript;
|
|
|
|
|
for (int i = 0; i < 21; ++i)
|
|
|
|
|
hashScript << OP_SHA256;
|
|
|
|
|
stacktype hashStack{valtype(32, 0xaa)};
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_MAY2025 | SCRIPT_VM_LIMITS_STANDARD, hashStack, hashScript, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_TOO_MANY_HASH_ITERS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestScriptNum::may2026_bitwise_shift_opcodes()
|
|
|
|
|
{
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
|
|
|
|
|
stacktype disabled{valtype{0x0f, 0xf0}};
|
|
|
|
|
QVERIFY(!EvalScript(0, disabled, CScript() << OP_INVERT, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_DISABLED_OPCODE);
|
|
|
|
|
|
|
|
|
|
stacktype inverted{valtype{0x0f, 0xf0}};
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, inverted, CScript() << OP_INVERT, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(inverted.back(), valtype({0xf0, 0x0f}));
|
|
|
|
|
|
|
|
|
|
stacktype leftNum{CScriptNum(12345).getvch(), CScriptNum(3).getvch()};
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, leftNum, CScript() << OP_LSHIFTNUM, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(leftNum.back(), CScriptNum(12345 * 8).getvch());
|
|
|
|
|
|
|
|
|
|
stacktype rightNum{CScriptNum(12345).getvch(), CScriptNum(3).getvch()};
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, rightNum, CScript() << OP_RSHIFTNUM, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(rightNum.back(), CScriptNum(12345 / 8).getvch());
|
|
|
|
|
|
|
|
|
|
stacktype leftBlob{valtype{0xad, 0x00, 0xff}, CScriptNum(2).getvch()};
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, leftBlob, CScript() << OP_LSHIFTBIN, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(leftBlob.back(), valtype({0xb4, 0x03, 0xfc}));
|
|
|
|
|
|
|
|
|
|
stacktype rightBlob{valtype{0xad, 0x00, 0xff}, CScriptNum(2).getvch()};
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, rightBlob, CScript() << OP_RSHIFTBIN, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(rightBlob.back(), valtype({0x2b, 0x40, 0x3f}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TestScriptNum::may2026_control_flow_opcodes()
|
|
|
|
|
{
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
|
|
|
|
|
stacktype disabled;
|
|
|
|
|
QVERIFY(!EvalScript(0, disabled, CScript() << OP_BEGIN, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_BAD_OPCODE);
|
|
|
|
|
|
|
|
|
|
CScript loopScript;
|
|
|
|
|
loopScript << OP_0 << OP_BEGIN << OP_1ADD << OP_DUP << OP_3 << OP_NUMEQUAL << OP_UNTIL;
|
|
|
|
|
stacktype loopStack;
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, loopStack, loopScript, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(loopStack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(loopStack.back(), CScriptNum(3).getvch());
|
|
|
|
|
|
|
|
|
|
const valtype functionCode{OP_2, OP_3, OP_ADD};
|
|
|
|
|
const valtype functionId{'f'};
|
|
|
|
|
CScript functionScript;
|
|
|
|
|
functionScript << functionCode << functionId << OP_DEFINE << functionId << OP_INVOKE;
|
|
|
|
|
stacktype functionStack;
|
|
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_MAY2026, functionStack, functionScript, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(functionStack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(functionStack.back(), CScriptNum(5).getvch());
|
|
|
|
|
}
|