Improve invariant checking:

Add a new invariant checker that verifies that we never charge a
fee higher than specified in the transaction; we will charge less
in some corner cases where the transacting account cannot afford
the fee.

Detect more anomalous conditions, and improve the logged error
messages.

Clarify the code flow associated with invoking the invariant checker
from `Transactor`, add extra comments and improve naming to make the
code self-documenting.
This commit is contained in:
Nikolaos D. Bougalis
2018-04-17 02:03:10 -07:00
committed by seelabs
parent 118c25c0f0
commit 2ac1c2b433
7 changed files with 305 additions and 159 deletions

View File

@@ -52,15 +52,21 @@ class Invariants_test : public beast::unit_test::suite
// this is common setup/method for running a failing invariant check. The
// precheck function is used to manipulate the ApplyContext with view
// changes that will cause the check to fail.
using Precheck = std::function <
bool (
test::jtx::Account const& a,
test::jtx::Account const& b,
ApplyContext& ac)>;
using TXMod = std::function <
void (STTx& tx)>;
void
doInvariantCheck( bool enabled,
std::vector<std::string> const& expect_logs,
std::function <
bool (
test::jtx::Account const& a,
test::jtx::Account const& b,
ApplyContext& ac)>
const& precheck )
Precheck const& precheck,
XRPAmount fee = XRPAmount{},
TXMod txmod = [](STTx&){})
{
using namespace test::jtx;
Env env {*this};
@@ -79,6 +85,7 @@ class Invariants_test : public beast::unit_test::suite
// dummy/empty tx to setup the AccountContext
auto tx = STTx {ttACCOUNT_SET, [](STObject&){ } };
txmod(tx);
OpenView ov {*env.current()};
TestSink sink;
beast::Journal jlog {sink};
@@ -98,7 +105,7 @@ class Invariants_test : public beast::unit_test::suite
// invoke check twice to cover tec and tef cases
for (auto i : {0,1})
{
tr = ac.checkInvariants(tr);
tr = ac.checkInvariants(tr, fee);
if (enabled)
{
BEAST_EXPECT(tr == (i == 0
@@ -144,7 +151,7 @@ class Invariants_test : public beast::unit_test::suite
testcase << "checks " << (enabled ? "enabled" : "disabled") <<
" - XRP created";
doInvariantCheck (enabled,
{{ "XRP net change was 500 on a fee of 0" }},
{{ "XRP net change was positive: 500" }},
[](Account const& A1, Account const&, ApplyContext& ac)
{
// put a single account in the view and "manufacture" some XRP
@@ -185,7 +192,7 @@ class Invariants_test : public beast::unit_test::suite
" - LE types don't match";
doInvariantCheck (enabled,
{{ "ledger entry type mismatch" },
{ "XRP net change was -1000000000 on a fee of 0" }},
{ "XRP net change of -1000000000 doesn't match fee 0" }},
[](Account const& A1, Account const&, ApplyContext& ac)
{
// replace an entry in the table with an SLE of a different type
@@ -258,7 +265,7 @@ class Invariants_test : public beast::unit_test::suite
doInvariantCheck (enabled,
{{ "incorrect account XRP balance" },
{ "XRP net change was 99999999000000001 on a fee of 0" }},
{ "XRP net change was positive: 99999999000000001" }},
[](Account const& A1, Account const&, ApplyContext& ac)
{
// balance exceeds genesis amount
@@ -272,7 +279,7 @@ class Invariants_test : public beast::unit_test::suite
doInvariantCheck (enabled,
{{ "incorrect account XRP balance" },
{ "XRP net change was -1000000001 on a fee of 0" }},
{ "XRP net change of -1000000001 doesn't match fee 0" }},
[](Account const& A1, Account const&, ApplyContext& ac)
{
// balance is negative
@@ -285,6 +292,37 @@ class Invariants_test : public beast::unit_test::suite
});
}
void
testTransactionFeeCheck(bool enabled)
{
using namespace test::jtx;
using namespace std::string_literals;
testcase << "checks " << (enabled ? "enabled" : "disabled") <<
" - Transaction fee checks";
doInvariantCheck (enabled,
{{ "fee paid was negative: -1" },
{ "XRP net change of 0 doesn't match fee -1" }},
[](Account const&, Account const&, ApplyContext&) { return true; },
XRPAmount{-1});
doInvariantCheck (enabled,
{{ "fee paid exceeds system limit: "s +
std::to_string(SYSTEM_CURRENCY_START) },
{ "XRP net change of 0 doesn't match fee "s +
std::to_string(SYSTEM_CURRENCY_START) }},
[](Account const&, Account const&, ApplyContext&) { return true; },
XRPAmount{SYSTEM_CURRENCY_START});
doInvariantCheck (enabled,
{{ "fee paid is 20 exceeds fee specified in transaction." },
{ "XRP net change of 0 doesn't match fee 20" }},
[](Account const&, Account const&, ApplyContext&) { return true; },
XRPAmount{20},
[](STTx& tx) { tx.setFieldAmount(sfFee, XRPAmount{10}); } );
}
void
testNoBadOffers(bool enabled)
{
@@ -373,7 +411,7 @@ class Invariants_test : public beast::unit_test::suite
});
doInvariantCheck (enabled,
{{ "XRP net change was -1000000 on a fee of 0"},
{{ "XRP net change of -1000000 doesn't match fee 0"},
{ "escrow specifies invalid amount" }},
[](Account const& A1, Account const&, ApplyContext& ac)
{
@@ -389,7 +427,7 @@ class Invariants_test : public beast::unit_test::suite
});
doInvariantCheck (enabled,
{{ "XRP net change was 100000000000000001 on a fee of 0" },
{{ "XRP net change was positive: 100000000000000001" },
{ "escrow specifies invalid amount" }},
[](Account const& A1, Account const&, ApplyContext& ac)
{
@@ -419,6 +457,7 @@ public:
testTypesMatch (b);
testNoXRPTrustLine (b);
testXRPBalanceCheck (b);
testTransactionFeeCheck(b);
testNoBadOffers (b);
testNoZeroEscrow (b);
}