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

@@ -22,6 +22,52 @@
namespace ripple {
void
TransactionFeeCheck::visitEntry(
uint256 const&,
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&)
{
// nothing to do
}
bool
TransactionFeeCheck::finalize(
STTx const& tx,
TER const result,
XRPAmount const fee,
beast::Journal const& j)
{
// We should never charge a negative fee
if (fee.drops() < 0)
{
JLOG(j.fatal()) << "Invariant failed: fee paid was negative: " << fee.drops();
return false;
}
// We should never charge a fee that's greater than or equal to the
// entire XRP supply.
if (fee.drops() >= SYSTEM_CURRENCY_START)
{
JLOG(j.fatal()) << "Invariant failed: fee paid exceeds system limit: " << fee.drops();
return false;
}
// We should never charge more for a transaction than the transaction
// authorizes. It's possible to charge less in some circumstances.
if (fee > tx.getFieldAmount(sfFee).xrp())
{
JLOG(j.fatal()) << "Invariant failed: fee paid is " << fee.drops() <<
" exceeds fee specified in transaction.";
return false;
}
return true;
}
//------------------------------------------------------------------------------
void
XRPNotCreated::visitEntry(
uint256 const&,
@@ -29,6 +75,13 @@ XRPNotCreated::visitEntry(
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const& after)
{
/* We go through all modified ledger entries, looking only at account roots,
* escrow payments, and payment channels. We remove from the total any
* previous XRP values and add to the total any new XRP values. The net
* balance of a payment channel is computed from two fields (amount and
* balance) and deletions are ignored for paychan and escrow because the
* amount fields have not been adjusted for those in the case of deletion.
*/
if(before)
{
switch (before->getType())
@@ -69,15 +122,31 @@ XRPNotCreated::visitEntry(
}
bool
XRPNotCreated::finalize(STTx const& tx, TER /*tec*/, beast::Journal const& j)
XRPNotCreated::finalize(
STTx const& tx,
TER const,
XRPAmount const fee,
beast::Journal const& j)
{
auto fee = tx.getFieldAmount(sfFee).xrp().drops();
if(-1*fee <= drops_ && drops_ <= 0)
return true;
// The net change should never be positive, as this would mean that the
// transaction created XRP out of thin air. That's not possible.
if (drops_ > 0)
{
JLOG(j.fatal()) <<
"Invariant failed: XRP net change was positive: " << drops_;
return false;
}
JLOG(j.fatal()) << "Invariant failed: XRP net change was " << drops_ <<
" on a fee of " << fee;
return false;
// The negative of the net change should be equal to actual fee charged.
if (-drops_ != fee.drops())
{
JLOG(j.fatal()) <<
"Invariant failed: XRP net change of " << drops_ <<
" doesn't match fee " << fee.drops();
return false;
}
return true;
}
//------------------------------------------------------------------------------
@@ -116,7 +185,7 @@ XRPBalanceChecks::visitEntry(
}
bool
XRPBalanceChecks::finalize(STTx const&, TER, beast::Journal const& j)
XRPBalanceChecks::finalize(STTx const&, TER const, XRPAmount const, beast::Journal const& j)
{
if (bad_)
{
@@ -157,7 +226,7 @@ NoBadOffers::visitEntry(
}
bool
NoBadOffers::finalize(STTx const& tx, TER, beast::Journal const& j)
NoBadOffers::finalize(STTx const& tx, TER const, XRPAmount const, beast::Journal const& j)
{
if (bad_)
{
@@ -199,7 +268,7 @@ NoZeroEscrow::visitEntry(
}
bool
NoZeroEscrow::finalize(STTx const& tx, TER, beast::Journal const& j)
NoZeroEscrow::finalize(STTx const& tx, TER const, XRPAmount const, beast::Journal const& j)
{
if (bad_)
{
@@ -224,7 +293,7 @@ AccountRootsNotDeleted::visitEntry(
}
bool
AccountRootsNotDeleted::finalize(STTx const&, TER, beast::Journal const& j)
AccountRootsNotDeleted::finalize(STTx const&, TER const, XRPAmount const, beast::Journal const& j)
{
if (! accountDeleted_)
return true;
@@ -270,7 +339,7 @@ LedgerEntryTypesMatch::visitEntry(
}
bool
LedgerEntryTypesMatch::finalize(STTx const&, TER, beast::Journal const& j)
LedgerEntryTypesMatch::finalize(STTx const&, TER const, XRPAmount const, beast::Journal const& j)
{
if ((! typeMismatch_) && (! invalidTypeAdded_))
return true;
@@ -309,7 +378,7 @@ NoXRPTrustLines::visitEntry(
}
bool
NoXRPTrustLines::finalize(STTx const&, TER, beast::Journal const& j)
NoXRPTrustLines::finalize(STTx const&, TER const, XRPAmount const, beast::Journal const& j)
{
if (! xrpTrustLine_)
return true;