Changeset View
Changeset View
Standalone View
Standalone View
src/test/checkdatasig_tests.cpp
Show All 13 Lines | |||||
typedef std::vector<uint8_t> valtype; | typedef std::vector<uint8_t> valtype; | ||||
typedef std::vector<valtype> stacktype; | typedef std::vector<valtype> stacktype; | ||||
BOOST_FIXTURE_TEST_SUITE(checkdatasig_tests, BasicTestingSetup) | BOOST_FIXTURE_TEST_SUITE(checkdatasig_tests, BasicTestingSetup) | ||||
std::array<uint32_t, 3> flagset{ | std::array<uint32_t, 3> flagset{ | ||||
{0, STANDARD_SCRIPT_VERIFY_FLAGS, MANDATORY_SCRIPT_VERIFY_FLAGS}}; | {0, STANDARD_SCRIPT_VERIFY_FLAGS, MANDATORY_SCRIPT_VERIFY_FLAGS}}; | ||||
const uint8_t vchPrivkey[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; | |||||
struct KeyData { | |||||
CKey privkey, privkeyC; | |||||
CPubKey pubkey, pubkeyC, pubkeyH; | |||||
KeyData() { | |||||
privkey.Set(vchPrivkey, vchPrivkey + 32, false); | |||||
privkeyC.Set(vchPrivkey, vchPrivkey + 32, true); | |||||
pubkey = privkey.GetPubKey(); | |||||
pubkeyH = privkey.GetPubKey(); | |||||
pubkeyC = privkeyC.GetPubKey(); | |||||
*const_cast<uint8_t *>(&pubkeyH[0]) = 0x06 | (pubkeyH[64] & 1); | |||||
} | |||||
}; | |||||
static void CheckError(uint32_t flags, const stacktype &original_stack, | |||||
const CScript &script, ScriptError expected) { | |||||
BaseSignatureChecker sigchecker; | |||||
ScriptError err = SCRIPT_ERR_OK; | |||||
stacktype stack{original_stack}; | |||||
bool r = EvalScript(stack, script, flags, sigchecker, &err); | |||||
BOOST_CHECK(!r); | |||||
BOOST_CHECK_EQUAL(err, expected); | |||||
} | |||||
static void CheckPass(uint32_t flags, const stacktype &original_stack, | |||||
const CScript &script, const stacktype &expected) { | |||||
BaseSignatureChecker sigchecker; | |||||
ScriptError err = SCRIPT_ERR_OK; | |||||
stacktype stack{original_stack}; | |||||
bool r = EvalScript(stack, script, flags, sigchecker, &err); | |||||
BOOST_CHECK(r); | |||||
BOOST_CHECK_EQUAL(err, SCRIPT_ERR_OK); | |||||
BOOST_CHECK(stack == expected); | |||||
} | |||||
/** | /** | ||||
* General utility functions to check for script passing/failing. | * General utility functions to check for script passing/failing. | ||||
*/ | */ | ||||
static void CheckTestResultForAllFlags(const stacktype &original_stack, | static void CheckTestResultForAllFlags(const stacktype &original_stack, | ||||
const CScript &script, | const CScript &script, | ||||
const stacktype &expected) { | const stacktype &expected) { | ||||
BaseSignatureChecker sigchecker; | |||||
for (uint32_t flags : flagset) { | for (uint32_t flags : flagset) { | ||||
// The opcode are not implemented yet, so we get a bad opcode error when | // Make sure that we get a bad opcode when the activation flag is not | ||||
// passing the activation flag. | // passed. | ||||
ScriptError err = SCRIPT_ERR_OK; | CheckError(flags, original_stack, script, SCRIPT_ERR_BAD_OPCODE); | ||||
stacktype stack{original_stack}; | |||||
bool r = EvalScript(stack, script, flags | SCRIPT_ENABLE_CHECKDATASIG, | |||||
sigchecker, &err); | |||||
BOOST_CHECK(!r); | |||||
BOOST_CHECK_EQUAL(err, SCRIPT_ERR_BAD_OPCODE); | |||||
// The script execute as expected if the opcodes are activated. | |||||
CheckPass(flags | SCRIPT_ENABLE_CHECKDATASIG, original_stack, script, | |||||
expected); | |||||
} | |||||
} | |||||
static void CheckErrorForAllFlags(const stacktype &original_stack, | |||||
const CScript &script, ScriptError expected) { | |||||
for (uint32_t flags : flagset) { | |||||
// Make sure that we get a bad opcode when the activation flag is not | // Make sure that we get a bad opcode when the activation flag is not | ||||
// passed. | // passed. | ||||
stack = original_stack; | CheckError(flags, original_stack, script, SCRIPT_ERR_BAD_OPCODE); | ||||
r = EvalScript(stack, script, flags, sigchecker, &err); | |||||
BOOST_CHECK(!r); | // The script generates the proper error if the opcodes are activated. | ||||
BOOST_CHECK_EQUAL(err, SCRIPT_ERR_BAD_OPCODE); | CheckError(flags | SCRIPT_ENABLE_CHECKDATASIG, original_stack, script, | ||||
expected); | |||||
} | } | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(checkdatasig_test) { | BOOST_AUTO_TEST_CASE(checkdatasig_test) { | ||||
CheckTestResultForAllFlags({}, CScript() << OP_CHECKDATASIG, {}); | // Empty stack. | ||||
CheckTestResultForAllFlags({}, CScript() << OP_CHECKDATASIGVERIFY, {}); | CheckErrorForAllFlags({}, CScript() << OP_CHECKDATASIG, | ||||
SCRIPT_ERR_INVALID_STACK_OPERATION); | |||||
CheckErrorForAllFlags({{0x00}}, CScript() << OP_CHECKDATASIG, | |||||
SCRIPT_ERR_INVALID_STACK_OPERATION); | |||||
CheckErrorForAllFlags({{0x00}, {0x00}}, CScript() << OP_CHECKDATASIG, | |||||
SCRIPT_ERR_INVALID_STACK_OPERATION); | |||||
CheckErrorForAllFlags({}, CScript() << OP_CHECKDATASIGVERIFY, | |||||
SCRIPT_ERR_INVALID_STACK_OPERATION); | |||||
CheckErrorForAllFlags({{0x00}}, CScript() << OP_CHECKDATASIGVERIFY, | |||||
SCRIPT_ERR_INVALID_STACK_OPERATION); | |||||
CheckErrorForAllFlags({{0x00}, {0x00}}, CScript() << OP_CHECKDATASIGVERIFY, | |||||
SCRIPT_ERR_INVALID_STACK_OPERATION); | |||||
// Check invalid message sizes. | |||||
for (valtype message; message.size() < 42; message.push_back(0x00)) { | |||||
if (message.size() == 32) { | |||||
// 32 is the epxected size, skip. | |||||
jasonbcox: epxected -> expected | |||||
continue; | |||||
} | |||||
CheckErrorForAllFlags({{0x00}, message, {0x00}}, | |||||
CScript() << OP_CHECKDATASIG, | |||||
SCRIPT_ERR_INVALID_OPERAND_SIZE); | |||||
CheckErrorForAllFlags({{0x00}, message, {0x00}}, | |||||
CScript() << OP_CHECKDATASIGVERIFY, | |||||
SCRIPT_ERR_INVALID_OPERAND_SIZE); | |||||
} | |||||
// Check various pubkey encoding. | |||||
const valtype message(32, 0x00); | |||||
KeyData kd; | |||||
valtype pubkey = ToByteVector(kd.pubkey); | |||||
valtype pubkeyC = ToByteVector(kd.pubkeyC); | |||||
valtype pubkeyH = ToByteVector(kd.pubkeyH); | |||||
CheckTestResultForAllFlags({{}, message, pubkey}, | |||||
CScript() << OP_CHECKDATASIG, {{}}); | |||||
CheckTestResultForAllFlags({{}, message, pubkeyC}, | |||||
CScript() << OP_CHECKDATASIG, {{}}); | |||||
CheckErrorForAllFlags({{}, message, pubkey}, | |||||
CScript() << OP_CHECKDATASIGVERIFY, | |||||
SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
CheckErrorForAllFlags({{}, message, pubkeyC}, | |||||
CScript() << OP_CHECKDATASIGVERIFY, | |||||
SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
// Flags dependent checks. | |||||
const CScript script = CScript() << OP_CHECKDATASIG << OP_NOT << OP_VERIFY; | |||||
const CScript scriptverify = CScript() << OP_CHECKDATASIGVERIFY; | |||||
// Check valid signatures. | |||||
const uint256 one(uint256S( | |||||
"0000000000000000000000000000000000000000000000000000000000000001")); | |||||
const uint256 two(uint256S( | |||||
"0000000000000000000000000000000000000000000000000000000000000002")); | |||||
valtype validsig; | |||||
kd.privkey.Sign(one, validsig); | |||||
CheckTestResultForAllFlags({validsig, ToByteVector(one), pubkey}, | |||||
CScript() << OP_CHECKDATASIG, {{0x01}}); | |||||
CheckTestResultForAllFlags({validsig, ToByteVector(one), pubkey}, | |||||
CScript() << OP_CHECKDATASIGVERIFY, {}); | |||||
const valtype minimalsig{0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; | |||||
const valtype nondersig{0x30, 0x80, 0x06, 0x02, 0x01, | |||||
0x01, 0x02, 0x01, 0x01}; | |||||
const valtype highSSig{ | |||||
0x30, 0x45, 0x02, 0x20, 0x3e, 0x45, 0x16, 0xda, 0x72, 0x53, 0xcf, 0x06, | |||||
0x8e, 0xff, 0xec, 0x6b, 0x95, 0xc4, 0x12, 0x21, 0xc0, 0xcf, 0x3a, 0x8e, | |||||
0x6c, 0xcb, 0x8c, 0xbf, 0x17, 0x25, 0xb5, 0x62, 0xe9, 0xaf, 0xde, 0x2c, | |||||
0x02, 0x21, 0x00, 0xab, 0x1e, 0x3d, 0xa7, 0x3d, 0x67, 0xe3, 0x20, 0x45, | |||||
0xa2, 0x0e, 0x0b, 0x99, 0x9e, 0x04, 0x99, 0x78, 0xea, 0x8d, 0x6e, 0xe5, | |||||
0x48, 0x0d, 0x48, 0x5f, 0xcf, 0x2c, 0xe0, 0xd0, 0x3b, 0x2e, 0xf0}; | |||||
// If we add many more flags, this loop can get too expensive, but we can | |||||
// rewrite in the future to randomly pick a set of flags to evaluate. | |||||
for (uint32_t flags = 0; flags < (1U << 17); flags++) { | |||||
// Make sure we activate the opcodes. | |||||
flags |= SCRIPT_ENABLE_CHECKDATASIG; | |||||
if (flags & SCRIPT_VERIFY_STRICTENC) { | |||||
// When strict encoding is enforced, hybrid key are invalid. | |||||
CheckError(flags, {{}, message, pubkeyH}, script, | |||||
SCRIPT_ERR_PUBKEYTYPE); | |||||
CheckError(flags, {{}, message, pubkeyH}, scriptverify, | |||||
SCRIPT_ERR_PUBKEYTYPE); | |||||
} else { | |||||
// When strict encoding is not enforced, hybrid key are valid. | |||||
CheckPass(flags, {{}, message, pubkeyH}, script, {}); | |||||
CheckError(flags, {{}, message, pubkeyH}, scriptverify, | |||||
SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
} | |||||
if (flags & SCRIPT_VERIFY_NULLFAIL) { | |||||
// When strict encoding is enforced, hybrid key are invalid. | |||||
CheckError(flags, {minimalsig, message, pubkey}, script, | |||||
SCRIPT_ERR_SIG_NULLFAIL); | |||||
CheckError(flags, {minimalsig, message, pubkey}, scriptverify, | |||||
SCRIPT_ERR_SIG_NULLFAIL); | |||||
// Invalid message cause checkdatasig to fail. | |||||
CheckError(flags, {validsig, ToByteVector(two), pubkey}, script, | |||||
SCRIPT_ERR_SIG_NULLFAIL); | |||||
CheckError(flags, {validsig, ToByteVector(two), pubkey}, | |||||
scriptverify, SCRIPT_ERR_SIG_NULLFAIL); | |||||
} else { | |||||
// When nullfail is not enforced, invalid signature are just false. | |||||
CheckPass(flags, {minimalsig, message, pubkey}, script, {}); | |||||
CheckError(flags, {minimalsig, message, pubkey}, scriptverify, | |||||
SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
// Invalid message cause checkdatasig to fail. | |||||
CheckPass(flags, {validsig, ToByteVector(two), pubkey}, script, {}); | |||||
CheckError(flags, {validsig, ToByteVector(two), pubkey}, | |||||
scriptverify, SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
} | |||||
if (flags & SCRIPT_VERIFY_LOW_S) { | |||||
// If we do enforce low S, then high S sigs are rejected. | |||||
CheckError(flags, {highSSig, message, pubkey}, script, | |||||
SCRIPT_ERR_SIG_HIGH_S); | |||||
CheckError(flags, {highSSig, message, pubkey}, scriptverify, | |||||
SCRIPT_ERR_SIG_HIGH_S); | |||||
} else { | |||||
// If we do not enforce low S, then high S sigs are accepted. | |||||
CheckPass(flags, {highSSig, message, pubkey}, script, {}); | |||||
CheckError(flags, {highSSig, message, pubkey}, scriptverify, | |||||
SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
} | |||||
if (flags & (SCRIPT_VERIFY_DERSIG | SCRIPT_VERIFY_LOW_S | | |||||
SCRIPT_VERIFY_STRICTENC)) { | |||||
// If we get any of the dersig flags, the non canonical dersig | |||||
// signature fails. | |||||
CheckError(flags, {nondersig, message, pubkey}, script, | |||||
SCRIPT_ERR_SIG_DER); | |||||
CheckError(flags, {nondersig, message, pubkey}, scriptverify, | |||||
SCRIPT_ERR_SIG_DER); | |||||
} else { | |||||
// If we do not check, then it is accepted. | |||||
CheckPass(flags, {nondersig, message, pubkey}, script, {}); | |||||
CheckError(flags, {nondersig, message, pubkey}, scriptverify, | |||||
SCRIPT_ERR_CHECKDATASIGVERIFY); | |||||
} | |||||
} | |||||
} | } | ||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |
epxected -> expected