mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-11 12:22:39 -03:00
[refactor] use CheckTxScripts, TrimFlags, FillFlags
Co-authored-by: Johnson Lau <jl2012@xbt.hk>
This commit is contained in:
parent
7a77727b2f
commit
a7098a2a8d
1 changed files with 78 additions and 42 deletions
|
@ -40,7 +40,6 @@ typedef std::vector<unsigned char> valtype;
|
|||
extern UniValue read_json(const std::string& jsondata);
|
||||
|
||||
static std::map<std::string, unsigned int> mapFlagNames = {
|
||||
{std::string("NONE"), (unsigned int)SCRIPT_VERIFY_NONE},
|
||||
{std::string("P2SH"), (unsigned int)SCRIPT_VERIFY_P2SH},
|
||||
{std::string("STRICTENC"), (unsigned int)SCRIPT_VERIFY_STRICTENC},
|
||||
{std::string("DERSIG"), (unsigned int)SCRIPT_VERIFY_DERSIG},
|
||||
|
@ -63,9 +62,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
|
|||
|
||||
unsigned int ParseScriptFlags(std::string strFlags)
|
||||
{
|
||||
if (strFlags.empty()) {
|
||||
return 0;
|
||||
}
|
||||
if (strFlags.empty() | strFlags == "NONE") return 0;
|
||||
unsigned int flags = 0;
|
||||
std::vector<std::string> words;
|
||||
boost::algorithm::split(words, strFlags, boost::algorithm::is_any_of(","));
|
||||
|
@ -96,6 +93,77 @@ std::string FormatScriptFlags(unsigned int flags)
|
|||
return ret.substr(0, ret.size() - 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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),
|
||||
&input.scriptWitness, flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
|
||||
} 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;
|
||||
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;
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Return valid flags that are all except one flag for each flag
|
||||
std::vector<unsigned int> ExcludeIndividualFlags(unsigned int flags)
|
||||
{
|
||||
std::vector<unsigned int> flags_combos;
|
||||
for (unsigned int i = 0; i < mapFlagNames.size(); ++i) {
|
||||
const unsigned int flags_excluding_i = TrimFlags(flags & ~(1U << i));
|
||||
if (flags != flags_excluding_i && std::find(flags_combos.begin(), flags_combos.end(), flags_excluding_i) != flags_combos.end()) {
|
||||
flags_combos.push_back(flags_excluding_i);
|
||||
}
|
||||
}
|
||||
return flags_combos;
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(transaction_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(tx_valid)
|
||||
|
@ -103,7 +171,6 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||
// Read tests from test/data/tx_valid.json
|
||||
UniValue tests = read_json(std::string(json_tests::tx_valid, json_tests::tx_valid + sizeof(json_tests::tx_valid)));
|
||||
|
||||
ScriptError err;
|
||||
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
||||
UniValue test = tests[idx];
|
||||
std::string strTest = test.write();
|
||||
|
@ -153,24 +220,10 @@ BOOST_AUTO_TEST_CASE(tx_valid)
|
|||
BOOST_CHECK(state.IsValid());
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
{
|
||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||
{
|
||||
BOOST_ERROR("Bad test: " << strTest);
|
||||
break;
|
||||
}
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
|
||||
CAmount amount = 0;
|
||||
if (mapprevOutValues.count(tx.vin[i].prevout)) {
|
||||
amount = mapprevOutValues[tx.vin[i].prevout];
|
||||
}
|
||||
unsigned int verify_flags = ~ParseScriptFlags(test[2].get_str());
|
||||
const CScriptWitness *witness = &tx.vin[i].scriptWitness;
|
||||
BOOST_CHECK_MESSAGE(VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
||||
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err),
|
||||
strTest);
|
||||
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_OK, ScriptErrorString(err));
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, ~verify_flags, txdata, strTest, /* expect_valid */ true)) {
|
||||
BOOST_ERROR("Tx unexpectedly failed: " << strTest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,9 +234,6 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||
// Read tests from test/data/tx_invalid.json
|
||||
UniValue tests = read_json(std::string(json_tests::tx_invalid, json_tests::tx_invalid + sizeof(json_tests::tx_invalid)));
|
||||
|
||||
// Initialize to SCRIPT_ERR_OK. The tests expect err to be changed to a
|
||||
// value other than SCRIPT_ERR_OK.
|
||||
ScriptError err = SCRIPT_ERR_OK;
|
||||
for (unsigned int idx = 0; idx < tests.size(); idx++) {
|
||||
UniValue test = tests[idx];
|
||||
std::string strTest = test.write();
|
||||
|
@ -235,25 +285,11 @@ BOOST_AUTO_TEST_CASE(tx_invalid)
|
|||
}
|
||||
|
||||
PrecomputedTransactionData txdata(tx);
|
||||
for (unsigned int i = 0; i < tx.vin.size() && fValid; i++)
|
||||
{
|
||||
if (!mapprevOutScriptPubKeys.count(tx.vin[i].prevout))
|
||||
{
|
||||
BOOST_ERROR("Bad test: " << strTest);
|
||||
break;
|
||||
}
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
|
||||
unsigned int verify_flags = ParseScriptFlags(test[2].get_str());
|
||||
CAmount amount = 0;
|
||||
if (mapprevOutValues.count(tx.vin[i].prevout)) {
|
||||
amount = mapprevOutValues[tx.vin[i].prevout];
|
||||
}
|
||||
const CScriptWitness *witness = &tx.vin[i].scriptWitness;
|
||||
fValid = VerifyScript(tx.vin[i].scriptSig, mapprevOutScriptPubKeys[tx.vin[i].prevout],
|
||||
witness, verify_flags, TransactionSignatureChecker(&tx, i, amount, txdata), &err);
|
||||
if (!CheckTxScripts(tx, mapprevOutScriptPubKeys, mapprevOutValues, verify_flags, txdata, strTest, /* expect_valid */ false)) {
|
||||
BOOST_ERROR("Tx unexpectedly passed: " << strTest);
|
||||
}
|
||||
BOOST_CHECK_MESSAGE(!fValid, strTest);
|
||||
BOOST_CHECK_MESSAGE(err != SCRIPT_ERR_OK, ScriptErrorString(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue