diff options
author | Gavin Andresen <gavinandresen@gmail.com> | 2012-04-17 17:57:06 -0400 |
---|---|---|
committer | Gavin Andresen <gavinandresen@gmail.com> | 2012-04-21 19:35:39 -0400 |
commit | 8449a8788aa6efeddf635241a3c32b35f3a356c1 (patch) | |
tree | 34f13fa13061479f76b45b5ac40da012bdbee0a6 /src/test/script_tests.cpp | |
parent | 6a7a42be16e09fbbdeb7f61051aa50a2f56c6bc3 (diff) |
Data-drive script evaluation unit tests.
Diffstat (limited to 'src/test/script_tests.cpp')
-rw-r--r-- | src/test/script_tests.cpp | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 493ea69d93..988bd24de8 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1,17 +1,167 @@ +#include <iostream> +#include <fstream> #include <vector> +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/algorithm/string/split.hpp> #include <boost/test/unit_test.hpp> #include <boost/foreach.hpp> +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" #include "main.h" #include "wallet.h" using namespace std; +using namespace json_spirit; +using namespace boost::algorithm; + extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType); extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, bool fValidatePayToScriptHash, int nHashType); +CScript +ParseScript(string s) +{ + CScript result; + + static map<string, opcodetype> mapOpNames; + + if (mapOpNames.size() == 0) + { + for (int op = OP_NOP; op <= OP_NOP10; op++) + { + const char* name = GetOpName((opcodetype)op); + if (strcmp(name, "OP_UNKNOWN") == 0) + continue; + string strName(name); + mapOpNames[strName] = (opcodetype)op; + // Convenience: OP_ADD and just ADD are both recognized: + replace_first(strName, "OP_", ""); + mapOpNames[strName] = (opcodetype)op; + } + } + + vector<string> words; + split(words, s, is_any_of(" \t\n"), token_compress_on); + + BOOST_FOREACH(string w, words) + { + if (all(w, is_digit()) || + (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit()))) + { + // Number + int64 n = atoi64(w); + result << n; + } + else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end()))) + { + // Hex data: + result << ParseHex(string(w.begin()+2, w.end())); + } + else if (s.size() >= 2 && starts_with(w, "'") && ends_with(w, "'")) + { + // Single-quoted string, pushed as data: + std::vector<unsigned char> value(s.begin()+1, s.end()-1); + result << value; + } + else if (mapOpNames.count(w)) + { + // opcode, e.g. OP_ADD or OP_1: + result << mapOpNames[w]; + } + else + { + BOOST_ERROR("Parse error: " << s); + return CScript(); + } + } + + return result; +} + +Array +read_json(const std::string& filename) +{ + namespace fs = boost::filesystem; + fs::path testFile = fs::current_path() / "test" / "data" / filename; + if (!fs::exists(testFile)) + { + fs::path testFile = fs::path(__FILE__).parent_path() / "data" / filename; + } + + ifstream ifs(testFile.string().c_str(), ifstream::in); + Value v; + if (!read_stream(ifs, v)) + { + BOOST_ERROR("Cound not find/open " << filename); + return Array(); + } + if (v.type() != array_type) + { + BOOST_ERROR(filename << " does not contain a json array"); + return Array(); + } + + return v.get_array(); +} + BOOST_AUTO_TEST_SUITE(script_tests) +BOOST_AUTO_TEST_CASE(script_valid) +{ + // Read tests from test/data/script_valid.json + // Format is an array of arrays + // Inner arrays are [ "scriptSig", "scriptPubKey" ] + // ... where scriptSig and scriptPubKey are stringified + // scripts. + Array tests = read_json("script_valid.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + string strTest = write_string(tv, false); + if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + string scriptSigString = test[0].get_str(); + CScript scriptSig = ParseScript(scriptSigString); + string scriptPubKeyString = test[1].get_str(); + CScript scriptPubKey = ParseScript(scriptPubKeyString); + + CTransaction tx; + BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest); + } +} + +BOOST_AUTO_TEST_CASE(script_invalid) +{ + // Scripts that should evaluate as invalid + Array tests = read_json("script_invalid.json"); + + BOOST_FOREACH(Value& tv, tests) + { + Array test = tv.get_array(); + string strTest = write_string(tv, false); + if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments) + { + BOOST_ERROR("Bad test: " << strTest); + continue; + } + string scriptSigString = test[0].get_str(); + CScript scriptSig = ParseScript(scriptSigString); + string scriptPubKeyString = test[1].get_str(); + CScript scriptPubKey = ParseScript(scriptPubKeyString); + + CTransaction tx; + BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest); + } +} + BOOST_AUTO_TEST_CASE(script_PushData) { // Check that PUSHDATA1, PUSHDATA2, and PUSHDATA4 create the same value on |