aboutsummaryrefslogtreecommitdiff
path: root/src/test/transaction_tests.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/transaction_tests.cpp')
-rw-r--r--src/test/transaction_tests.cpp202
1 files changed, 132 insertions, 70 deletions
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index 03919e7c7..fb0df1aff 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -1,46 +1,59 @@
// Copyright (c) 2011-2014 The Bitcoin Core developers
-// Distributed under the MIT/X11 software license, see the accompanying
+// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "data/tx_invalid.json.h"
#include "data/tx_valid.json.h"
+#include "test/test_bitcoin.h"
+#include "clientversion.h"
+#include "consensus/validation.h"
+#include "core_io.h"
#include "key.h"
#include "keystore.h"
-#include "main.h"
-#include "script.h"
-#include "core_io.h"
+#include "main.h" // For CheckTransaction
+#include "policy/policy.h"
+#include "script/script.h"
+#include "script/script_error.h"
+#include "utilstrencodings.h"
#include <map>
#include <string>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
+#include <boost/assign/list_of.hpp>
#include <boost/test/unit_test.hpp>
-#include "json/json_spirit_writer_template.h"
+#include <boost/assign/list_of.hpp>
+
+#include <univalue.h>
using namespace std;
-using namespace json_spirit;
-using namespace boost::algorithm;
// In script_tests.cpp
-extern Array read_json(const std::string& jsondata);
-
-unsigned int ParseFlags(string strFlags){
+extern UniValue read_json(const std::string& jsondata);
+
+static std::map<string, unsigned int> mapFlagNames = boost::assign::map_list_of
+ (string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE)
+ (string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH)
+ (string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC)
+ (string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG)
+ (string("LOW_S"), (unsigned int)SCRIPT_VERIFY_LOW_S)
+ (string("SIGPUSHONLY"), (unsigned int)SCRIPT_VERIFY_SIGPUSHONLY)
+ (string("MINIMALDATA"), (unsigned int)SCRIPT_VERIFY_MINIMALDATA)
+ (string("NULLDUMMY"), (unsigned int)SCRIPT_VERIFY_NULLDUMMY)
+ (string("DISCOURAGE_UPGRADABLE_NOPS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS)
+ (string("CLEANSTACK"), (unsigned int)SCRIPT_VERIFY_CLEANSTACK)
+ (string("CHECKLOCKTIMEVERIFY"), (unsigned int)SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY);
+
+unsigned int ParseScriptFlags(string strFlags)
+{
+ if (strFlags.empty()) {
+ return 0;
+ }
unsigned int flags = 0;
vector<string> words;
- split(words, strFlags, is_any_of(","));
-
- // Note how NOCACHE is not included as it is a runtime-only flag.
- static map<string, unsigned int> mapFlagNames;
- if (mapFlagNames.size() == 0)
- {
- mapFlagNames["NONE"] = SCRIPT_VERIFY_NONE;
- mapFlagNames["P2SH"] = SCRIPT_VERIFY_P2SH;
- mapFlagNames["STRICTENC"] = SCRIPT_VERIFY_STRICTENC;
- mapFlagNames["LOW_S"] = SCRIPT_VERIFY_LOW_S;
- mapFlagNames["NULLDUMMY"] = SCRIPT_VERIFY_NULLDUMMY;
- }
+ boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
BOOST_FOREACH(string word, words)
{
@@ -52,7 +65,23 @@ unsigned int ParseFlags(string strFlags){
return flags;
}
-BOOST_AUTO_TEST_SUITE(transaction_tests)
+string FormatScriptFlags(unsigned int flags)
+{
+ if (flags == 0) {
+ return "";
+ }
+ string ret;
+ std::map<string, unsigned int>::const_iterator it = mapFlagNames.begin();
+ while (it != mapFlagNames.end()) {
+ if (flags & it->second) {
+ ret += it->first + ",";
+ }
+ it++;
+ }
+ return ret.substr(0, ret.size() - 1);
+}
+
+BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(tx_valid)
{
@@ -63,38 +92,38 @@ BOOST_AUTO_TEST_CASE(tx_valid)
// ... where all scripts are stringified scripts.
//
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
- Array tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
+ UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
- BOOST_FOREACH(Value& tv, tests)
- {
- Array test = tv.get_array();
- string strTest = write_string(tv, false);
- if (test[0].type() == array_type)
+ ScriptError err;
+ for (unsigned int idx = 0; idx < tests.size(); idx++) {
+ UniValue test = tests[idx];
+ string strTest = test.write();
+ if (test[0].isArray())
{
- if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type)
+ if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
map<COutPoint, CScript> mapprevOutScriptPubKeys;
- Array inputs = test[0].get_array();
+ UniValue inputs = test[0].get_array();
bool fValid = true;
- BOOST_FOREACH(Value& input, inputs)
- {
- if (input.type() != array_type)
+ for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
+ const UniValue& input = inputs[inpIdx];
+ if (!input.isArray())
{
fValid = false;
break;
}
- Array vinput = input.get_array();
+ UniValue vinput = input.get_array();
if (vinput.size() != 3)
{
fValid = false;
break;
}
- mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
+ mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
}
if (!fValid)
{
@@ -119,10 +148,11 @@ BOOST_AUTO_TEST_CASE(tx_valid)
break;
}
- unsigned int verify_flags = ParseFlags(test[2].get_str());
+ unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
- tx, i, verify_flags, 0),
+ verify_flags, TransactionSignatureChecker(&tx, i), &err),
strTest);
+ BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
}
}
}
@@ -137,38 +167,38 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
// ... where all scripts are stringified scripts.
//
// verifyFlags is a comma separated list of script verification flags to apply, or "NONE"
- Array tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
+ UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
- BOOST_FOREACH(Value& tv, tests)
- {
- Array test = tv.get_array();
- string strTest = write_string(tv, false);
- if (test[0].type() == array_type)
+ ScriptError err;
+ for (unsigned int idx = 0; idx < tests.size(); idx++) {
+ UniValue test = tests[idx];
+ string strTest = test.write();
+ if (test[0].isArray())
{
- if (test.size() != 3 || test[1].type() != str_type || test[2].type() != str_type)
+ if (test.size() != 3 || !test[1].isStr() || !test[2].isStr())
{
BOOST_ERROR("Bad test: " << strTest);
continue;
}
map<COutPoint, CScript> mapprevOutScriptPubKeys;
- Array inputs = test[0].get_array();
+ UniValue inputs = test[0].get_array();
bool fValid = true;
- BOOST_FOREACH(Value& input, inputs)
- {
- if (input.type() != array_type)
+ for (unsigned int inpIdx = 0; inpIdx < inputs.size(); inpIdx++) {
+ const UniValue& input = inputs[inpIdx];
+ if (!input.isArray())
{
fValid = false;
break;
}
- Array vinput = input.get_array();
+ UniValue vinput = input.get_array();
if (vinput.size() != 3)
{
fValid = false;
break;
}
- mapprevOutScriptPubKeys[COutPoint(uint256(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
+ mapprevOutScriptPubKeys[COutPoint(uint256S(vinput[0].get_str()), vinput[1].get_int())] = ParseScript(vinput[2].get_str());
}
if (!fValid)
{
@@ -192,12 +222,12 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
break;
}
- unsigned int verify_flags = ParseFlags(test[2].get_str());
+ unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
- tx, i, verify_flags, 0);
+ verify_flags, TransactionSignatureChecker(&tx, i), &err);
}
-
BOOST_CHECK_MESSAGE(!fValid, strTest);
+ BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
}
}
}
@@ -225,7 +255,7 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
// paid to a TX_PUBKEYHASH.
//
static std::vector<CMutableTransaction>
-SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet)
+SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsViewCache& coinsRet)
{
std::vector<CMutableTransaction> dummyTransactions;
dummyTransactions.resize(2);
@@ -241,17 +271,17 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, CCoinsView & coinsRet)
// Create some dummy input transactions
dummyTransactions[0].vout.resize(2);
dummyTransactions[0].vout[0].nValue = 11*CENT;
- dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG;
+ dummyTransactions[0].vout[0].scriptPubKey << ToByteVector(key[0].GetPubKey()) << OP_CHECKSIG;
dummyTransactions[0].vout[1].nValue = 50*CENT;
- dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG;
- coinsRet.SetCoins(dummyTransactions[0].GetHash(), CCoins(dummyTransactions[0], 0));
+ dummyTransactions[0].vout[1].scriptPubKey << ToByteVector(key[1].GetPubKey()) << OP_CHECKSIG;
+ coinsRet.ModifyCoins(dummyTransactions[0].GetHash())->FromTx(dummyTransactions[0], 0);
dummyTransactions[1].vout.resize(2);
dummyTransactions[1].vout[0].nValue = 21*CENT;
- dummyTransactions[1].vout[0].scriptPubKey.SetDestination(key[2].GetPubKey().GetID());
+ dummyTransactions[1].vout[0].scriptPubKey = GetScriptForDestination(key[2].GetPubKey().GetID());
dummyTransactions[1].vout[1].nValue = 22*CENT;
- dummyTransactions[1].vout[1].scriptPubKey.SetDestination(key[3].GetPubKey().GetID());
- coinsRet.SetCoins(dummyTransactions[1].GetHash(), CCoins(dummyTransactions[1], 0));
+ dummyTransactions[1].vout[1].scriptPubKey = GetScriptForDestination(key[3].GetPubKey().GetID());
+ coinsRet.ModifyCoins(dummyTransactions[1].GetHash())->FromTx(dummyTransactions[1], 0);
return dummyTransactions;
}
@@ -260,7 +290,7 @@ BOOST_AUTO_TEST_CASE(test_Get)
{
CBasicKeyStore keystore;
CCoinsView coinsDummy;
- CCoinsViewCache coins(coinsDummy);
+ CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
CMutableTransaction t1;
@@ -295,7 +325,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
LOCK(cs_main);
CBasicKeyStore keystore;
CCoinsView coinsDummy;
- CCoinsViewCache coins(coinsDummy);
+ CCoinsViewCache coins(&coinsDummy);
std::vector<CMutableTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
CMutableTransaction t;
@@ -307,26 +337,58 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = 90*CENT;
CKey key;
key.MakeNewKey(true);
- t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
+ t.vout[0].scriptPubKey = GetScriptForDestination(key.GetPubKey().GetID());
string reason;
BOOST_CHECK(IsStandardTx(t, reason));
- t.vout[0].nValue = 501; // dust
+ // Check dust with default relay fee:
+ CAmount nDustThreshold = 182 * minRelayTxFee.GetFeePerK()/1000 * 3;
+ BOOST_CHECK_EQUAL(nDustThreshold, 546);
+ // dust:
+ t.vout[0].nValue = nDustThreshold - 1;
BOOST_CHECK(!IsStandardTx(t, reason));
+ // not dust:
+ t.vout[0].nValue = nDustThreshold;
+ BOOST_CHECK(IsStandardTx(t, reason));
- t.vout[0].nValue = 601; // not dust
+ // Check dust with odd relay fee to verify rounding:
+ // nDustThreshold = 182 * 1234 / 1000 * 3
+ minRelayTxFee = CFeeRate(1234);
+ // dust:
+ t.vout[0].nValue = 672 - 1;
+ BOOST_CHECK(!IsStandardTx(t, reason));
+ // not dust:
+ t.vout[0].nValue = 672;
BOOST_CHECK(IsStandardTx(t, reason));
+ minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
t.vout[0].scriptPubKey = CScript() << OP_1;
BOOST_CHECK(!IsStandardTx(t, reason));
- // 40-byte TX_NULL_DATA (standard)
- t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
+ // MAX_OP_RETURN_RELAY-byte TX_NULL_DATA (standard)
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
+ BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
+ BOOST_CHECK(IsStandardTx(t, reason));
+
+ // MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard)
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
+ BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
+ BOOST_CHECK(!IsStandardTx(t, reason));
+
+ // Data payload can be encoded in any way...
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("");
+ BOOST_CHECK(IsStandardTx(t, reason));
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01");
+ BOOST_CHECK(IsStandardTx(t, reason));
+ // 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;
+ BOOST_CHECK(IsStandardTx(t, reason));
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
BOOST_CHECK(IsStandardTx(t, reason));
- // 41-byte TX_NULL_DATA (non-standard)
- t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
+ // ...so long as it only contains PUSHDATA's
+ t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN;
BOOST_CHECK(!IsStandardTx(t, reason));
// TX_NULL_DATA w/o PUSHDATA