New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Data-driven script evaluation unit tests. #1121

Merged
merged 1 commit into from Apr 21, 2012
Jump to file or symbol
Failed to load files and symbols.
+253 −0
Diff settings

Always

Just for now

@@ -0,0 +1,25 @@
[
["", ""],
["", "NOP"],
["NOP", ""],
["NOP","NOP"],
["0 1","EQUAL"],
["1 1 ADD", "0 EQUAL"],
["11 1 ADD 12 SUB", "11 EQUAL"],
["2147483648 0 ADD", "NOP", "arithmetic operands must be in range [-2^31...2^31] "],
["-2147483648 0 ADD", "NOP", "arithmetic operands must be in range [-2^31...2^31] "],
["2147483647 DUP ADD", "4294967294 NUMEQUAL", "NUMEQUAL must be in numeric range"],
["0xaabbccddeeff NOT", "0 EQUAL", "NOT is an arithmetic operand"],
["2 DUP MUL", "4 EQUAL", "disabled"],
["2 DUP DIV", "1 EQUAL", "disabled"],
["2 2MUL", "4 EQUAL", "disabled"],
["2 2DIV", "1 EQUAL", "disabled"],
["7 3 MOD", "1 EQUAL", "disabled"],
["2 2 LSHIFT", "8 EQUAL", "disabled"],
["2 1 RSHIFT", "1 EQUAL", "disabled"],
["NOP1","NOP10"]
]
@@ -0,0 +1,78 @@
[
["2 -2 ADD", "0 EQUAL"],
["2147483647 -2147483647 ADD", "0 EQUAL"],
["-1 -1 ADD", "-2 EQUAL"],
["1","NOP"],
["0 0","EQUAL"],
["1 1 ADD", "2 EQUAL"],
["1 1ADD", "2 EQUAL"],
["111 1SUB", "110 EQUAL"],
["111 1 ADD 12 SUB", "100 EQUAL"],
["0 ABS", "0 EQUAL"],
["16 ABS", "16 EQUAL"],
["-16 ABS", "-16 NEGATE EQUAL"],
["0 NOT", "NOP"],
["1 NOT", "0 EQUAL"],
["11 NOT", "0 EQUAL"],
["0 0NOTEQUAL", "0 EQUAL"],
["1 0NOTEQUAL", "1 EQUAL"],
["111 0NOTEQUAL", "1 EQUAL"],
["-111 0NOTEQUAL", "1 EQUAL"],
["1 1 BOOLAND", "NOP"],
["1 0 BOOLAND", "NOT"],
["0 1 BOOLAND", "NOT"],
["0 0 BOOLAND", "NOT"],
["16 17 BOOLAND", "NOP"],
["1 1 BOOLOR", "NOP"],
["1 0 BOOLOR", "NOP"],
["0 1 BOOLOR", "NOP"],
["0 0 BOOLOR", "NOT"],
["16 17 BOOLOR", "NOP"],
["11 10 1 ADD", "NUMEQUAL"],
["11 10 1 ADD", "NUMEQUALVERIFY 1"],
["11 10 1 ADD", "NUMNOTEQUAL NOT"],
["111 10 1 ADD", "NUMNOTEQUAL"],
["11 10", "LESSTHAN NOT"],
["4 4", "LESSTHAN NOT"],
["10 11", "LESSTHAN"],
["-11 11", "LESSTHAN"],
["-11 -10", "LESSTHAN"],
["11 10", "GREATERTHAN"],
["4 4", "GREATERTHAN NOT"],
["10 11", "GREATERTHAN NOT"],
["-11 11", "GREATERTHAN NOT"],
["-11 -10", "GREATERTHAN NOT"],
["11 10", "LESSTHANOREQUAL NOT"],
["4 4", "LESSTHANOREQUAL"],
["10 11", "LESSTHANOREQUAL"],
["-11 11", "LESSTHANOREQUAL"],
["-11 -10", "LESSTHANOREQUAL"],
["11 10", "GREATERTHANOREQUAL"],
["4 4", "GREATERTHANOREQUAL"],
["10 11", "GREATERTHANOREQUAL NOT"],
["-11 11", "GREATERTHANOREQUAL NOT"],
["-11 -10", "GREATERTHANOREQUAL NOT"],
["1 0 MIN", "0 NUMEQUAL"],
["0 1 MIN", "0 NUMEQUAL"],
["-1 0 MIN", "-1 NUMEQUAL"],
["0 -2147483647 MIN", "-2147483647 NUMEQUAL"],
["2147483647 0 MAX", "2147483647 NUMEQUAL"],
["0 100 MAX", "100 NUMEQUAL"],
["-100 0 MAX", "0 NUMEQUAL"],
["0 -2147483647 MAX", "0 NUMEQUAL"],
["0 0 1", "WITHIN"],
["1 0 1", "WITHIN NOT"],
["0 -2147483647 2147483647", "WITHIN"],
["-1 -100 100", "WITHIN"],
["11 -100 100", "WITHIN"],
["-2147483647 -100 100", "WITHIN NOT"],
["2147483647 -100 100", "WITHIN NOT"],
["2147483647 2147483647 SUB", "0 EQUAL"],
["2147483647 DUP ADD", "4294967294 EQUAL", ">32 bit EQUAL is valid"],
["2147483647 NEGATE DUP ADD", "-4294967294 EQUAL"],
["NOP","1"]
]
View
@@ -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
ProTip! Use n and p to navigate between commits in a pull request.