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, it does show which error was actually thrown. Here’s what the above 1*COIN
bug would result in:
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 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++.