diff --git a/src/script/script.cpp b/src/script/script.cpp index 1fcf447a9..9a15469b0 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -1,471 +1,478 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "script.h" #include "script/script_flags.h" #include "tinyformat.h" #include "utilstrencodings.h" #include const char *GetOpName(opcodetype opcode) { switch (opcode) { // push value case OP_0: return "0"; case OP_PUSHDATA1: return "OP_PUSHDATA1"; case OP_PUSHDATA2: return "OP_PUSHDATA2"; case OP_PUSHDATA4: return "OP_PUSHDATA4"; case OP_1NEGATE: return "-1"; case OP_RESERVED: return "OP_RESERVED"; case OP_1: return "1"; case OP_2: return "2"; case OP_3: return "3"; case OP_4: return "4"; case OP_5: return "5"; case OP_6: return "6"; case OP_7: return "7"; case OP_8: return "8"; case OP_9: return "9"; case OP_10: return "10"; case OP_11: return "11"; case OP_12: return "12"; case OP_13: return "13"; case OP_14: return "14"; case OP_15: return "15"; case OP_16: return "16"; // control case OP_NOP: return "OP_NOP"; case OP_VER: return "OP_VER"; case OP_IF: return "OP_IF"; case OP_NOTIF: return "OP_NOTIF"; case OP_VERIF: return "OP_VERIF"; case OP_VERNOTIF: return "OP_VERNOTIF"; case OP_ELSE: return "OP_ELSE"; case OP_ENDIF: return "OP_ENDIF"; case OP_VERIFY: return "OP_VERIFY"; case OP_RETURN: return "OP_RETURN"; // stack ops case OP_TOALTSTACK: return "OP_TOALTSTACK"; case OP_FROMALTSTACK: return "OP_FROMALTSTACK"; case OP_2DROP: return "OP_2DROP"; case OP_2DUP: return "OP_2DUP"; case OP_3DUP: return "OP_3DUP"; case OP_2OVER: return "OP_2OVER"; case OP_2ROT: return "OP_2ROT"; case OP_2SWAP: return "OP_2SWAP"; case OP_IFDUP: return "OP_IFDUP"; case OP_DEPTH: return "OP_DEPTH"; case OP_DROP: return "OP_DROP"; case OP_DUP: return "OP_DUP"; case OP_NIP: return "OP_NIP"; case OP_OVER: return "OP_OVER"; case OP_PICK: return "OP_PICK"; case OP_ROLL: return "OP_ROLL"; case OP_ROT: return "OP_ROT"; case OP_SWAP: return "OP_SWAP"; case OP_TUCK: return "OP_TUCK"; // splice ops case OP_CAT: return "OP_CAT"; case OP_SPLIT: return "OP_SPLIT"; case OP_NUM2BIN: return "OP_NUM2BIN"; case OP_BIN2NUM: return "OP_BIN2NUM"; case OP_SIZE: return "OP_SIZE"; // bit logic case OP_INVERT: return "OP_INVERT"; case OP_AND: return "OP_AND"; case OP_OR: return "OP_OR"; case OP_XOR: return "OP_XOR"; case OP_EQUAL: return "OP_EQUAL"; case OP_EQUALVERIFY: return "OP_EQUALVERIFY"; case OP_RESERVED1: return "OP_RESERVED1"; case OP_RESERVED2: return "OP_RESERVED2"; // numeric case OP_1ADD: return "OP_1ADD"; case OP_1SUB: return "OP_1SUB"; case OP_2MUL: return "OP_2MUL"; case OP_2DIV: return "OP_2DIV"; case OP_NEGATE: return "OP_NEGATE"; case OP_ABS: return "OP_ABS"; case OP_NOT: return "OP_NOT"; case OP_0NOTEQUAL: return "OP_0NOTEQUAL"; case OP_ADD: return "OP_ADD"; case OP_SUB: return "OP_SUB"; case OP_MUL: return "OP_MUL"; case OP_DIV: return "OP_DIV"; case OP_MOD: return "OP_MOD"; case OP_LSHIFT: return "OP_LSHIFT"; case OP_RSHIFT: return "OP_RSHIFT"; case OP_BOOLAND: return "OP_BOOLAND"; case OP_BOOLOR: return "OP_BOOLOR"; case OP_NUMEQUAL: return "OP_NUMEQUAL"; case OP_NUMEQUALVERIFY: return "OP_NUMEQUALVERIFY"; case OP_NUMNOTEQUAL: return "OP_NUMNOTEQUAL"; case OP_LESSTHAN: return "OP_LESSTHAN"; case OP_GREATERTHAN: return "OP_GREATERTHAN"; case OP_LESSTHANOREQUAL: return "OP_LESSTHANOREQUAL"; case OP_GREATERTHANOREQUAL: return "OP_GREATERTHANOREQUAL"; case OP_MIN: return "OP_MIN"; case OP_MAX: return "OP_MAX"; case OP_WITHIN: return "OP_WITHIN"; // crypto case OP_RIPEMD160: return "OP_RIPEMD160"; case OP_SHA1: return "OP_SHA1"; case OP_SHA256: return "OP_SHA256"; case OP_HASH160: return "OP_HASH160"; case OP_HASH256: return "OP_HASH256"; case OP_CODESEPARATOR: return "OP_CODESEPARATOR"; case OP_CHECKSIG: return "OP_CHECKSIG"; case OP_CHECKSIGVERIFY: return "OP_CHECKSIGVERIFY"; case OP_CHECKMULTISIG: return "OP_CHECKMULTISIG"; case OP_CHECKMULTISIGVERIFY: return "OP_CHECKMULTISIGVERIFY"; case OP_CHECKDATASIG: return "OP_CHECKDATASIG"; case OP_CHECKDATASIGVERIFY: return "OP_CHECKDATASIGVERIFY"; // expansion case OP_NOP1: return "OP_NOP1"; case OP_CHECKLOCKTIMEVERIFY: return "OP_CHECKLOCKTIMEVERIFY"; case OP_CHECKSEQUENCEVERIFY: return "OP_CHECKSEQUENCEVERIFY"; case OP_NOP4: return "OP_NOP4"; case OP_NOP5: return "OP_NOP5"; case OP_NOP6: return "OP_NOP6"; case OP_NOP7: return "OP_NOP7"; case OP_NOP8: return "OP_NOP8"; case OP_NOP9: return "OP_NOP9"; case OP_NOP10: return "OP_NOP10"; case OP_INVALIDOPCODE: return "OP_INVALIDOPCODE"; // Note: // The template matching params OP_SMALLINTEGER/etc are defined in // opcodetype enum as kind of implementation hack, they are *NOT* // real opcodes. If found in real Script, just let the default: // case deal with them. default: return "OP_UNKNOWN"; } } bool CScriptNum::IsMinimallyEncoded(const std::vector &vch, const size_t nMaxNumSize) { if (vch.size() > nMaxNumSize) { return false; } if (vch.size() > 0) { // Check that the number is encoded with the minimum possible number // of bytes. // // If the most-significant-byte - excluding the sign bit - is zero // then we're not minimal. Note how this test also rejects the // negative-zero encoding, 0x80. if ((vch.back() & 0x7f) == 0) { // One exception: if there's more than one byte and the most // significant bit of the second-most-significant-byte is set it // would conflict with the sign bit. An example of this case is // +-255, which encode to 0xff00 and 0xff80 respectively. // (big-endian). if (vch.size() <= 1 || (vch[vch.size() - 2] & 0x80) == 0) { return false; } } } return true; } bool CScriptNum::MinimallyEncode(std::vector &data) { if (data.size() == 0) { return false; } // If the last byte is not 0x00 or 0x80, we are minimally encoded. uint8_t last = data.back(); if (last & 0x7f) { return false; } // If the script is one byte long, then we have a zero, which encodes as an // empty array. if (data.size() == 1) { data = {}; return true; } // If the next byte has it sign bit set, then we are minimaly encoded. if (data[data.size() - 2] & 0x80) { return false; } // We are not minimally encoded, we need to figure out how much to trim. for (size_t i = data.size() - 1; i > 0; i--) { // We found a non zero byte, time to encode. if (data[i - 1] != 0) { if (data[i - 1] & 0x80) { // We found a byte with it sign bit set so we need one more // byte. data[i++] = last; } else { // the sign bit is clear, we can use it. data[i - 1] |= last; } data.resize(i); return true; } } // If we the whole thing is zeros, then we have a zero. data = {}; return true; } uint32_t CScript::GetSigOpCount(uint32_t flags, bool fAccurate) const { uint32_t n = 0; const_iterator pc = begin(); opcodetype lastOpcode = OP_INVALIDOPCODE; while (pc < end()) { opcodetype opcode; if (!GetOp(pc, opcode)) { break; } switch (opcode) { case OP_CHECKSIG: case OP_CHECKSIGVERIFY: n++; break; + case OP_CHECKDATASIG: + case OP_CHECKDATASIGVERIFY: + if (flags & SCRIPT_ENABLE_CHECKDATASIG) { + n++; + } + break; + case OP_CHECKMULTISIG: case OP_CHECKMULTISIGVERIFY: if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) { n += DecodeOP_N(lastOpcode); } else { n += MAX_PUBKEYS_PER_MULTISIG; } break; default: break; } lastOpcode = opcode; } return n; } uint32_t CScript::GetSigOpCount(uint32_t flags, const CScript &scriptSig) const { if ((flags & SCRIPT_VERIFY_P2SH) == 0 || !IsPayToScriptHash()) { return GetSigOpCount(flags, true); } // This is a pay-to-script-hash scriptPubKey; // get the last item that the scriptSig // pushes onto the stack: const_iterator pc = scriptSig.begin(); std::vector data; while (pc < scriptSig.end()) { opcodetype opcode; if (!scriptSig.GetOp(pc, opcode, data)) { return 0; } if (opcode > OP_16) { return 0; } } /// ... and return its opcount: CScript subscript(data.begin(), data.end()); return subscript.GetSigOpCount(flags, true); } bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: return (this->size() == 23 && (*this)[0] == OP_HASH160 && (*this)[1] == 0x14 && (*this)[22] == OP_EQUAL); } bool CScript::IsCommitment(const std::vector &data) const { // To ensure we have an immediate push, we limit the commitment size to 64 // bytes. In addition to the data themselves, we have 2 extra bytes: // OP_RETURN and the push opcode itself. if (data.size() > 64 || this->size() != data.size() + 2) { return false; } if ((*this)[0] != OP_RETURN || (*this)[1] != data.size()) { return false; } for (size_t i = 0; i < data.size(); i++) { if ((*this)[i + 2] != data[i]) { return false; } } return true; } // A witness program is any valid CScript that consists of a 1-byte push opcode // followed by a data push between 2 and 40 bytes. bool CScript::IsWitnessProgram(int &version, std::vector &program) const { if (this->size() < 4 || this->size() > 42) { return false; } if ((*this)[0] != OP_0 && ((*this)[0] < OP_1 || (*this)[0] > OP_16)) { return false; } if (size_t((*this)[1] + 2) == this->size()) { version = DecodeOP_N((opcodetype)(*this)[0]); program = std::vector(this->begin() + 2, this->end()); return true; } return false; } bool CScript::IsPushOnly(const_iterator pc) const { while (pc < end()) { opcodetype opcode; if (!GetOp(pc, opcode)) { return false; } // Note that IsPushOnly() *does* consider OP_RESERVED to be a push-type // opcode, however execution of OP_RESERVED fails, so it's not relevant // to P2SH/BIP62 as the scriptSig would fail prior to the P2SH special // validation code being executed. if (opcode > OP_16) { return false; } } return true; } bool CScript::IsPushOnly() const { return this->IsPushOnly(begin()); } diff --git a/src/test/sigopcount_tests.cpp b/src/test/sigopcount_tests.cpp index fcea2e21e..f9980f272 100644 --- a/src/test/sigopcount_tests.cpp +++ b/src/test/sigopcount_tests.cpp @@ -1,262 +1,279 @@ // Copyright (c) 2012-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "consensus/consensus.h" #include "consensus/validation.h" #include "key.h" #include "policy/policy.h" // For STANDARD_CHECKDATASIG_VERIFY_FLAGS. #include "pubkey.h" #include "script/interpreter.h" #include "script/script.h" #include "script/standard.h" #include "test/test_bitcoin.h" #include "uint256.h" #include "validation.h" #include #include #include // Helpers: static std::vector Serialize(const CScript &s) { std::vector sSerialized(s.begin(), s.end()); return sSerialized; } BOOST_FIXTURE_TEST_SUITE(sigopcount_tests, BasicTestingSetup) void CheckScriptSigOps(const CScript &script, uint32_t accurate_sigops, - uint32_t inaccurate_sigops) { - const uint32_t flags = STANDARD_CHECKDATASIG_VERIFY_FLAGS; + uint32_t inaccurate_sigops, uint32_t datasigops) { + const uint32_t stdflags = STANDARD_SCRIPT_VERIFY_FLAGS; + const uint32_t datasigflags = STANDARD_CHECKDATASIG_VERIFY_FLAGS; - BOOST_CHECK_EQUAL(script.GetSigOpCount(flags, false), inaccurate_sigops); - BOOST_CHECK_EQUAL(script.GetSigOpCount(flags, true), accurate_sigops); + BOOST_CHECK_EQUAL(script.GetSigOpCount(stdflags, false), inaccurate_sigops); + BOOST_CHECK_EQUAL(script.GetSigOpCount(datasigflags, false), + inaccurate_sigops + datasigops); + BOOST_CHECK_EQUAL(script.GetSigOpCount(stdflags, true), accurate_sigops); + BOOST_CHECK_EQUAL(script.GetSigOpCount(datasigflags, true), + accurate_sigops + datasigops); const CScript p2sh = GetScriptForDestination(CScriptID(script)); const CScript scriptSig = CScript() << OP_0 << Serialize(script); - BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(flags, scriptSig), accurate_sigops); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(stdflags, scriptSig), accurate_sigops); + BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(datasigflags, scriptSig), + accurate_sigops + datasigops); // Check that GetSigOpCount do not report sigops in the P2SH script when the // P2SH flags isn't passed in. BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(SCRIPT_VERIFY_NONE, scriptSig), 0U); // Check that GetSigOpCount report the exact count when not passed a P2SH. - BOOST_CHECK_EQUAL(script.GetSigOpCount(flags, p2sh), accurate_sigops); + BOOST_CHECK_EQUAL(script.GetSigOpCount(stdflags, p2sh), accurate_sigops); + BOOST_CHECK_EQUAL(script.GetSigOpCount(datasigflags, p2sh), + accurate_sigops + datasigops); BOOST_CHECK_EQUAL(script.GetSigOpCount(SCRIPT_VERIFY_NONE, p2sh), accurate_sigops); } BOOST_AUTO_TEST_CASE(GetSigOpCount) { // Test CScript::GetSigOpCount() - CheckScriptSigOps(CScript(), 0, 0); + CheckScriptSigOps(CScript(), 0, 0, 0); uint160 dummy; const CScript s1 = CScript() << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << OP_2 << OP_CHECKMULTISIG; - CheckScriptSigOps(s1, 2, 20); + CheckScriptSigOps(s1, 2, 20, 0); const CScript s2 = CScript(s1) << OP_IF << OP_CHECKSIG << OP_ENDIF; - CheckScriptSigOps(s2, 3, 21); + CheckScriptSigOps(s2, 3, 21, 0); std::vector keys; for (int i = 0; i < 3; i++) { CKey k; k.MakeNewKey(true); keys.push_back(k.GetPubKey()); } const CScript s3 = GetScriptForMultisig(1, keys); - CheckScriptSigOps(s3, 3, 20); + CheckScriptSigOps(s3, 3, 20, 0); const CScript p2sh = GetScriptForDestination(CScriptID(s3)); - CheckScriptSigOps(p2sh, 0, 0); + CheckScriptSigOps(p2sh, 0, 0, 0); CScript scriptSig2; scriptSig2 << OP_1 << ToByteVector(dummy) << ToByteVector(dummy) << Serialize(s3); + BOOST_CHECK_EQUAL( + p2sh.GetSigOpCount(STANDARD_SCRIPT_VERIFY_FLAGS, scriptSig2), 3U); BOOST_CHECK_EQUAL( p2sh.GetSigOpCount(STANDARD_CHECKDATASIG_VERIFY_FLAGS, scriptSig2), 3U); BOOST_CHECK_EQUAL(p2sh.GetSigOpCount(SCRIPT_VERIFY_NONE, scriptSig2), 0U); + + const CScript s4 = CScript(s1) << OP_IF << OP_CHECKDATASIG << OP_ENDIF; + CheckScriptSigOps(s4, 2, 20, 1); + + const CScript s5 = CScript(s4) << OP_CHECKDATASIGVERIFY; + CheckScriptSigOps(s5, 2, 20, 2); } /** * Verifies script execution of the zeroth scriptPubKey of tx output and zeroth * scriptSig and witness of tx input. */ ScriptError VerifyWithFlag(const CTransaction &output, const CMutableTransaction &input, int flags) { ScriptError error; CTransaction inputi(input); bool ret = VerifyScript( inputi.vin[0].scriptSig, output.vout[0].scriptPubKey, flags, TransactionSignatureChecker(&inputi, 0, output.vout[0].nValue), &error); BOOST_CHECK_EQUAL((ret == true), (error == SCRIPT_ERR_OK)); return error; } /** * Builds a creationTx from scriptPubKey and a spendingTx from scriptSig and * witness such that spendingTx spends output zero of creationTx. Also inserts * creationTx's output into the coins view. */ void BuildTxs(CMutableTransaction &spendingTx, CCoinsViewCache &coins, CMutableTransaction &creationTx, const CScript &scriptPubKey, const CScript &scriptSig) { creationTx.nVersion = 1; creationTx.vin.resize(1); creationTx.vin[0].prevout = COutPoint(); creationTx.vin[0].scriptSig = CScript(); creationTx.vout.resize(1); creationTx.vout[0].nValue = Amount(1); creationTx.vout[0].scriptPubKey = scriptPubKey; spendingTx.nVersion = 1; spendingTx.vin.resize(1); spendingTx.vin[0].prevout = COutPoint(creationTx.GetId(), 0); spendingTx.vin[0].scriptSig = scriptSig; spendingTx.vout.resize(1); spendingTx.vout[0].nValue = Amount(1); spendingTx.vout[0].scriptPubKey = CScript(); AddCoins(coins, CTransaction(creationTx), 0); } BOOST_AUTO_TEST_CASE(GetTxSigOpCost) { // Transaction creates outputs CMutableTransaction creationTx; // Transaction that spends outputs and whose sig op cost is going to be // tested CMutableTransaction spendingTx; // Create utxo set CCoinsView coinsDummy; CCoinsViewCache coins(&coinsDummy); // Create key CKey key; key.MakeNewKey(true); CPubKey pubkey = key.GetPubKey(); // Default flags const uint32_t flags = SCRIPT_VERIFY_P2SH; // Multisig script (legacy counting) { CScript scriptPubKey = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY; // Do not use a valid signature to avoid using wallet operations. CScript scriptSig = CScript() << OP_0 << OP_0; BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig); // Legacy counting only includes signature operations in scriptSigs and // scriptPubKeys of a transaction and does not take the actual executed // sig operations into account. spendingTx in itself does not contain a // signature operation. BOOST_CHECK_EQUAL( GetTransactionSigOpCount(CTransaction(spendingTx), coins, flags), 0); // creationTx contains two signature operations in its scriptPubKey, but // legacy counting is not accurate. BOOST_CHECK_EQUAL( GetTransactionSigOpCount(CTransaction(creationTx), coins, flags), MAX_PUBKEYS_PER_MULTISIG); // Sanity check: script verification fails because of an invalid // signature. BOOST_CHECK_EQUAL( VerifyWithFlag(CTransaction(creationTx), spendingTx, flags), SCRIPT_ERR_CHECKMULTISIGVERIFY); // Make sure non P2SH sigops are counted even if the flag for P2SH is // not passed in. BOOST_CHECK_EQUAL(GetTransactionSigOpCount(CTransaction(spendingTx), coins, SCRIPT_VERIFY_NONE), 0); BOOST_CHECK_EQUAL(GetTransactionSigOpCount(CTransaction(creationTx), coins, SCRIPT_VERIFY_NONE), MAX_PUBKEYS_PER_MULTISIG); } // Multisig nested in P2SH { CScript redeemScript = CScript() << 1 << ToByteVector(pubkey) << ToByteVector(pubkey) << 2 << OP_CHECKMULTISIGVERIFY; CScript scriptPubKey = GetScriptForDestination(CScriptID(redeemScript)); CScript scriptSig = CScript() << OP_0 << OP_0 << ToByteVector(redeemScript); BuildTxs(spendingTx, coins, creationTx, scriptPubKey, scriptSig); BOOST_CHECK_EQUAL( GetTransactionSigOpCount(CTransaction(spendingTx), coins, flags), 2); BOOST_CHECK_EQUAL( VerifyWithFlag(CTransaction(creationTx), spendingTx, flags), SCRIPT_ERR_CHECKMULTISIGVERIFY); // Make sure P2SH sigops are not counted if the flag for P2SH is not // passed in. BOOST_CHECK_EQUAL(GetTransactionSigOpCount(CTransaction(spendingTx), coins, SCRIPT_VERIFY_NONE), 0); } } BOOST_AUTO_TEST_CASE(test_consensus_sigops_limit) { BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(1), MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(123456), MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(1000000), MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(1000001), 2 * MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(1348592), 2 * MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(2000000), 2 * MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(2000001), 3 * MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL(GetMaxBlockSigOpsCount(2654321), 3 * MAX_BLOCK_SIGOPS_PER_MB); BOOST_CHECK_EQUAL( GetMaxBlockSigOpsCount(std::numeric_limits::max()), 4295 * MAX_BLOCK_SIGOPS_PER_MB); } BOOST_AUTO_TEST_CASE(test_max_sigops_per_tx) { CMutableTransaction tx; tx.nVersion = 1; tx.vin.resize(1); tx.vin[0].prevout = COutPoint(InsecureRand256(), 0); tx.vin[0].scriptSig = CScript(); tx.vout.resize(1); tx.vout[0].nValue = Amount(1); tx.vout[0].scriptPubKey = CScript(); { CValidationState state; BOOST_CHECK(CheckRegularTransaction(CTransaction(tx), state, false)); } // Get just before the limit. for (size_t i = 0; i < MAX_TX_SIGOPS_COUNT; i++) { tx.vout[0].scriptPubKey << OP_CHECKSIG; } { CValidationState state; BOOST_CHECK(CheckRegularTransaction(CTransaction(tx), state, false)); } // And go over. tx.vout[0].scriptPubKey << OP_CHECKSIG; { CValidationState state; BOOST_CHECK(!CheckRegularTransaction(CTransaction(tx), state, false)); BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txn-sigops"); } } BOOST_AUTO_TEST_SUITE_END()