2020-04-16 13:14:08 -04:00
|
|
|
// Copyright (c) 2011-2020 The Bitcoin Core developers
|
2014-12-13 12:09:33 +08:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2014-03-18 10:11:00 +01:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2013-04-13 00:13:08 -05:00
|
|
|
|
2017-08-15 21:22:09 +02:00
|
|
|
#include <test/data/tx_invalid.json.h>
|
|
|
|
#include <test/data/tx_valid.json.h>
|
2019-11-05 15:18:59 -05:00
|
|
|
#include <test/util/setup_common.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
|
|
|
|
#include <checkqueue.h>
|
2020-04-16 13:11:54 -04:00
|
|
|
#include <clientversion.h>
|
2021-09-11 10:29:00 +08:00
|
|
|
#include <consensus/amount.h>
|
2019-04-02 13:41:12 -04:00
|
|
|
#include <consensus/tx_check.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <consensus/validation.h>
|
|
|
|
#include <core_io.h>
|
|
|
|
#include <key.h>
|
|
|
|
#include <policy/policy.h>
|
2019-04-02 14:14:58 -04:00
|
|
|
#include <policy/settings.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <script/script.h>
|
2020-04-16 13:11:54 -04:00
|
|
|
#include <script/script_error.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <script/sign.h>
|
2019-06-06 22:52:24 +02:00
|
|
|
#include <script/signingprovider.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <script/standard.h>
|
2019-06-24 17:22:28 +02:00
|
|
|
#include <streams.h>
|
2021-02-23 07:04:51 -08:00
|
|
|
#include <test/util/script.h>
|
2020-02-18 17:11:16 +01:00
|
|
|
#include <test/util/transaction_utils.h>
|
2020-04-16 13:11:54 -04:00
|
|
|
#include <util/strencodings.h>
|
2021-01-26 14:08:28 -08:00
|
|
|
#include <util/string.h>
|
2020-04-16 13:11:54 -04:00
|
|
|
#include <validation.h>
|
2013-04-13 00:13:08 -05:00
|
|
|
|
2021-01-13 02:05:00 +01:00
|
|
|
#include <functional>
|
2012-08-04 18:28:49 +02:00
|
|
|
#include <map>
|
|
|
|
#include <string>
|
2013-04-13 00:13:08 -05:00
|
|
|
|
2014-03-10 18:17:56 -04:00
|
|
|
#include <boost/algorithm/string/classification.hpp>
|
|
|
|
#include <boost/algorithm/string/split.hpp>
|
2011-07-31 20:07:53 +02:00
|
|
|
#include <boost/test/unit_test.hpp>
|
2014-08-20 15:15:16 -04:00
|
|
|
|
2015-09-04 16:11:34 +02:00
|
|
|
#include <univalue.h>
|
2011-07-31 20:07:53 +02:00
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
typedef std::vector<unsigned char> valtype;
|
2015-11-20 16:22:47 +01:00
|
|
|
|
2012-08-04 18:28:49 +02:00
|
|
|
// In script_tests.cpp
|
2021-05-10 09:13:33 +02:00
|
|
|
UniValue read_json(const std::string& jsondata);
|
2011-07-31 20:07:53 +02:00
|
|
|
|
2017-06-06 21:15:28 +02:00
|
|
|
static std::map<std::string, unsigned int> mapFlagNames = {
|
|
|
|
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
|
|
|
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
|
|
|
|
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
|
|
|
{std::string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S},
|
|
|
|
{std::string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY},
|
|
|
|
{std::string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA},
|
|
|
|
{std::string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY},
|
|
|
|
{std::string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS},
|
|
|
|
{std::string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK},
|
|
|
|
{std::string("MINIMALIF"), (unsigned int)SCRIPT_VERIFY_MINIMALIF},
|
|
|
|
{std::string("NULLFAIL"), (unsigned int)SCRIPT_VERIFY_NULLFAIL},
|
|
|
|
{std::string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY},
|
|
|
|
{std::string("CHECKSEQUENCEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKSEQUENCEVERIFY},
|
|
|
|
{std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS},
|
|
|
|
{std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM},
|
|
|
|
{std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE},
|
2017-10-21 04:24:14 +08:00
|
|
|
{std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE},
|
2020-09-15 21:00:43 -07:00
|
|
|
{std::string("TAPROOT"), (unsigned int)SCRIPT_VERIFY_TAPROOT},
|
2021-02-23 07:04:51 -08:00
|
|
|
{std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE},
|
|
|
|
{std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
|
|
|
|
{std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
|
2017-06-06 21:15:28 +02:00
|
|
|
};
|
2016-12-05 16:03:53 +09:00
|
|
|
|
|
|
|
unsigned int ParseScriptFlags(std::string strFlags)
|
2014-09-20 03:13:04 +02:00
|
|
|
{
|
2021-02-24 12:55:43 +02:00
|
|
|
if (strFlags.empty() || strFlags == "NONE") return 0;
|
2014-03-10 18:17:56 -04:00
|
|
|
unsigned int flags = 0;
|
2016-12-05 16:03:53 +09:00
|
|
|
std::vector<std::string> words;
|
2014-12-19 16:50:15 -05:00
|
|
|
boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
|
2014-03-10 18:17:56 -04:00
|
|
|
|
2018-06-18 07:58:28 +02:00
|
|
|
for (const std::string& word : words)
|
2014-03-10 18:17:56 -04:00
|
|
|
{
|
|
|
|
if (!mapFlagNames.count(word))
|
|
|
|
BOOST_ERROR("Bad test: unknown verification flag '" << word << "'");
|
|
|
|
flags |= mapFlagNames[word];
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2021-04-05 09:41:25 -07:00
|
|
|
// Check that all flags in STANDARD_SCRIPT_VERIFY_FLAGS are present in mapFlagNames.
|
|
|
|
bool CheckMapFlagNames()
|
|
|
|
{
|
|
|
|
unsigned int standard_flags_missing{STANDARD_SCRIPT_VERIFY_FLAGS};
|
|
|
|
for (const auto& pair : mapFlagNames) {
|
|
|
|
standard_flags_missing &= ~(pair.second);
|
|
|
|
}
|
|
|
|
return standard_flags_missing == 0;
|
|
|
|
}
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
std::string FormatScriptFlags(unsigned int flags)
|
2014-09-20 03:13:04 +02:00
|
|
|
{
|
|
|
|
if (flags == 0) {
|
|
|
|
return "";
|
|
|
|
}
|
2016-12-05 16:03:53 +09:00
|
|
|
std::string ret;
|
|
|
|
std::map<std::string, unsigned int>::const_iterator it = mapFlagNames.begin();
|
2014-09-20 03:13:04 +02:00
|
|
|
while (it != mapFlagNames.end()) {
|
|
|
|
if (flags & it->second) {
|
|
|
|
ret += it->first + ",";
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
|
|
|
return ret.substr(0, ret.size() - 1);
|
|
|
|
}
|
|
|
|
|
2021-01-26 14:10:15 -08:00
|
|
|
/*
|
|
|
|
* Check that the input scripts of a transaction are valid/invalid as expected.
|
|
|
|
*/
|
|
|
|
bool CheckTxScripts(const CTransaction& tx, const std::map<COutPoint, CScript>& map_prevout_scriptPubKeys,
|
|
|
|
const std::map<COutPoint, int64_t>& map_prevout_values, unsigned int flags,
|
|
|
|
const PrecomputedTransactionData& txdata, const std::string& strTest, bool expect_valid)
|
|
|
|
{
|
|
|
|
bool tx_valid = true;
|
|
|
|
ScriptError err = expect_valid ? SCRIPT_ERR_UNKNOWN_ERROR : SCRIPT_ERR_OK;
|
|
|
|
for (unsigned int i = 0; i < tx.vin.size() && tx_valid; ++i) {
|
|
|
|
const CTxIn input = tx.vin[i];
|
|
|
|
const CAmount amount = map_prevout_values.count(input.prevout) ? map_prevout_values.at(input.prevout) : 0;
|
|
|
|
try {
|
|
|
|
tx_valid = VerifyScript(input.scriptSig, map_prevout_scriptPubKeys.at(input.prevout),
|
2021-03-01 18:07:14 -08:00
|
|
|
&input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata, MissingDataBehavior::ASSERT_FAIL), &err);
|
2021-01-26 14:10:15 -08:00
|
|
|
} catch (...) {
|
|
|
|
BOOST_ERROR("Bad test: " << strTest);
|
|
|
|
return true; // The test format is bad and an error is thrown. Return true to silence further error.
|
|
|
|
}
|
|
|
|
if (expect_valid) {
|
|
|
|
BOOST_CHECK_MESSAGE(tx_valid, strTest);
|
|
|
|
BOOST_CHECK_MESSAGE((err == SCRIPT_ERR_OK), ScriptErrorString(err));
|
|
|
|
err = SCRIPT_ERR_UNKNOWN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!expect_valid) {
|
|
|
|
BOOST_CHECK_MESSAGE(!tx_valid, strTest);
|
|
|
|
BOOST_CHECK_MESSAGE((err != SCRIPT_ERR_OK), ScriptErrorString(err));
|
|
|
|
}
|
|
|
|
return (tx_valid == expect_valid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Trim or fill flags to make the combination valid:
|
|
|
|
* WITNESS must be used with P2SH
|
|
|
|
* CLEANSTACK must be used WITNESS and P2SH
|
|
|
|
*/
|
|
|
|
|
|
|
|
unsigned int TrimFlags(unsigned int flags)
|
|
|
|
{
|
|
|
|
// WITNESS requires P2SH
|
|
|
|
if (!(flags & SCRIPT_VERIFY_P2SH)) flags &= ~(unsigned int)SCRIPT_VERIFY_WITNESS;
|
|
|
|
|
|
|
|
// CLEANSTACK requires WITNESS (and transitively CLEANSTACK requires P2SH)
|
|
|
|
if (!(flags & SCRIPT_VERIFY_WITNESS)) flags &= ~(unsigned int)SCRIPT_VERIFY_CLEANSTACK;
|
2021-02-23 07:04:51 -08:00
|
|
|
Assert(IsValidFlagCombination(flags));
|
2021-01-26 14:10:15 -08:00
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int FillFlags(unsigned int flags)
|
|
|
|
{
|
|
|
|
// CLEANSTACK implies WITNESS
|
|
|
|
if (flags & SCRIPT_VERIFY_CLEANSTACK) flags |= SCRIPT_VERIFY_WITNESS;
|
|
|
|
|
|
|
|
// WITNESS implies P2SH (and transitively CLEANSTACK implies P2SH)
|
|
|
|
if (flags & SCRIPT_VERIFY_WITNESS) flags |= SCRIPT_VERIFY_P2SH;
|
2021-02-23 07:04:51 -08:00
|
|
|
Assert(IsValidFlagCombination(flags));
|
2021-01-26 14:10:15 -08:00
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2021-02-23 06:56:34 -08:00
|
|
|
// Exclude each possible script verify flag from flags. Returns a set of these flag combinations
|
|
|
|
// that are valid and without duplicates. For example: if flags=1111 and the 4 possible flags are
|
|
|
|
// 0001, 0010, 0100, and 1000, this should return the set {0111, 1011, 1101, 1110}.
|
|
|
|
// Assumes that mapFlagNames contains all script verify flags.
|
|
|
|
std::set<unsigned int> ExcludeIndividualFlags(unsigned int flags)
|
2021-01-26 14:10:15 -08:00
|
|
|
{
|
2021-02-23 06:56:34 -08:00
|
|
|
std::set<unsigned int> flags_combos;
|
|
|
|
for (const auto& pair : mapFlagNames) {
|
|
|
|
const unsigned int flags_excluding_one = TrimFlags(flags & ~(pair.second));
|
|
|
|
if (flags != flags_excluding_one) {
|
|
|
|
flags_combos.insert(flags_excluding_one);
|
2021-01-26 14:10:15 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return flags_combos;
|
|
|
|
}
|
|
|
|
|
2015-03-12 09:34:42 +01:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
|
2011-07-31 20:07:53 +02:00
|
|
|
|
2012-08-04 18:28:49 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(tx_valid)
|
|
|
|
{
|
2021-04-05 09:41:25 -07:00
|
|
|
BOOST_CHECK_MESSAGE(CheckMapFlagNames(), "mapFlagNames is missing a script verification flag");
|
2012-08-04 18:28:49 +02:00
|
|
|
// Read tests from test/data/tx_valid.json
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
|
2012-08-04 18:28:49 +02:00
|
|
|
|
2014-08-20 15:15:16 -04:00
|
|
|
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue test = tests[idx];
|
2016-12-05 16:03:53 +09:00
|
|
|
std::string strTest = test.write();
|
2014-08-20 15:15:16 -04:00
|
|
|
if (test[0].isArray())
|
2012-08-04 18:28:49 +02:00
|
|
|
{
|
2014-08-20 15:15:16 -04:00
|
|
|
if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
|
2012-08-04 18:28:49 +02:00
|
|
|
{
|
|
|
|
BOOST_ERROR("Bad test: " << strTest);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
std::map<COutPoint, CScript> mapprevOutScriptPubKeys;
|
|
|
|
std::map<COutPoint, int64_t> mapprevOutValues;
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue inputs = test[0].get_array();
|
2012-08-04 18:28:49 +02:00
|
|
|
bool fValid = true;
|
2020-02-19 08:19:44 -08:00
|
|
|
for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
|
|
|
|
const UniValue& input = inputs[inpIdx];
|
|
|
|
if (!input.isArray()) {
|
2012-08-04 18:28:49 +02:00
|
|
|
fValid = false;
|
|
|
|
break;
|
|
|
|
}
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue vinput = input.get_array();
|
2016-04-03 23:48:50 +09:00
|
|
|
if (vinput.size() < 3 || vinput.size() > 4)
|
2012-08-04 18:28:49 +02:00
|
|
|
{
|
|
|
|
fValid = false;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-03 23:48:50 +09:00
|
|
|
COutPoint outpoint(uint256S(vinput[0].get_str()), vinput[1].get_int());
|
|
|
|
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
|
|
|
|
if (vinput.size() >= 4)
|
|
|
|
{
|
|
|
|
mapprevOutValues[outpoint] = vinput[3].get_int64();
|
|
|
|
}
|
2012-08-04 18:28:49 +02:00
|
|
|
}
|
|
|
|
if (!fValid)
|
|
|
|
{
|
|
|
|
BOOST_ERROR("Bad test: " << strTest);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
std::string transaction = test[1].get_str();
|
2012-08-04 18:28:49 +02:00
|
|
|
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION);
|
2016-11-11 16:23:17 -08:00
|
|
|
CTransaction tx(deserialize, stream);
|
2012-08-04 18:28:49 +02:00
|
|
|
|
2019-10-24 11:35:42 -04:00
|
|
|
TxValidationState state;
|
2013-01-08 04:17:15 -08:00
|
|
|
BOOST_CHECK_MESSAGE(CheckTransaction(tx, state), strTest);
|
2013-01-27 00:14:11 +01:00
|
|
|
BOOST_CHECK(state.IsValid());
|
2012-09-05 21:46:48 -04:00
|
|
|
|
2016-08-26 18:38:20 +02:00
|
|
|
PrecomputedTransactionData txdata(tx);
|
2021-01-26 14:10:15 -08:00
|
|
|
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
2012-08-04 18:28:49 +02:00
|
|
|
|
2021-02-02 09:16:34 -08:00
|
|
|
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
|
|
|
|
if (~verify_flags != FillFlags(~verify_flags)) {
|
|
|
|
BOOST_ERROR("Bad test flags: " << strTest);
|
|
|
|
}
|
|
|
|
|
2021-02-23 07:04:51 -08:00
|
|
|
BOOST_CHECK_MESSAGE(CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true),
|
|
|
|
"Tx unexpectedly failed: " << strTest);
|
2021-01-26 14:08:28 -08:00
|
|
|
|
|
|
|
// Backwards compatibility of script verification flags: Removing any flag(s) should not invalidate a valid transaction
|
2021-02-23 07:04:51 -08:00
|
|
|
for (const auto& [name, flag] : mapFlagNames) {
|
2021-01-26 14:08:28 -08:00
|
|
|
// Removing individual flags
|
2021-02-23 07:04:51 -08:00
|
|
|
unsigned int flags = TrimFlags(~(verify_flags | flag));
|
2021-01-26 14:08:28 -08:00
|
|
|
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ true)) {
|
2021-02-23 07:04:51 -08:00
|
|
|
BOOST_ERROR("Tx unexpectedly failed with flag " << name << " unset: " << strTest);
|
2021-01-26 14:08:28 -08:00
|
|
|
}
|
|
|
|
// Removing random combinations of flags
|
|
|
|
flags = TrimFlags(~(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size())));
|
|
|
|
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ true)) {
|
|
|
|
BOOST_ERROR("Tx unexpectedly failed with random flags " << ToString(flags) << ": " << strTest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 14:10:18 -08:00
|
|
|
// Check that flags are maximal: transaction should fail if any unset flags are set.
|
2021-02-23 07:04:51 -08:00
|
|
|
for (auto flags_excluding_one : ExcludeIndividualFlags(verify_flags)) {
|
2021-01-26 14:10:18 -08:00
|
|
|
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~flags_excluding_one, txdata, strTest, /* expect_valid */ false)) {
|
|
|
|
BOOST_ERROR("Too many flags unset: " << strTest);
|
|
|
|
}
|
|
|
|
}
|
2012-08-04 18:28:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(tx_invalid)
|
|
|
|
{
|
|
|
|
// Read tests from test/data/tx_invalid.json
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
|
2012-08-04 18:28:49 +02:00
|
|
|
|
2014-08-20 15:15:16 -04:00
|
|
|
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue test = tests[idx];
|
2016-12-05 16:03:53 +09:00
|
|
|
std::string strTest = test.write();
|
2014-08-20 15:15:16 -04:00
|
|
|
if (test[0].isArray())
|
2012-08-04 18:28:49 +02:00
|
|
|
{
|
2014-08-20 15:15:16 -04:00
|
|
|
if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
|
2012-08-04 18:28:49 +02:00
|
|
|
{
|
|
|
|
BOOST_ERROR("Bad test: " << strTest);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
std::map<COutPoint, CScript> mapprevOutScriptPubKeys;
|
|
|
|
std::map<COutPoint, int64_t> mapprevOutValues;
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue inputs = test[0].get_array();
|
2012-08-04 18:28:49 +02:00
|
|
|
bool fValid = true;
|
2020-02-19 08:19:44 -08:00
|
|
|
for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
|
|
|
|
const UniValue& input = inputs[inpIdx];
|
|
|
|
if (!input.isArray()) {
|
2012-08-04 18:28:49 +02:00
|
|
|
fValid = false;
|
|
|
|
break;
|
|
|
|
}
|
2015-05-13 21:29:19 +02:00
|
|
|
UniValue vinput = input.get_array();
|
2016-04-03 23:48:50 +09:00
|
|
|
if (vinput.size() < 3 || vinput.size() > 4)
|
2012-08-04 18:28:49 +02:00
|
|
|
{
|
|
|
|
fValid = false;
|
|
|
|
break;
|
|
|
|
}
|
2016-04-03 23:48:50 +09:00
|
|
|
COutPoint outpoint(uint256S(vinput[0].get_str()), vinput[1].get_int());
|
|
|
|
mapprevOutScriptPubKeys[outpoint] = ParseScript(vinput[2].get_str());
|
|
|
|
if (vinput.size() >= 4)
|
|
|
|
{
|
|
|
|
mapprevOutValues[outpoint] = vinput[3].get_int64();
|
|
|
|
}
|
2012-08-04 18:28:49 +02:00
|
|
|
}
|
|
|
|
if (!fValid)
|
|
|
|
{
|
|
|
|
BOOST_ERROR("Bad test: " << strTest);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
std::string transaction = test[1].get_str();
|
2015-11-06 01:32:04 +01:00
|
|
|
CDataStream stream(ParseHex(transaction), SER_NETWORK, PROTOCOL_VERSION );
|
2016-11-11 16:23:17 -08:00
|
|
|
CTransaction tx(deserialize, stream);
|
2012-08-04 18:28:49 +02:00
|
|
|
|
2019-10-24 11:35:42 -04:00
|
|
|
TxValidationState state;
|
2021-01-26 14:10:08 -08:00
|
|
|
if (!CheckTransaction(tx, state) || state.IsInvalid()) {
|
|
|
|
BOOST_CHECK_MESSAGE(test[2].get_str() == "BADTX", strTest);
|
|
|
|
continue;
|
|
|
|
}
|
2012-09-05 21:46:48 -04:00
|
|
|
|
2016-08-26 18:38:20 +02:00
|
|
|
PrecomputedTransactionData txdata(tx);
|
2021-01-26 14:10:15 -08:00
|
|
|
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
2012-08-04 18:28:49 +02:00
|
|
|
|
2021-02-23 07:04:51 -08:00
|
|
|
// Check that the test gives a valid combination of flags (otherwise VerifyScript will throw). Don't edit the flags.
|
|
|
|
if (verify_flags != FillFlags(verify_flags)) {
|
|
|
|
BOOST_ERROR("Bad test flags: " << strTest);
|
2012-08-04 18:28:49 +02:00
|
|
|
}
|
2021-01-26 14:08:28 -08:00
|
|
|
|
2021-02-23 07:04:51 -08:00
|
|
|
// Not using FillFlags() in the main test, in order to detect invalid verifyFlags combination
|
|
|
|
BOOST_CHECK_MESSAGE(CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false),
|
|
|
|
"Tx unexpectedly passed: " << strTest);
|
|
|
|
|
2021-01-26 14:08:28 -08:00
|
|
|
// Backwards compatibility of script verification flags: Adding any flag(s) should not validate an invalid transaction
|
2021-02-23 07:04:51 -08:00
|
|
|
for (const auto& [name, flag] : mapFlagNames) {
|
|
|
|
unsigned int flags = FillFlags(verify_flags | flag);
|
2021-01-26 14:08:28 -08:00
|
|
|
// Adding individual flags
|
|
|
|
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ false)) {
|
2021-02-23 07:04:51 -08:00
|
|
|
BOOST_ERROR("Tx unexpectedly passed with flag " << name << " set: " << strTest);
|
2021-01-26 14:08:28 -08:00
|
|
|
}
|
|
|
|
// Adding random combinations of flags
|
|
|
|
flags = FillFlags(verify_flags | (unsigned int)InsecureRandBits(mapFlagNames.size()));
|
|
|
|
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags, txdata, strTest, /* expect_valid */ false)) {
|
2021-02-23 07:04:51 -08:00
|
|
|
BOOST_ERROR("Tx unexpectedly passed with random flags " << name << ": " << strTest);
|
2021-01-26 14:08:28 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-26 14:10:18 -08:00
|
|
|
// Check that flags are minimal: transaction should succeed if any set flags are unset.
|
2021-02-23 07:04:51 -08:00
|
|
|
for (auto flags_excluding_one : ExcludeIndividualFlags(verify_flags)) {
|
2021-01-26 14:10:18 -08:00
|
|
|
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, flags_excluding_one, txdata, strTest, /* expect_valid */ true)) {
|
|
|
|
BOOST_ERROR("Too many flags set: " << strTest);
|
|
|
|
}
|
|
|
|
}
|
2012-08-04 18:28:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-31 20:07:53 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(basic_transaction_tests)
|
|
|
|
{
|
|
|
|
// Random real transaction (e2769b09e784f32f62ef849763d4f45b98e07ba658647343b915ff832b110436)
|
|
|
|
unsigned char ch[] = {0x01, 0x00, 0x00, 0x00, 0x01, 0x6b, 0xff, 0x7f, 0xcd, 0x4f, 0x85, 0x65, 0xef, 0x40, 0x6d, 0xd5, 0xd6, 0x3d, 0x4f, 0xf9, 0x4f, 0x31, 0x8f, 0xe8, 0x20, 0x27, 0xfd, 0x4d, 0xc4, 0x51, 0xb0, 0x44, 0x74, 0x01, 0x9f, 0x74, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x49, 0x30, 0x46, 0x02, 0x21, 0x00, 0xda, 0x0d, 0xc6, 0xae, 0xce, 0xfe, 0x1e, 0x06, 0xef, 0xdf, 0x05, 0x77, 0x37, 0x57, 0xde, 0xb1, 0x68, 0x82, 0x09, 0x30, 0xe3, 0xb0, 0xd0, 0x3f, 0x46, 0xf5, 0xfc, 0xf1, 0x50, 0xbf, 0x99, 0x0c, 0x02, 0x21, 0x00, 0xd2, 0x5b, 0x5c, 0x87, 0x04, 0x00, 0x76, 0xe4, 0xf2, 0x53, 0xf8, 0x26, 0x2e, 0x76, 0x3e, 0x2d, 0xd5, 0x1e, 0x7f, 0xf0, 0xbe, 0x15, 0x77, 0x27, 0xc4, 0xbc, 0x42, 0x80, 0x7f, 0x17, 0xbd, 0x39, 0x01, 0x41, 0x04, 0xe6, 0xc2, 0x6e, 0xf6, 0x7d, 0xc6, 0x10, 0xd2, 0xcd, 0x19, 0x24, 0x84, 0x78, 0x9a, 0x6c, 0xf9, 0xae, 0xa9, 0x93, 0x0b, 0x94, 0x4b, 0x7e, 0x2d, 0xb5, 0x34, 0x2b, 0x9d, 0x9e, 0x5b, 0x9f, 0xf7, 0x9a, 0xff, 0x9a, 0x2e, 0xe1, 0x97, 0x8d, 0xd7, 0xfd, 0x01, 0xdf, 0xc5, 0x22, 0xee, 0x02, 0x28, 0x3d, 0x3b, 0x06, 0xa9, 0xd0, 0x3a, 0xcf, 0x80, 0x96, 0x96, 0x8d, 0x7d, 0xbb, 0x0f, 0x91, 0x78, 0xff, 0xff, 0xff, 0xff, 0x02, 0x8b, 0xa7, 0x94, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xba, 0xde, 0xec, 0xfd, 0xef, 0x05, 0x07, 0x24, 0x7f, 0xc8, 0xf7, 0x42, 0x41, 0xd7, 0x3b, 0xc0, 0x39, 0x97, 0x2d, 0x7b, 0x88, 0xac, 0x40, 0x94, 0xa8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x19, 0x76, 0xa9, 0x14, 0xc1, 0x09, 0x32, 0x48, 0x3f, 0xec, 0x93, 0xed, 0x51, 0xf5, 0xfe, 0x95, 0xe7, 0x25, 0x59, 0xf2, 0xcc, 0x70, 0x43, 0xf9, 0x88, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00};
|
2016-12-05 16:03:53 +09:00
|
|
|
std::vector<unsigned char> vch(ch, ch + sizeof(ch) -1);
|
2012-04-17 20:37:47 +02:00
|
|
|
CDataStream stream(vch, SER_DISK, CLIENT_VERSION);
|
2014-06-07 13:53:27 +02:00
|
|
|
CMutableTransaction tx;
|
2011-07-31 20:07:53 +02:00
|
|
|
stream >> tx;
|
2019-10-24 11:35:42 -04:00
|
|
|
TxValidationState state;
|
2018-12-08 15:38:12 -08:00
|
|
|
BOOST_CHECK_MESSAGE(CheckTransaction(CTransaction(tx), state) && state.IsValid(), "Simple deserialized transaction should be valid.");
|
2011-07-31 20:07:53 +02:00
|
|
|
|
|
|
|
// Check that duplicate txins fail
|
|
|
|
tx.vin.push_back(tx.vin[0]);
|
2018-12-08 15:38:12 -08:00
|
|
|
BOOST_CHECK_MESSAGE(!CheckTransaction(CTransaction(tx), state) || !state.IsValid(), "Transaction with duplicate txins should be invalid.");
|
2011-07-31 20:07:53 +02:00
|
|
|
}
|
|
|
|
|
2012-01-10 20:18:00 -05:00
|
|
|
BOOST_AUTO_TEST_CASE(test_Get)
|
|
|
|
{
|
2019-06-06 16:33:23 +02:00
|
|
|
FillableSigningProvider keystore;
|
Ultraprune
This switches bitcoin's transaction/block verification logic to use a
"coin database", which contains all unredeemed transaction output scripts,
amounts and heights.
The name ultraprune comes from the fact that instead of a full transaction
index, we only (need to) keep an index with unspent outputs. For now, the
blocks themselves are kept as usual, although they are only necessary for
serving, rescanning and reorganizing.
The basic datastructures are CCoins (representing the coins of a single
transaction), and CCoinsView (representing a state of the coins database).
There are several implementations for CCoinsView. A dummy, one backed by
the coins database (coins.dat), one backed by the memory pool, and one
that adds a cache on top of it. FetchInputs, ConnectInputs, ConnectBlock,
DisconnectBlock, ... now operate on a generic CCoinsView.
The block switching logic now builds a single cached CCoinsView with
changes to be committed to the database before any changes are made.
This means no uncommitted changes are ever read from the database, and
should ease the transition to another database layer which does not
support transactions (but does support atomic writes), like LevelDB.
For the getrawtransaction() RPC call, access to a txid-to-disk index
would be preferable. As this index is not necessary or even useful
for any other part of the implementation, it is not provided. Instead,
getrawtransaction() uses the coin database to find the block height,
and then scans that block to find the requested transaction. This is
slow, but should suffice for debug purposes.
2012-07-01 18:54:00 +02:00
|
|
|
CCoinsView coinsDummy;
|
2014-09-24 03:19:04 +02:00
|
|
|
CCoinsViewCache coins(&coinsDummy);
|
2020-02-18 17:11:16 +01:00
|
|
|
std::vector<CMutableTransaction> dummyTransactions =
|
|
|
|
SetupDummyInputs(keystore, coins, {11*CENT, 50*CENT, 21*CENT, 22*CENT});
|
2012-01-10 20:18:00 -05:00
|
|
|
|
2014-06-07 13:53:27 +02:00
|
|
|
CMutableTransaction t1;
|
2012-01-10 20:18:00 -05:00
|
|
|
t1.vin.resize(3);
|
|
|
|
t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
|
|
|
|
t1.vin[0].prevout.n = 1;
|
2012-01-19 13:30:54 -05:00
|
|
|
t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
|
|
|
|
t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();
|
2012-01-10 20:18:00 -05:00
|
|
|
t1.vin[1].prevout.n = 0;
|
2012-01-19 13:30:54 -05:00
|
|
|
t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
|
|
|
|
t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();
|
2012-01-10 20:18:00 -05:00
|
|
|
t1.vin[2].prevout.n = 1;
|
2012-01-19 13:30:54 -05:00
|
|
|
t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
|
2012-01-10 20:18:00 -05:00
|
|
|
t1.vout.resize(2);
|
|
|
|
t1.vout[0].nValue = 90*CENT;
|
|
|
|
t1.vout[0].scriptPubKey << OP_1;
|
|
|
|
|
2020-10-15 20:57:58 -07:00
|
|
|
BOOST_CHECK(AreInputsStandard(CTransaction(t1), coins, false));
|
2012-01-10 20:18:00 -05:00
|
|
|
}
|
|
|
|
|
2019-06-06 16:33:23 +02:00
|
|
|
static void CreateCreditAndSpend(const FillableSigningProvider& keystore, const CScript& outscript, CTransactionRef& output, CMutableTransaction& input, bool success = true)
|
2015-11-20 16:22:47 +01:00
|
|
|
{
|
|
|
|
CMutableTransaction outputm;
|
|
|
|
outputm.nVersion = 1;
|
|
|
|
outputm.vin.resize(1);
|
|
|
|
outputm.vin[0].prevout.SetNull();
|
|
|
|
outputm.vin[0].scriptSig = CScript();
|
|
|
|
outputm.vout.resize(1);
|
|
|
|
outputm.vout[0].nValue = 1;
|
|
|
|
outputm.vout[0].scriptPubKey = outscript;
|
|
|
|
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssout << outputm;
|
|
|
|
ssout >> output;
|
2016-11-11 16:23:17 -08:00
|
|
|
assert(output->vin.size() == 1);
|
|
|
|
assert(output->vin[0] == outputm.vin[0]);
|
|
|
|
assert(output->vout.size() == 1);
|
|
|
|
assert(output->vout[0] == outputm.vout[0]);
|
2015-11-20 16:22:47 +01:00
|
|
|
|
|
|
|
CMutableTransaction inputm;
|
|
|
|
inputm.nVersion = 1;
|
|
|
|
inputm.vin.resize(1);
|
2016-11-11 16:23:17 -08:00
|
|
|
inputm.vin[0].prevout.hash = output->GetHash();
|
2015-11-20 16:22:47 +01:00
|
|
|
inputm.vin[0].prevout.n = 0;
|
|
|
|
inputm.vout.resize(1);
|
|
|
|
inputm.vout[0].nValue = 1;
|
|
|
|
inputm.vout[0].scriptPubKey = CScript();
|
2016-11-11 16:23:17 -08:00
|
|
|
bool ret = SignSignature(keystore, *output, inputm, 0, SIGHASH_ALL);
|
2015-11-20 16:22:47 +01:00
|
|
|
assert(ret == success);
|
|
|
|
CDataStream ssin(SER_NETWORK, PROTOCOL_VERSION);
|
|
|
|
ssin << inputm;
|
|
|
|
ssin >> input;
|
|
|
|
assert(input.vin.size() == 1);
|
|
|
|
assert(input.vin[0] == inputm.vin[0]);
|
|
|
|
assert(input.vout.size() == 1);
|
|
|
|
assert(input.vout[0] == inputm.vout[0]);
|
2016-08-04 02:49:16 +02:00
|
|
|
assert(input.vin[0].scriptWitness.stack == inputm.vin[0].scriptWitness.stack);
|
2015-11-20 16:22:47 +01:00
|
|
|
}
|
|
|
|
|
2021-06-13 17:11:09 +02:00
|
|
|
static void CheckWithFlag(const CTransactionRef& output, const CMutableTransaction& input, uint32_t flags, bool success)
|
2015-11-20 16:22:47 +01:00
|
|
|
{
|
|
|
|
ScriptError error;
|
|
|
|
CTransaction inputi(input);
|
2021-03-01 18:07:14 -08:00
|
|
|
bool ret = VerifyScript(inputi.vin[0].scriptSig, output->vout[0].scriptPubKey, &inputi.vin[0].scriptWitness, flags, TransactionSignatureChecker(&inputi, 0, output->vout[0].nValue, MissingDataBehavior::ASSERT_FAIL), &error);
|
2015-11-20 16:22:47 +01:00
|
|
|
assert(ret == success);
|
|
|
|
}
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
static CScript PushAll(const std::vector<valtype>& values)
|
2015-11-20 16:22:47 +01:00
|
|
|
{
|
|
|
|
CScript result;
|
2017-06-02 03:18:57 +02:00
|
|
|
for (const valtype& v : values) {
|
2015-11-20 16:22:47 +01:00
|
|
|
if (v.size() == 0) {
|
|
|
|
result << OP_0;
|
|
|
|
} else if (v.size() == 1 && v[0] >= 1 && v[0] <= 16) {
|
|
|
|
result << CScript::EncodeOP_N(v[0]);
|
2018-04-25 16:49:39 -07:00
|
|
|
} else if (v.size() == 1 && v[0] == 0x81) {
|
|
|
|
result << OP_1NEGATE;
|
2015-11-20 16:22:47 +01:00
|
|
|
} else {
|
|
|
|
result << v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-05-02 17:14:48 +02:00
|
|
|
static void ReplaceRedeemScript(CScript& script, const CScript& redeemScript)
|
2015-11-20 16:22:47 +01:00
|
|
|
{
|
2016-12-05 16:03:53 +09:00
|
|
|
std::vector<valtype> stack;
|
2018-03-09 15:03:40 +01:00
|
|
|
EvalScript(stack, script, SCRIPT_VERIFY_STRICTENC, BaseSignatureChecker(), SigVersion::BASE);
|
2015-11-20 16:22:47 +01:00
|
|
|
assert(stack.size() > 0);
|
|
|
|
stack.back() = std::vector<unsigned char>(redeemScript.begin(), redeemScript.end());
|
|
|
|
script = PushAll(stack);
|
|
|
|
}
|
|
|
|
|
2018-12-02 01:56:12 -08:00
|
|
|
BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
|
|
|
|
{
|
2016-08-16 15:37:56 +02:00
|
|
|
CMutableTransaction mtx;
|
|
|
|
mtx.nVersion = 1;
|
|
|
|
|
|
|
|
CKey key;
|
2016-10-02 01:19:33 +08:00
|
|
|
key.MakeNewKey(true); // Need to use compressed keys in segwit or the signing will fail
|
2019-06-06 16:33:23 +02:00
|
|
|
FillableSigningProvider keystore;
|
2018-12-12 14:31:38 +01:00
|
|
|
BOOST_CHECK(keystore.AddKeyPubKey(key, key.GetPubKey()));
|
2016-08-16 15:37:56 +02:00
|
|
|
CKeyID hash = key.GetPubKey().GetID();
|
|
|
|
CScript scriptPubKey = CScript() << OP_0 << std::vector<unsigned char>(hash.begin(), hash.end());
|
|
|
|
|
2016-12-05 16:03:53 +09:00
|
|
|
std::vector<int> sigHashes;
|
2016-08-16 15:37:56 +02:00
|
|
|
sigHashes.push_back(SIGHASH_NONE | SIGHASH_ANYONECANPAY);
|
|
|
|
sigHashes.push_back(SIGHASH_SINGLE | SIGHASH_ANYONECANPAY);
|
|
|
|
sigHashes.push_back(SIGHASH_ALL | SIGHASH_ANYONECANPAY);
|
|
|
|
sigHashes.push_back(SIGHASH_NONE);
|
|
|
|
sigHashes.push_back(SIGHASH_SINGLE);
|
|
|
|
sigHashes.push_back(SIGHASH_ALL);
|
|
|
|
|
|
|
|
// create a big transaction of 4500 inputs signed by the same key
|
|
|
|
for(uint32_t ij = 0; ij < 4500; ij++) {
|
|
|
|
uint32_t i = mtx.vin.size();
|
|
|
|
uint256 prevId;
|
|
|
|
prevId.SetHex("0000000000000000000000000000000000000000000000000000000000000100");
|
|
|
|
COutPoint outpoint(prevId, i);
|
|
|
|
|
|
|
|
mtx.vin.resize(mtx.vin.size() + 1);
|
|
|
|
mtx.vin[i].prevout = outpoint;
|
|
|
|
mtx.vin[i].scriptSig = CScript();
|
|
|
|
|
|
|
|
mtx.vout.resize(mtx.vout.size() + 1);
|
|
|
|
mtx.vout[i].nValue = 1000;
|
|
|
|
mtx.vout[i].scriptPubKey = CScript() << OP_1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sign all inputs
|
|
|
|
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
|
|
|
bool hashSigned = SignSignature(keystore, scriptPubKey, mtx, i, 1000, sigHashes.at(i % sigHashes.size()));
|
|
|
|
assert(hashSigned);
|
|
|
|
}
|
|
|
|
|
|
|
|
CDataStream ssout(SER_NETWORK, PROTOCOL_VERSION);
|
2018-12-02 01:56:12 -08:00
|
|
|
ssout << mtx;
|
|
|
|
CTransaction tx(deserialize, ssout);
|
2016-08-16 15:37:56 +02:00
|
|
|
|
|
|
|
// check all inputs concurrently, with the cache
|
2016-08-26 18:38:20 +02:00
|
|
|
PrecomputedTransactionData txdata(tx);
|
2016-08-16 15:37:56 +02:00
|
|
|
CCheckQueue<CScriptCheck> scriptcheckqueue(128);
|
|
|
|
CCheckQueueControl<CScriptCheck> control(&scriptcheckqueue);
|
|
|
|
|
2020-08-21 09:24:32 +03:00
|
|
|
scriptcheckqueue.StartWorkerThreads(20);
|
2016-08-16 15:37:56 +02:00
|
|
|
|
2017-04-25 11:29:32 -07:00
|
|
|
std::vector<Coin> coins;
|
2016-08-16 15:37:56 +02:00
|
|
|
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
2017-04-25 11:29:32 -07:00
|
|
|
Coin coin;
|
|
|
|
coin.nHeight = 1;
|
|
|
|
coin.fCoinBase = false;
|
|
|
|
coin.out.nValue = 1000;
|
|
|
|
coin.out.scriptPubKey = scriptPubKey;
|
|
|
|
coins.emplace_back(std::move(coin));
|
2016-08-16 15:37:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < mtx.vin.size(); i++) {
|
|
|
|
std::vector<CScriptCheck> vChecks;
|
2017-07-30 17:32:39 +08:00
|
|
|
CScriptCheck check(coins[tx.vin[i].prevout.n].out, tx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false, &txdata);
|
2016-08-16 15:37:56 +02:00
|
|
|
vChecks.push_back(CScriptCheck());
|
|
|
|
check.swap(vChecks.back());
|
|
|
|
control.Add(vChecks);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool controlCheck = control.Wait();
|
|
|
|
assert(controlCheck);
|
2020-08-21 09:24:32 +03:00
|
|
|
scriptcheckqueue.StopWorkerThreads();
|
2016-08-16 15:37:56 +02:00
|
|
|
}
|
|
|
|
|
2018-06-08 14:39:36 -07:00
|
|
|
SignatureData CombineSignatures(const CMutableTransaction& input1, const CMutableTransaction& input2, const CTransactionRef tx)
|
|
|
|
{
|
|
|
|
SignatureData sigdata;
|
|
|
|
sigdata = DataFromTransaction(input1, 0, tx->vout[0]);
|
|
|
|
sigdata.MergeSignatureData(DataFromTransaction(input2, 0, tx->vout[0]));
|
2021-06-17 17:36:48 -07:00
|
|
|
ProduceSignature(DUMMY_SIGNING_PROVIDER, MutableTransactionSignatureCreator(&input1, 0, tx->vout[0].nValue, SIGHASH_ALL), tx->vout[0].scriptPubKey, sigdata);
|
2018-06-08 14:39:36 -07:00
|
|
|
return sigdata;
|
|
|
|
}
|
|
|
|
|
2015-11-20 16:22:47 +01:00
|
|
|
BOOST_AUTO_TEST_CASE(test_witness)
|
|
|
|
{
|
2019-06-06 16:33:23 +02:00
|
|
|
FillableSigningProvider keystore, keystore2;
|
2015-11-20 16:22:47 +01:00
|
|
|
CKey key1, key2, key3, key1L, key2L;
|
|
|
|
CPubKey pubkey1, pubkey2, pubkey3, pubkey1L, pubkey2L;
|
|
|
|
key1.MakeNewKey(true);
|
|
|
|
key2.MakeNewKey(true);
|
|
|
|
key3.MakeNewKey(true);
|
|
|
|
key1L.MakeNewKey(false);
|
|
|
|
key2L.MakeNewKey(false);
|
|
|
|
pubkey1 = key1.GetPubKey();
|
|
|
|
pubkey2 = key2.GetPubKey();
|
|
|
|
pubkey3 = key3.GetPubKey();
|
|
|
|
pubkey1L = key1L.GetPubKey();
|
|
|
|
pubkey2L = key2L.GetPubKey();
|
2018-12-12 14:31:38 +01:00
|
|
|
BOOST_CHECK(keystore.AddKeyPubKey(key1, pubkey1));
|
|
|
|
BOOST_CHECK(keystore.AddKeyPubKey(key2, pubkey2));
|
|
|
|
BOOST_CHECK(keystore.AddKeyPubKey(key1L, pubkey1L));
|
|
|
|
BOOST_CHECK(keystore.AddKeyPubKey(key2L, pubkey2L));
|
2015-11-20 16:22:47 +01:00
|
|
|
CScript scriptPubkey1, scriptPubkey2, scriptPubkey1L, scriptPubkey2L, scriptMulti;
|
|
|
|
scriptPubkey1 << ToByteVector(pubkey1) << OP_CHECKSIG;
|
|
|
|
scriptPubkey2 << ToByteVector(pubkey2) << OP_CHECKSIG;
|
|
|
|
scriptPubkey1L << ToByteVector(pubkey1L) << OP_CHECKSIG;
|
2016-04-03 23:48:50 +09:00
|
|
|
scriptPubkey2L << ToByteVector(pubkey2L) << OP_CHECKSIG;
|
2015-11-20 16:22:47 +01:00
|
|
|
std::vector<CPubKey> oneandthree;
|
|
|
|
oneandthree.push_back(pubkey1);
|
|
|
|
oneandthree.push_back(pubkey3);
|
|
|
|
scriptMulti = GetScriptForMultisig(2, oneandthree);
|
2018-12-12 14:31:38 +01:00
|
|
|
BOOST_CHECK(keystore.AddCScript(scriptPubkey1));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(scriptPubkey2));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(scriptPubkey1L));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(scriptPubkey2L));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(scriptMulti));
|
2020-07-02 11:47:18 +12:00
|
|
|
CScript destination_script_1, destination_script_2, destination_script_1L, destination_script_2L, destination_script_multi;
|
|
|
|
destination_script_1 = GetScriptForDestination(WitnessV0KeyHash(pubkey1));
|
|
|
|
destination_script_2 = GetScriptForDestination(WitnessV0KeyHash(pubkey2));
|
|
|
|
destination_script_1L = GetScriptForDestination(WitnessV0KeyHash(pubkey1L));
|
|
|
|
destination_script_2L = GetScriptForDestination(WitnessV0KeyHash(pubkey2L));
|
|
|
|
destination_script_multi = GetScriptForDestination(WitnessV0ScriptHash(scriptMulti));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(destination_script_1));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(destination_script_2));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(destination_script_1L));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(destination_script_2L));
|
|
|
|
BOOST_CHECK(keystore.AddCScript(destination_script_multi));
|
2018-12-12 14:31:38 +01:00
|
|
|
BOOST_CHECK(keystore2.AddCScript(scriptMulti));
|
2020-07-02 11:47:18 +12:00
|
|
|
BOOST_CHECK(keystore2.AddCScript(destination_script_multi));
|
2018-12-12 14:31:38 +01:00
|
|
|
BOOST_CHECK(keystore2.AddKeyPubKey(key3, pubkey3));
|
2015-11-20 16:22:47 +01:00
|
|
|
|
2016-11-11 16:23:17 -08:00
|
|
|
CTransactionRef output1, output2;
|
2015-11-20 16:22:47 +01:00
|
|
|
CMutableTransaction input1, input2;
|
|
|
|
|
|
|
|
// Normal pay-to-compressed-pubkey.
|
|
|
|
CreateCreditAndSpend(keystore, scriptPubkey1, output1, input1);
|
|
|
|
CreateCreditAndSpend(keystore, scriptPubkey2, output2, input2);
|
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
CheckWithFlag(output1, input2, 0, false);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
|
|
|
|
|
|
|
|
// P2SH pay-to-compressed-pubkey.
|
2019-02-19 17:00:45 -05:00
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1)), output1, input1);
|
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2)), output2, input2);
|
2015-11-20 16:22:47 +01:00
|
|
|
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1);
|
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
CheckWithFlag(output1, input2, 0, true);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
|
|
|
|
|
|
|
|
// Witness pay-to-compressed-pubkey (v0).
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore, destination_script_1, output1, input1);
|
|
|
|
CreateCreditAndSpend(keystore, destination_script_2, output2, input2);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
CheckWithFlag(output1, input2, 0, true);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
|
|
|
|
|
|
|
|
// P2SH witness pay-to-compressed-pubkey (v0).
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_1)), output1, input1);
|
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_2)), output2, input2);
|
|
|
|
ReplaceRedeemScript(input2.vin[0].scriptSig, destination_script_1);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
CheckWithFlag(output1, input2, 0, true);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
|
|
|
|
|
|
|
|
// Normal pay-to-uncompressed-pubkey.
|
|
|
|
CreateCreditAndSpend(keystore, scriptPubkey1L, output1, input1);
|
|
|
|
CreateCreditAndSpend(keystore, scriptPubkey2L, output2, input2);
|
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
CheckWithFlag(output1, input2, 0, false);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
|
|
|
|
|
|
|
|
// P2SH pay-to-uncompressed-pubkey.
|
2019-02-19 17:00:45 -05:00
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey1L)), output1, input1);
|
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptPubkey2L)), output2, input2);
|
2015-11-20 16:22:47 +01:00
|
|
|
ReplaceRedeemScript(input2.vin[0].scriptSig, scriptPubkey1L);
|
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
CheckWithFlag(output1, input2, 0, true);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_P2SH, false);
|
|
|
|
CheckWithFlag(output1, input2, STANDARD_SCRIPT_VERIFY_FLAGS, false);
|
|
|
|
|
2016-10-02 01:19:33 +08:00
|
|
|
// Signing disabled for witness pay-to-uncompressed-pubkey (v1).
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore, destination_script_1L, output1, input1, false);
|
|
|
|
CreateCreditAndSpend(keystore, destination_script_2L, output2, input2, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
|
2016-10-02 01:19:33 +08:00
|
|
|
// Signing disabled for P2SH witness pay-to-uncompressed-pubkey (v1).
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_1L)), output1, input1, false);
|
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_2L)), output2, input2, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
|
|
|
|
// Normal 2-of-2 multisig
|
|
|
|
CreateCreditAndSpend(keystore, scriptMulti, output1, input1, false);
|
|
|
|
CheckWithFlag(output1, input1, 0, false);
|
|
|
|
CreateCreditAndSpend(keystore2, scriptMulti, output2, input2, false);
|
|
|
|
CheckWithFlag(output2, input2, 0, false);
|
2016-11-11 16:23:17 -08:00
|
|
|
BOOST_CHECK(*output1 == *output2);
|
2018-06-08 14:39:36 -07:00
|
|
|
UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
|
|
|
|
// P2SH 2-of-2 multisig
|
2019-02-19 17:00:45 -05:00
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(scriptMulti)), output1, input1, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, false);
|
2019-02-19 17:00:45 -05:00
|
|
|
CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(scriptMulti)), output2, input2, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output2, input2, 0, true);
|
|
|
|
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, false);
|
2016-11-11 16:23:17 -08:00
|
|
|
BOOST_CHECK(*output1 == *output2);
|
2018-06-08 14:39:36 -07:00
|
|
|
UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
|
|
|
|
// Witness 2-of-2 multisig
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore, destination_script_multi, output1, input1, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, 0, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore2, destination_script_multi, output2, input2, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output2, input2, 0, true);
|
|
|
|
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
|
2016-11-11 16:23:17 -08:00
|
|
|
BOOST_CHECK(*output1 == *output2);
|
2018-06-08 14:39:36 -07:00
|
|
|
UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
|
|
|
|
// P2SH witness 2-of-2 multisig
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore, GetScriptForDestination(ScriptHash(destination_script_multi)), output1, input1, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
|
2020-07-02 11:47:18 +12:00
|
|
|
CreateCreditAndSpend(keystore2, GetScriptForDestination(ScriptHash(destination_script_multi)), output2, input2, false);
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH, true);
|
|
|
|
CheckWithFlag(output2, input2, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, false);
|
2016-11-11 16:23:17 -08:00
|
|
|
BOOST_CHECK(*output1 == *output2);
|
2018-06-08 14:39:36 -07:00
|
|
|
UpdateInput(input1.vin[0], CombineSignatures(input1, input2, output1));
|
2015-11-20 16:22:47 +01:00
|
|
|
CheckWithFlag(output1, input1, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_WITNESS, true);
|
|
|
|
CheckWithFlag(output1, input1, STANDARD_SCRIPT_VERIFY_FLAGS, true);
|
|
|
|
}
|
|
|
|
|
2013-04-24 18:27:00 -04:00
|
|
|
BOOST_AUTO_TEST_CASE(test_IsStandard)
|
2012-01-10 20:18:00 -05:00
|
|
|
{
|
2014-04-23 08:05:05 +02:00
|
|
|
LOCK(cs_main);
|
2019-06-06 16:33:23 +02:00
|
|
|
FillableSigningProvider keystore;
|
Ultraprune
This switches bitcoin's transaction/block verification logic to use a
"coin database", which contains all unredeemed transaction output scripts,
amounts and heights.
The name ultraprune comes from the fact that instead of a full transaction
index, we only (need to) keep an index with unspent outputs. For now, the
blocks themselves are kept as usual, although they are only necessary for
serving, rescanning and reorganizing.
The basic datastructures are CCoins (representing the coins of a single
transaction), and CCoinsView (representing a state of the coins database).
There are several implementations for CCoinsView. A dummy, one backed by
the coins database (coins.dat), one backed by the memory pool, and one
that adds a cache on top of it. FetchInputs, ConnectInputs, ConnectBlock,
DisconnectBlock, ... now operate on a generic CCoinsView.
The block switching logic now builds a single cached CCoinsView with
changes to be committed to the database before any changes are made.
This means no uncommitted changes are ever read from the database, and
should ease the transition to another database layer which does not
support transactions (but does support atomic writes), like LevelDB.
For the getrawtransaction() RPC call, access to a txid-to-disk index
would be preferable. As this index is not necessary or even useful
for any other part of the implementation, it is not provided. Instead,
getrawtransaction() uses the coin database to find the block height,
and then scans that block to find the requested transaction. This is
slow, but should suffice for debug purposes.
2012-07-01 18:54:00 +02:00
|
|
|
CCoinsView coinsDummy;
|
2014-09-24 03:19:04 +02:00
|
|
|
CCoinsViewCache coins(&coinsDummy);
|
2020-02-18 17:11:16 +01:00
|
|
|
std::vector<CMutableTransaction> dummyTransactions =
|
|
|
|
SetupDummyInputs(keystore, coins, {11*CENT, 50*CENT, 21*CENT, 22*CENT});
|
2012-01-10 20:18:00 -05:00
|
|
|
|
2014-06-07 13:53:27 +02:00
|
|
|
CMutableTransaction t;
|
2013-04-24 18:27:00 -04:00
|
|
|
t.vin.resize(1);
|
|
|
|
t.vin[0].prevout.hash = dummyTransactions[0].GetHash();
|
|
|
|
t.vin[0].prevout.n = 1;
|
|
|
|
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
|
|
|
|
t.vout.resize(1);
|
|
|
|
t.vout[0].nValue = 90*CENT;
|
|
|
|
CKey key;
|
|
|
|
key.MakeNewKey(true);
|
2019-02-19 17:00:45 -05:00
|
|
|
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
2013-04-24 18:27:00 -04:00
|
|
|
|
2021-09-02 09:42:52 +02:00
|
|
|
constexpr auto CheckIsStandard = [](const auto& t) {
|
|
|
|
std::string reason;
|
|
|
|
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
|
|
|
|
BOOST_CHECK(reason.empty());
|
|
|
|
};
|
|
|
|
constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in) {
|
|
|
|
std::string reason;
|
|
|
|
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
|
|
|
|
BOOST_CHECK_EQUAL(reason_in, reason);
|
|
|
|
};
|
|
|
|
|
|
|
|
CheckIsStandard(t);
|
2013-04-24 18:27:00 -04:00
|
|
|
|
2015-10-13 19:23:11 +02:00
|
|
|
// Check dust with default relay fee:
|
2021-09-02 09:42:52 +02:00
|
|
|
CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK() / 1000;
|
2015-10-13 19:23:11 +02:00
|
|
|
BOOST_CHECK_EQUAL(nDustThreshold, 546);
|
|
|
|
// dust:
|
|
|
|
t.vout[0].nValue = nDustThreshold - 1;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "dust");
|
2015-10-13 19:23:11 +02:00
|
|
|
// not dust:
|
|
|
|
t.vout[0].nValue = nDustThreshold;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2013-04-24 18:27:00 -04:00
|
|
|
|
2019-11-21 21:09:32 +01:00
|
|
|
// Disallowed nVersion
|
|
|
|
t.nVersion = -1;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "version");
|
2019-11-21 21:09:32 +01:00
|
|
|
|
|
|
|
t.nVersion = 0;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "version");
|
2019-11-21 21:09:32 +01:00
|
|
|
|
|
|
|
t.nVersion = 3;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "version");
|
2019-11-21 21:09:32 +01:00
|
|
|
|
|
|
|
// Allowed nVersion
|
|
|
|
t.nVersion = 1;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2019-11-21 21:09:32 +01:00
|
|
|
|
|
|
|
t.nVersion = 2;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2019-11-21 21:09:32 +01:00
|
|
|
|
2015-10-13 19:23:11 +02:00
|
|
|
// Check dust with odd relay fee to verify rounding:
|
2017-07-17 06:52:09 -04:00
|
|
|
// nDustThreshold = 182 * 3702 / 1000
|
|
|
|
dustRelayFee = CFeeRate(3702);
|
2015-10-13 19:23:11 +02:00
|
|
|
// dust:
|
2017-07-17 06:52:09 -04:00
|
|
|
t.vout[0].nValue = 673 - 1;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "dust");
|
2015-10-13 19:23:11 +02:00
|
|
|
// not dust:
|
2017-07-17 06:52:09 -04:00
|
|
|
t.vout[0].nValue = 673;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2016-12-13 16:19:17 -05:00
|
|
|
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
2013-04-24 18:27:00 -04:00
|
|
|
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_1;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "scriptpubkey");
|
2013-06-24 15:09:50 -04:00
|
|
|
|
2020-05-30 09:16:05 -04:00
|
|
|
// MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
|
2014-11-16 15:43:12 +00:00
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
|
2014-10-13 10:18:05 -04:00
|
|
|
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2013-06-24 15:09:50 -04:00
|
|
|
|
2020-05-30 09:16:05 -04:00
|
|
|
// MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
|
2014-11-16 15:43:12 +00:00
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
|
2014-10-13 10:18:05 -04:00
|
|
|
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "scriptpubkey");
|
2014-10-13 10:18:05 -04:00
|
|
|
|
|
|
|
// Data payload can be encoded in any way...
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("");
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2014-10-13 10:18:05 -04:00
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01");
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2014-10-13 10:18:05 -04:00
|
|
|
// OP_RESERVED *is* considered to be a PUSHDATA type opcode by IsPushOnly()!
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RESERVED << -1 << 0 << ParseHex("01") << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 15 << 16;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2014-10-13 10:18:05 -04:00
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2014-10-13 10:18:05 -04:00
|
|
|
|
|
|
|
// ...so long as it only contains PUSHDATA's
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "scriptpubkey");
|
2013-06-24 15:09:50 -04:00
|
|
|
|
2020-05-30 09:16:05 -04:00
|
|
|
// TxoutType::NULL_DATA w/o PUSHDATA
|
2013-10-24 04:32:35 -04:00
|
|
|
t.vout.resize(1);
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2013-10-24 04:32:35 -04:00
|
|
|
|
2020-05-30 09:16:05 -04:00
|
|
|
// Only one TxoutType::NULL_DATA permitted in all cases
|
2013-06-24 15:09:50 -04:00
|
|
|
t.vout.resize(2);
|
2014-02-26 12:58:08 -05:00
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
|
2020-12-24 13:52:34 +01:00
|
|
|
t.vout[0].nValue = 0;
|
2014-02-26 12:58:08 -05:00
|
|
|
t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
|
2020-12-24 13:52:34 +01:00
|
|
|
t.vout[1].nValue = 0;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "multi-op-return");
|
2013-10-24 04:32:35 -04:00
|
|
|
|
2014-02-26 12:58:08 -05:00
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
|
2013-10-24 04:32:35 -04:00
|
|
|
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "multi-op-return");
|
2013-10-24 04:32:35 -04:00
|
|
|
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
|
|
|
|
t.vout[1].scriptPubKey = CScript() << OP_RETURN;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "multi-op-return");
|
2019-11-14 19:32:51 +01:00
|
|
|
|
|
|
|
// Check large scriptSig (non-standard if size is >1650 bytes)
|
|
|
|
t.vout.resize(1);
|
|
|
|
t.vout[0].nValue = MAX_MONEY;
|
|
|
|
t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey()));
|
|
|
|
// OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes
|
|
|
|
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1647, 0); // 1650
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2019-11-14 19:32:51 +01:00
|
|
|
|
|
|
|
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(1648, 0); // 1651
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "scriptsig-size");
|
2019-11-18 03:16:56 +01:00
|
|
|
|
2019-12-11 09:52:57 +01:00
|
|
|
// Check scriptSig format (non-standard if there are any other ops than just PUSHs)
|
|
|
|
t.vin[0].scriptSig = CScript()
|
|
|
|
<< OP_TRUE << OP_0 << OP_1NEGATE << OP_16 // OP_n (single byte pushes: n = 1, 0, -1, 16)
|
|
|
|
<< std::vector<unsigned char>(75, 0) // OP_PUSHx [...x bytes...]
|
|
|
|
<< std::vector<unsigned char>(235, 0) // OP_PUSHDATA1 x [...x bytes...]
|
|
|
|
<< std::vector<unsigned char>(1234, 0) // OP_PUSHDATA2 x [...x bytes...]
|
|
|
|
<< OP_9;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2019-12-11 09:52:57 +01:00
|
|
|
|
|
|
|
const std::vector<unsigned char> non_push_ops = { // arbitrary set of non-push operations
|
|
|
|
OP_NOP, OP_VERIFY, OP_IF, OP_ROT, OP_3DUP, OP_SIZE, OP_EQUAL, OP_ADD, OP_SUB,
|
|
|
|
OP_HASH256, OP_CODESEPARATOR, OP_CHECKSIG, OP_CHECKLOCKTIMEVERIFY };
|
|
|
|
|
|
|
|
CScript::const_iterator pc = t.vin[0].scriptSig.begin();
|
|
|
|
while (pc < t.vin[0].scriptSig.end()) {
|
|
|
|
opcodetype opcode;
|
|
|
|
CScript::const_iterator prev_pc = pc;
|
|
|
|
t.vin[0].scriptSig.GetOp(pc, opcode); // advance to next op
|
|
|
|
// for the sake of simplicity, we only replace single-byte push operations
|
|
|
|
if (opcode >= 1 && opcode <= OP_PUSHDATA4)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int index = prev_pc - t.vin[0].scriptSig.begin();
|
|
|
|
unsigned char orig_op = *prev_pc; // save op
|
|
|
|
// replace current push-op with each non-push-op
|
|
|
|
for (auto op : non_push_ops) {
|
|
|
|
t.vin[0].scriptSig[index] = op;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "scriptsig-not-pushonly");
|
2019-12-11 09:52:57 +01:00
|
|
|
}
|
|
|
|
t.vin[0].scriptSig[index] = orig_op; // restore op
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2019-12-11 09:52:57 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 15:00:13 +01:00
|
|
|
// Check tx-size (non-standard if transaction weight is > MAX_STANDARD_TX_WEIGHT)
|
|
|
|
t.vin.clear();
|
|
|
|
t.vin.resize(2438); // size per input (empty scriptSig): 41 bytes
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(19, 0); // output size: 30 bytes
|
|
|
|
// tx header: 12 bytes => 48 vbytes
|
|
|
|
// 2438 inputs: 2438*41 = 99958 bytes => 399832 vbytes
|
|
|
|
// 1 output: 30 bytes => 120 vbytes
|
|
|
|
// ===============================
|
|
|
|
// total: 400000 vbytes
|
|
|
|
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400000);
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2020-01-16 15:00:13 +01:00
|
|
|
|
|
|
|
// increase output size by one byte, so we end up with 400004 vbytes
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_RETURN << std::vector<unsigned char>(20, 0); // output size: 31 bytes
|
|
|
|
BOOST_CHECK_EQUAL(GetTransactionWeight(CTransaction(t)), 400004);
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "tx-size");
|
2020-01-16 15:00:13 +01:00
|
|
|
|
2019-11-18 03:16:56 +01:00
|
|
|
// Check bare multisig (standard if policy flag fIsBareMultisigStd is set)
|
|
|
|
fIsBareMultisigStd = true;
|
|
|
|
t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); // simple 1-of-1
|
2020-01-16 15:00:13 +01:00
|
|
|
t.vin.resize(1);
|
2019-11-18 03:16:56 +01:00
|
|
|
t.vin[0].scriptSig = CScript() << std::vector<unsigned char>(65, 0);
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2019-11-18 03:16:56 +01:00
|
|
|
|
|
|
|
fIsBareMultisigStd = false;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "bare-multisig");
|
2020-01-29 13:11:58 +08:00
|
|
|
fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG;
|
2021-08-24 09:15:01 +02:00
|
|
|
|
|
|
|
// Check P2WPKH outputs dust threshold
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_0 << ParseHex("ffffffffffffffffffffffffffffffffffffffff");
|
|
|
|
t.vout[0].nValue = 294;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2021-08-24 09:15:01 +02:00
|
|
|
t.vout[0].nValue = 293;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "dust");
|
2021-08-24 09:15:01 +02:00
|
|
|
|
|
|
|
// Check P2WSH outputs dust threshold
|
|
|
|
t.vout[0].scriptPubKey = CScript() << OP_0 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
|
|
|
t.vout[0].nValue = 330;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2021-08-24 09:15:01 +02:00
|
|
|
t.vout[0].nValue = 329;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "dust");
|
2021-08-24 09:15:01 +02:00
|
|
|
|
|
|
|
// Check future Witness Program versions dust threshold
|
|
|
|
for (int op = OP_2; op <= OP_16; op += 1) {
|
|
|
|
t.vout[0].scriptPubKey = CScript() << (opcodetype)op << ParseHex("ffff");
|
|
|
|
t.vout[0].nValue = 240;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsStandard(t);
|
2021-08-24 09:15:01 +02:00
|
|
|
|
|
|
|
t.vout[0].nValue = 239;
|
2021-09-02 09:42:52 +02:00
|
|
|
CheckIsNotStandard(t, "dust");
|
2021-08-24 09:15:01 +02:00
|
|
|
}
|
2012-01-10 20:18:00 -05:00
|
|
|
}
|
|
|
|
|
2011-07-31 20:07:53 +02:00
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|