2026-05-07 18:07:15 +02:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
|
|
|
|
* Copyright (C) 2026 The Flowee developers
|
|
|
|
|
*
|
|
|
|
|
* 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 "native_introspection_tests.h"
|
|
|
|
|
|
|
|
|
|
#include <primitives/transaction.h>
|
2026-05-09 21:13:23 +02:00
|
|
|
#include <primitives/ScriptDefines.h>
|
2026-05-07 18:07:15 +02:00
|
|
|
#include <script/interpreter.h>
|
|
|
|
|
#include <utilstrencodings.h>
|
|
|
|
|
|
2026-05-12 16:07:59 +02:00
|
|
|
static bool EvalScript(uint32_t flags, std::vector<std::vector<uint8_t>> &stack, const CScript &script,
|
2026-05-07 18:07:15 +02:00
|
|
|
const BaseSignatureChecker &checker, ScriptError &error)
|
|
|
|
|
{
|
|
|
|
|
Script::State state(flags);
|
|
|
|
|
const bool result = Script::eval(stack, script, checker, state);
|
|
|
|
|
error = state.error;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 16:07:59 +02:00
|
|
|
static std::vector<uint8_t> ToScriptNumVch(int64_t value)
|
2026-05-07 18:07:15 +02:00
|
|
|
{
|
|
|
|
|
return CScriptNum(value).getvch();
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 16:07:59 +02:00
|
|
|
static std::vector<uint8_t> ToBytes(const CScript &script)
|
2026-05-07 18:07:15 +02:00
|
|
|
{
|
2026-05-12 16:07:59 +02:00
|
|
|
return std::vector<uint8_t>(script.begin(), script.end());
|
2026-05-07 18:07:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-12 16:07:59 +02:00
|
|
|
static std::vector<uint8_t> ToBytes(const uint256 &value)
|
2026-05-07 18:07:15 +02:00
|
|
|
{
|
2026-05-12 16:07:59 +02:00
|
|
|
return std::vector<uint8_t>(value.begin(), value.end());
|
2026-05-07 18:07:15 +02:00
|
|
|
}
|
|
|
|
|
|
2026-05-14 08:52:55 +02:00
|
|
|
static bool EvalOne(const CTransaction &tx, int inputIndex, const std::vector<Tx::Output> &spentOutputs,
|
|
|
|
|
const CScript &script, std::vector<std::vector<uint8_t>> &stack, ScriptError &error)
|
|
|
|
|
{
|
2026-05-14 12:45:20 +02:00
|
|
|
auto newTx = Tx::fromOldTransaction(tx);
|
|
|
|
|
TransactionSignatureChecker checker(&tx, newTx, inputIndex, spentOutputs.at(inputIndex).outputValue, &spentOutputs);
|
2026-05-14 08:52:55 +02:00
|
|
|
return EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION | SCRIPT_ENABLE_CASHTOKENS, stack, script, checker, error);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-14 08:15:38 +02:00
|
|
|
static uint256 TokenCategoryA()
|
|
|
|
|
{
|
|
|
|
|
return uint256S("0x0000000000000000000000000000000000000000000000000000000000001234");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static uint256 TokenCategoryB()
|
|
|
|
|
{
|
|
|
|
|
return uint256S("0x0000000000000000000000000000000000000000000000000000000000005678");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static std::vector<uint8_t> TokenCategoryBytes(const Tx::Token &token)
|
|
|
|
|
{
|
|
|
|
|
std::vector<uint8_t> bytes(token.category.begin(), token.category.end());
|
|
|
|
|
if (token.isMutableNft() || token.isMintingNft())
|
|
|
|
|
bytes.push_back(token.bitfield & 0x0f);
|
|
|
|
|
return bytes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static CScript TokenScript(const uint256 &category, uint8_t bitfield, const std::vector<uint8_t> &commitment,
|
|
|
|
|
uint64_t amount, const CScript &bytecode)
|
|
|
|
|
{
|
|
|
|
|
std::vector<uint8_t> bytes;
|
|
|
|
|
bytes.reserve(1 + 32 + 1 + 1 + commitment.size() + 1 + bytecode.size());
|
|
|
|
|
bytes.push_back(0xef);
|
|
|
|
|
bytes.insert(bytes.end(), category.begin(), category.end());
|
|
|
|
|
bytes.push_back(bitfield);
|
|
|
|
|
if (bitfield & Tx::HasCommitment) {
|
|
|
|
|
bytes.push_back(static_cast<uint8_t>(commitment.size()));
|
|
|
|
|
bytes.insert(bytes.end(), commitment.begin(), commitment.end());
|
|
|
|
|
}
|
|
|
|
|
if (bitfield & Tx::HasFtAmount)
|
|
|
|
|
bytes.push_back(static_cast<uint8_t>(amount));
|
|
|
|
|
bytes.insert(bytes.end(), bytecode.begin(), bytecode.end());
|
|
|
|
|
return CScript(bytes.begin(), bytes.end());
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-07 18:07:15 +02:00
|
|
|
static CMutableTransaction BuildTransaction()
|
|
|
|
|
{
|
|
|
|
|
CMutableTransaction tx;
|
|
|
|
|
tx.nVersion = 2;
|
|
|
|
|
tx.nLockTime = 500;
|
|
|
|
|
tx.vin.emplace_back(COutPoint(uint256S("0x010203040506070809101112131415161718191a1b1c1d1e1f20212223242526"), 7),
|
|
|
|
|
CScript() << OP_2, 123);
|
|
|
|
|
tx.vin.emplace_back(COutPoint(uint256S("0x515253545556575859606162636465666768696a6b6c6d6e6f70717273747576"), 9),
|
|
|
|
|
CScript() << OP_3 << OP_4, CTxIn::SEQUENCE_FINAL - 1);
|
|
|
|
|
tx.vout.emplace_back(1111, CScript() << OP_5);
|
2026-05-14 08:15:38 +02:00
|
|
|
tx.vout.emplace_back(2222, TokenScript(TokenCategoryB(),
|
|
|
|
|
Tx::HasNft | Tx::HasCommitment | Tx::NftMutableBaton | Tx::HasFtAmount,
|
|
|
|
|
{0x12, 0x34}, 42, CScript() << OP_6 << OP_7));
|
2026-05-07 18:07:15 +02:00
|
|
|
return tx;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 15:46:48 +02:00
|
|
|
static std::vector<Tx::Output> BuildSpentOutputs()
|
2026-05-07 18:07:15 +02:00
|
|
|
{
|
|
|
|
|
return {
|
2026-05-14 08:15:38 +02:00
|
|
|
Tx::Output(Streaming::bufferFrom(TokenScript(TokenCategoryA(), Tx::HasFtAmount, {}, 77,
|
|
|
|
|
CScript() << OP_8)), 3333),
|
2026-05-13 16:43:23 +02:00
|
|
|
Tx::Output(Streaming::bufferFrom(CScript() << OP_9 << OP_10), 4444)
|
2026-05-07 18:07:15 +02:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 16:23:25 +02:00
|
|
|
void NativeIntrospectionTests::testOpcodes_data()
|
2026-05-07 18:07:15 +02:00
|
|
|
{
|
2026-05-12 16:23:25 +02:00
|
|
|
QTest::addColumn<CScript>("script");
|
|
|
|
|
QTest::addColumn<std::vector<uint8_t>>("expected");
|
|
|
|
|
|
|
|
|
|
const CMutableTransaction mutableTx = BuildTransaction();
|
|
|
|
|
const CTransaction tx(mutableTx);
|
|
|
|
|
const std::vector<Tx::Output> spentOutputs = BuildSpentOutputs();
|
|
|
|
|
|
|
|
|
|
// === Nullary opcodes ===
|
|
|
|
|
QTest::newRow("OP_INPUTINDEX")
|
|
|
|
|
<< (CScript() << OP_INPUTINDEX)
|
|
|
|
|
<< ToScriptNumVch(1);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_TXVERSION")
|
|
|
|
|
<< (CScript() << OP_TXVERSION)
|
|
|
|
|
<< ToScriptNumVch(2);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_TXINPUTCOUNT")
|
|
|
|
|
<< (CScript() << OP_TXINPUTCOUNT)
|
|
|
|
|
<< ToScriptNumVch(2);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_TXOUTPUTCOUNT")
|
|
|
|
|
<< (CScript() << OP_TXOUTPUTCOUNT)
|
|
|
|
|
<< ToScriptNumVch(2);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_TXLOCKTIME")
|
|
|
|
|
<< (CScript() << OP_TXLOCKTIME)
|
|
|
|
|
<< ToScriptNumVch(500);
|
|
|
|
|
|
|
|
|
|
const CScript activeBytecodeScript = CScript() << OP_CODESEPARATOR << OP_ACTIVEBYTECODE;
|
|
|
|
|
QTest::newRow("OP_ACTIVEBYTECODE")
|
|
|
|
|
<< activeBytecodeScript
|
|
|
|
|
<< std::vector<uint8_t>{static_cast<unsigned char>(OP_ACTIVEBYTECODE)};
|
|
|
|
|
|
|
|
|
|
// === Unary input opcodes ===
|
|
|
|
|
QTest::newRow("OP_UTXOVALUE input 0")
|
|
|
|
|
<< (CScript() << OP_0 << OP_UTXOVALUE)
|
|
|
|
|
<< ToScriptNumVch(3333);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_UTXOVALUE input 1")
|
|
|
|
|
<< (CScript() << OP_1 << OP_UTXOVALUE)
|
|
|
|
|
<< ToScriptNumVch(4444);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_UTXOBYTECODE input 1")
|
|
|
|
|
<< (CScript() << OP_1 << OP_UTXOBYTECODE)
|
2026-05-13 16:43:23 +02:00
|
|
|
<< ToBytes(spentOutputs.at(1).outputScript());
|
2026-05-12 16:23:25 +02:00
|
|
|
|
2026-05-14 08:15:38 +02:00
|
|
|
QTest::newRow("OP_UTXOTOKENCATEGORY FT input")
|
|
|
|
|
<< (CScript() << OP_0 << OP_UTXOTOKENCATEGORY)
|
|
|
|
|
<< TokenCategoryBytes(spentOutputs.at(0).token());
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_UTXOTOKENCOMMITMENT FT-only input")
|
|
|
|
|
<< (CScript() << OP_0 << OP_UTXOTOKENCOMMITMENT)
|
|
|
|
|
<< std::vector<uint8_t>{};
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_UTXOTOKENAMOUNT input")
|
|
|
|
|
<< (CScript() << OP_0 << OP_UTXOTOKENAMOUNT)
|
|
|
|
|
<< ToScriptNumVch(77);
|
|
|
|
|
|
2026-05-12 16:23:25 +02:00
|
|
|
QTest::newRow("OP_OUTPOINTTXHASH input 0")
|
|
|
|
|
<< (CScript() << OP_0 << OP_OUTPOINTTXHASH)
|
|
|
|
|
<< ToBytes(tx.vin.at(0).prevout.hash);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPOINTINDEX input 1")
|
|
|
|
|
<< (CScript() << OP_1 << OP_OUTPOINTINDEX)
|
|
|
|
|
<< ToScriptNumVch(9);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_INPUTBYTECODE input 1")
|
|
|
|
|
<< (CScript() << OP_1 << OP_INPUTBYTECODE)
|
|
|
|
|
<< ToBytes(mutableTx.vin.at(1).scriptSig);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_INPUTSEQUENCENUMBER input 0")
|
|
|
|
|
<< (CScript() << OP_0 << OP_INPUTSEQUENCENUMBER)
|
|
|
|
|
<< ToScriptNumVch(123);
|
|
|
|
|
|
|
|
|
|
// === Unary output opcodes ===
|
|
|
|
|
QTest::newRow("OP_OUTPUTVALUE output 1")
|
|
|
|
|
<< (CScript() << OP_1 << OP_OUTPUTVALUE)
|
|
|
|
|
<< ToScriptNumVch(2222);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPUTBYTECODE output 0")
|
|
|
|
|
<< (CScript() << OP_0 << OP_OUTPUTBYTECODE)
|
|
|
|
|
<< ToBytes(mutableTx.vout.at(0).scriptPubKey);
|
2026-05-14 08:15:38 +02:00
|
|
|
|
|
|
|
|
Tx outputTx = Tx::fromOldTransaction(tx);
|
|
|
|
|
const auto tokenOutput = outputTx.output(1);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPUTBYTECODE token output")
|
|
|
|
|
<< (CScript() << OP_1 << OP_OUTPUTBYTECODE)
|
|
|
|
|
<< ToBytes(tokenOutput.outputScript());
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPUTTOKENCATEGORY output")
|
|
|
|
|
<< (CScript() << OP_1 << OP_OUTPUTTOKENCATEGORY)
|
|
|
|
|
<< TokenCategoryBytes(tokenOutput.token());
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPUTTOKENCOMMITMENT output")
|
|
|
|
|
<< (CScript() << OP_1 << OP_OUTPUTTOKENCOMMITMENT)
|
|
|
|
|
<< std::vector<uint8_t>{0x12, 0x34};
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPUTTOKENAMOUNT output")
|
|
|
|
|
<< (CScript() << OP_1 << OP_OUTPUTTOKENAMOUNT)
|
|
|
|
|
<< ToScriptNumVch(42);
|
|
|
|
|
|
|
|
|
|
QTest::newRow("OP_OUTPUTTOKENAMOUNT no-token output")
|
|
|
|
|
<< (CScript() << OP_0 << OP_OUTPUTTOKENAMOUNT)
|
|
|
|
|
<< std::vector<uint8_t>{};
|
2026-05-12 16:23:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void NativeIntrospectionTests::testOpcodes()
|
|
|
|
|
{
|
|
|
|
|
QFETCH(CScript, script);
|
|
|
|
|
QFETCH(std::vector<uint8_t>, expected);
|
|
|
|
|
|
|
|
|
|
const CTransaction tx(BuildTransaction());
|
2026-05-14 12:45:20 +02:00
|
|
|
auto newTx = Tx::fromOldTransaction(tx);
|
2026-05-12 16:23:25 +02:00
|
|
|
const std::vector<Tx::Output> spentOutputs = BuildSpentOutputs();
|
|
|
|
|
|
2026-05-14 12:45:20 +02:00
|
|
|
TransactionSignatureChecker checker(&tx, newTx, 1, spentOutputs.at(1).outputValue, &spentOutputs);
|
2026-05-07 18:07:15 +02:00
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
2026-05-12 16:07:59 +02:00
|
|
|
std::vector<std::vector<uint8_t>> stack;
|
2026-05-07 18:07:15 +02:00
|
|
|
|
2026-05-14 08:15:38 +02:00
|
|
|
QVERIFY(EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION | SCRIPT_ENABLE_CASHTOKENS, stack, script, checker, error));
|
2026-05-07 18:07:15 +02:00
|
|
|
QCOMPARE(error, SCRIPT_ERR_OK);
|
|
|
|
|
QCOMPARE(stack.size(), size_t(1));
|
|
|
|
|
QCOMPARE(stack.back(), expected);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-14 08:52:55 +02:00
|
|
|
void NativeIntrospectionTests::cashtoken_opcode_variants()
|
|
|
|
|
{
|
|
|
|
|
CMutableTransaction mutableTx = BuildTransaction();
|
|
|
|
|
mutableTx.vout.clear();
|
|
|
|
|
mutableTx.vout.emplace_back(1000, CScript() << OP_TRUE);
|
|
|
|
|
mutableTx.vout.emplace_back(1000, TokenScript(TokenCategoryA(), Tx::HasNft | Tx::HasCommitment,
|
|
|
|
|
{0x01, 0x02, 0x03}, 0, CScript() << OP_1));
|
|
|
|
|
mutableTx.vout.emplace_back(1000, TokenScript(TokenCategoryA(), Tx::HasNft | Tx::NftMutableBaton,
|
|
|
|
|
{}, 0, CScript() << OP_2));
|
|
|
|
|
mutableTx.vout.emplace_back(1000, TokenScript(TokenCategoryA(), Tx::HasNft | Tx::NftMintBaton,
|
|
|
|
|
{}, 0, CScript() << OP_3));
|
|
|
|
|
mutableTx.vout.emplace_back(1000, TokenScript(TokenCategoryB(), Tx::HasFtAmount,
|
|
|
|
|
{}, 99, CScript() << OP_4));
|
|
|
|
|
const CTransaction tx(mutableTx);
|
|
|
|
|
|
|
|
|
|
const std::vector<Tx::Output> spentOutputs = {
|
|
|
|
|
Tx::Output(Streaming::bufferFrom(TokenScript(TokenCategoryA(), Tx::HasNft | Tx::HasCommitment,
|
|
|
|
|
{0xaa}, 0, CScript() << OP_5)), 1111),
|
|
|
|
|
Tx::Output(Streaming::bufferFrom(TokenScript(TokenCategoryA(), Tx::HasNft | Tx::NftMutableBaton,
|
|
|
|
|
{}, 0, CScript() << OP_6)), 2222)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
|
|
|
|
std::vector<std::vector<uint8_t>> stack;
|
|
|
|
|
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_0 << OP_UTXOTOKENCATEGORY, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), TokenCategoryBytes(spentOutputs.at(0).token()));
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_0 << OP_UTXOTOKENCOMMITMENT, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), std::vector<uint8_t>{0xaa});
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_1 << OP_UTXOTOKENCATEGORY, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), TokenCategoryBytes(spentOutputs.at(1).token()));
|
|
|
|
|
|
|
|
|
|
Tx outputTx = Tx::fromOldTransaction(tx);
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_0 << OP_OUTPUTTOKENCATEGORY, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), std::vector<uint8_t>{});
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_1 << OP_OUTPUTTOKENCATEGORY, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), TokenCategoryBytes(outputTx.output(1).token()));
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_1 << OP_OUTPUTTOKENCOMMITMENT, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), std::vector<uint8_t>({0x01, 0x02, 0x03}));
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_2 << OP_OUTPUTTOKENCATEGORY, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), TokenCategoryBytes(outputTx.output(2).token()));
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_3 << OP_OUTPUTTOKENCATEGORY, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), TokenCategoryBytes(outputTx.output(3).token()));
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_4 << OP_OUTPUTTOKENAMOUNT, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), ToScriptNumVch(99));
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(EvalOne(tx, 0, spentOutputs, CScript() << OP_4 << OP_OUTPUTTOKENCOMMITMENT, stack, error));
|
|
|
|
|
QCOMPARE(stack.back(), std::vector<uint8_t>{});
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 16:07:59 +02:00
|
|
|
void NativeIntrospectionTests::error_conditions()
|
2026-05-07 18:07:15 +02:00
|
|
|
{
|
2026-05-12 16:23:25 +02:00
|
|
|
const CTransaction tx(BuildTransaction());
|
2026-05-14 12:45:20 +02:00
|
|
|
auto newTx = Tx::fromOldTransaction(tx);
|
2026-05-12 15:46:48 +02:00
|
|
|
const std::vector<Tx::Output> spentOutputs = BuildSpentOutputs();
|
2026-05-07 18:07:15 +02:00
|
|
|
|
2026-05-14 12:45:20 +02:00
|
|
|
TransactionSignatureChecker checker(&tx, newTx, 1, spentOutputs.at(1).outputValue, &spentOutputs);
|
2026-05-07 18:07:15 +02:00
|
|
|
ScriptError error = SCRIPT_ERR_OK;
|
2026-05-12 16:07:59 +02:00
|
|
|
std::vector<std::vector<uint8_t>> stack;
|
2026-05-07 18:07:15 +02:00
|
|
|
|
|
|
|
|
QVERIFY(!EvalScript(0, stack, CScript() << OP_TXVERSION, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_BAD_OPCODE);
|
|
|
|
|
|
|
|
|
|
BaseSignatureChecker noContextChecker;
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_TXVERSION, noContextChecker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_CONTEXT_NOT_PRESENT);
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_UTXOVALUE, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_STACK_OPERATION);
|
|
|
|
|
|
2026-05-14 08:15:38 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_0 << OP_OUTPUTTOKENAMOUNT,
|
|
|
|
|
checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_BAD_OPCODE);
|
|
|
|
|
|
2026-05-14 08:52:55 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION | SCRIPT_ENABLE_CASHTOKENS, stack,
|
|
|
|
|
CScript() << OP_1NEGATE << OP_UTXOTOKENCATEGORY, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_INPUT_INDEX);
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION | SCRIPT_ENABLE_CASHTOKENS, stack,
|
|
|
|
|
CScript() << 2 << OP_UTXOTOKENCOMMITMENT, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_INPUT_INDEX);
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION | SCRIPT_ENABLE_CASHTOKENS, stack,
|
|
|
|
|
CScript() << 2 << OP_OUTPUTTOKENCATEGORY, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_OUTPUT_INDEX);
|
|
|
|
|
|
2026-05-07 18:07:15 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_1NEGATE << OP_UTXOVALUE, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_INPUT_INDEX);
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << 2 << OP_UTXOVALUE, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_INPUT_INDEX);
|
|
|
|
|
|
|
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << 2 << OP_OUTPUTVALUE, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_OUTPUT_INDEX);
|
|
|
|
|
|
2026-05-12 16:07:59 +02:00
|
|
|
const std::vector<uint8_t> largeIndex = CScriptNum(int64_t(1) << 32).getvch();
|
2026-05-07 18:07:15 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << largeIndex << OP_OUTPUTVALUE, checker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_INVALID_TX_OUTPUT_INDEX);
|
|
|
|
|
|
2026-05-14 12:45:20 +02:00
|
|
|
TransactionSignatureChecker limitedChecker(&tx, newTx, 0, spentOutputs.at(0).outputValue);
|
2026-05-07 18:07:15 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_1 << OP_UTXOVALUE, limitedChecker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_LIMITED_CONTEXT_NO_SIBLING_INFO);
|
|
|
|
|
|
2026-05-14 08:15:38 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION | SCRIPT_ENABLE_CASHTOKENS, stack,
|
|
|
|
|
CScript() << OP_1 << OP_UTXOTOKENAMOUNT, limitedChecker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_LIMITED_CONTEXT_NO_SIBLING_INFO);
|
|
|
|
|
|
2026-05-07 18:07:15 +02:00
|
|
|
CMutableTransaction oversizedInputTx = BuildTransaction();
|
|
|
|
|
for (unsigned int i = 0; i < MAX_SCRIPT_ELEMENT_SIZE + 1; ++i)
|
|
|
|
|
oversizedInputTx.vin.at(1).scriptSig << OP_0;
|
|
|
|
|
const CTransaction oversizedInput(oversizedInputTx);
|
2026-05-14 12:45:20 +02:00
|
|
|
newTx = Tx::fromOldTransaction(oversizedInput);
|
|
|
|
|
TransactionSignatureChecker oversizedInputChecker(&oversizedInput, newTx, 1, spentOutputs.at(1).outputValue, &spentOutputs);
|
2026-05-07 18:07:15 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_1 << OP_INPUTBYTECODE,
|
|
|
|
|
oversizedInputChecker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_PUSH_SIZE);
|
|
|
|
|
|
|
|
|
|
CMutableTransaction oversizedOutputTx = BuildTransaction();
|
|
|
|
|
for (unsigned int i = 0; i < MAX_SCRIPT_ELEMENT_SIZE + 1; ++i)
|
|
|
|
|
oversizedOutputTx.vout.at(1).scriptPubKey << OP_0;
|
|
|
|
|
const CTransaction oversizedOutput(oversizedOutputTx);
|
2026-05-14 12:45:20 +02:00
|
|
|
newTx = Tx::fromOldTransaction(oversizedInput);
|
|
|
|
|
TransactionSignatureChecker oversizedOutputChecker(&oversizedOutput, newTx, 1, spentOutputs.at(1).outputValue, &spentOutputs);
|
2026-05-07 18:07:15 +02:00
|
|
|
stack.clear();
|
|
|
|
|
QVERIFY(!EvalScript(SCRIPT_ENABLE_NATIVE_INTROSPECTION, stack, CScript() << OP_1 << OP_OUTPUTBYTECODE,
|
|
|
|
|
oversizedOutputChecker, error));
|
|
|
|
|
QCOMPARE(error, SCRIPT_ERR_PUSH_SIZE);
|
|
|
|
|
}
|