Merge #11220: Check specific validation error in miner tests
12781db
[Tests] check specific validation error in miner tests (Sjors Provoost)
Pull request description:
## Problem
`BOOST_CHECK_THROW` merely checks that some `std::runtime_error` is
thrown, but not which one.
Here's an example of how this can cause a test to pass when a developer
introduces a consensus bug. The test for the sigops limit assumes
that `CreateNewBlock` fails with `bad-blk-sigops`. However it can
also fail with bad-txns-vout-negative, if a naive developer lowers
`BLOCKSUBSIDY` to `1*COIN`.
## Solution
`BOOST_CHECK_EXCEPTION` allows an additional predicate function. This
commit uses this for all exceptions that are checked for in
`miner_tets.cpp`:
* `bad-blk-sigops`
* `bad-cb-multiple`
* `bad-txns-inputs-missingorspent`
* `block-validation-failed`
If the function throws a different error, the test will fail. Although the message produced by Boost is a bit [confusing](http://boost.2283326.n4.nabble.com/Test-BOOST-CHECK-EXCEPTION-error-message-still-vague-tt4683257.html#a4683554), it does show which error was actually thrown. Here's what the above `1*COIN` bug would result in:
<img width="1134" alt="schermafbeelding 2017-09-02 om 23 42 29" src="https://user-images.githubusercontent.com/10217/29998976-815cabce-9038-11e7-9c46-f5f6cfb0ca7d.png">
## Other considerations
A more elegant solution in my opinion would be to subclass `std::runtime_error` for each `INVALID_TRANSACTION` type, but this would involve touching consensus code.
I put the predicates in `test_bitcoin.h` because I assume they can be reused in other test files. However [serialize_tests.cpp](https://github.com/bitcoin/bitcoin/blob/v0.15.0rc3/src/test/serialize_tests.cpp#L245) also uses `BOOST_CHECK_EXCEPTION` and it defines the predicate in the test file itself.
Instead of four `IsRejectInvalidReasonX(std::runtime_error const& e)` functions, I'd prefer something reusable like `bool IsRejectInvalidReason(String reason)(std::runtime_error const& e)`, which would be used like `BOOST_CHECK_EXCEPTION(functionThatThrows(), std::runtime_error, IsRejectInvalidReason("bad-blk-sigops")`. I couldn't figure out how to do that in C++.
Tree-SHA512: e364f19b4ac19f910f6e8d6533357f57ccddcbd9d53dcfaf923d424d2b9711446d6f36da193208b35788ca21863eadaa7becd9ad890334d334bccf8c2e63dee1
This commit is contained in:
commit
2971fd030f
1 changed files with 19 additions and 5 deletions
|
@ -26,6 +26,17 @@
|
|||
|
||||
BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
|
||||
|
||||
// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
|
||||
class HasReason {
|
||||
public:
|
||||
HasReason(const std::string& reason) : m_reason(reason) {}
|
||||
bool operator() (const std::runtime_error& e) const {
|
||||
return std::string(e.what()).find(m_reason) != std::string::npos;
|
||||
};
|
||||
private:
|
||||
const std::string m_reason;
|
||||
};
|
||||
|
||||
static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
|
||||
|
||||
static BlockAssembler AssemblerForTest(const CChainParams& params) {
|
||||
|
@ -264,7 +275,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
}
|
||||
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
|
||||
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
|
||||
mempool.clear();
|
||||
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
|
@ -304,7 +316,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
// orphan in mempool, template creation fails
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
|
||||
mempool.clear();
|
||||
|
||||
// child with higher feerate than parent
|
||||
|
@ -332,7 +344,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
hash = tx.GetHash();
|
||||
// give it a fee so it'll get mined
|
||||
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
|
||||
// Should throw bad-cb-multiple
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
|
||||
mempool.clear();
|
||||
|
||||
// double spend txn pair in mempool, template creation fails
|
||||
|
@ -345,7 +358,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].scriptPubKey = CScript() << OP_2;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
|
||||
mempool.clear();
|
||||
|
||||
// subsidy changing
|
||||
|
@ -389,7 +402,8 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
tx.vout[0].nValue -= LOWFEE;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
|
||||
BOOST_CHECK_THROW(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error);
|
||||
// Should throw block-validation-failed
|
||||
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
|
||||
mempool.clear();
|
||||
|
||||
// Delete the dummy blocks again.
|
||||
|
|
Loading…
Reference in a new issue