Implement additional invariant checks:

Invariant checks run after a transaction has been processed and
ensure that the end result of that transaction did not violate
any protocol rules.

New checks include:

* XRP balance checks for negative balances
* Offer balance checks for negative balances
* Zero balance checks when handling escrow
This commit is contained in:
Nik Bougalis
2017-05-25 00:13:20 -07:00
parent da7da5527c
commit d04d3d3c4a
2 changed files with 200 additions and 5 deletions

View File

@@ -82,6 +82,136 @@ XRPNotCreated::finalize(STTx const& tx, TER /*tec*/, beast::Journal const& j)
//------------------------------------------------------------------------------
void
XRPBalanceChecks::visitEntry(
uint256 const&,
bool,
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const& after)
{
auto isBad = [](STAmount const& balance)
{
if (!balance.native())
return true;
auto const drops = balance.xrp().drops();
// Can't have more than the number of drops instantiated
// in the genesis ledger.
if (drops > SYSTEM_CURRENCY_START)
return true;
// Can't have a negative balance (0 is OK)
if (drops < 0)
return true;
return false;
};
if(before && before->getType() == ltACCOUNT_ROOT)
bad_ |= isBad ((*before)[sfBalance]);
if(after && after->getType() == ltACCOUNT_ROOT)
bad_ |= isBad ((*after)[sfBalance]);
}
bool
XRPBalanceChecks::finalize(STTx const&, TER, beast::Journal const& j)
{
if (bad_)
{
JLOG(j.fatal()) << "Invariant failed: incorrect account XRP balance";
return false;
}
return true;
}
//------------------------------------------------------------------------------
void
NoBadOffers::visitEntry(
uint256 const&,
bool isDelete,
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const& after)
{
auto isBad = [](STAmount const& pays, STAmount const& gets)
{
// An offer should never be negative
if (pays < beast::zero)
return true;
if (gets < beast::zero)
return true;
// Can't have an XRP to XRP offer:
return pays.native() && gets.native();
};
if(before && before->getType() == ltOFFER)
bad_ |= isBad ((*before)[sfTakerPays], (*before)[sfTakerGets]);
if(after && after->getType() == ltOFFER)
bad_ |= isBad((*after)[sfTakerPays], (*after)[sfTakerGets]);
}
bool
NoBadOffers::finalize(STTx const& tx, TER, beast::Journal const& j)
{
if (bad_)
{
JLOG(j.fatal()) << "Invariant failed: offer with a bad amount";
return false;
}
return true;
}
//------------------------------------------------------------------------------
void
NoZeroEscrow::visitEntry(
uint256 const&,
bool isDelete,
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const& after)
{
auto isBad = [](STAmount const& amount)
{
if (!amount.native())
return true;
if (amount.xrp().drops() <= 0)
return true;
if (amount.xrp().drops() >= SYSTEM_CURRENCY_START)
return true;
return false;
};
if(before && before->getType() == ltESCROW)
bad_ |= isBad((*before)[sfAmount]);
if(after && after->getType() == ltESCROW)
bad_ |= isBad((*after)[sfAmount]);
}
bool
NoZeroEscrow::finalize(STTx const& tx, TER, beast::Journal const& j)
{
if (bad_)
{
JLOG(j.fatal()) << "Invariant failed: escrow specifies invalid amount";
return false;
}
return true;
}
//------------------------------------------------------------------------------
void
AccountRootsNotDeleted::visitEntry(
uint256 const&,

View File

@@ -132,9 +132,28 @@ public:
};
/**
* @brief Invariant: corresponding modified ledger entries should match in type and
* added entries should be a valid type.
*
* @brief Invariant: An account XRP balance must be in XRP and take a value
between 0 and SYSTEM_CURRENCY_START drops, inclusive.
*/
class XRPBalanceChecks
{
bool bad_ = false;
public:
void
visitEntry(
uint256 const&,
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER, beast::Journal const&);
};
/**
* @brief Invariant: corresponding modified ledger entries should match in type
* and added entries should be a valid type.
*/
class LedgerEntryTypesMatch
{
@@ -156,7 +175,6 @@ public:
/**
* @brief Invariant: Trust lines using XRP are not allowed.
*
*/
class NoXRPTrustLines
{
@@ -175,13 +193,60 @@ public:
finalize(STTx const&, TER, beast::Journal const&);
};
/**
* @brief Invariant: offers should be for non-negative amounts and must not
* be XRP to XRP.
*/
class NoBadOffers
{
bool bad_ = false;
public:
void
visitEntry(
uint256 const&,
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER, beast::Journal const&);
};
/**
* @brief Invariant: an escrow entry must take a value between 0 and
* SYSTEM_CURRENCY_START drops exclusive.
*/
class NoZeroEscrow
{
bool bad_ = false;
public:
void
visitEntry(
uint256 const&,
bool,
std::shared_ptr<SLE const> const&,
std::shared_ptr<SLE const> const&);
bool
finalize(STTx const&, TER, beast::Journal const&);
};
// additional invariant checks can be declared above and then added to this
// tuple
using InvariantChecks = std::tuple<
AccountRootsNotDeleted,
LedgerEntryTypesMatch,
XRPBalanceChecks,
XRPNotCreated,
NoXRPTrustLines
NoXRPTrustLines,
NoBadOffers,
NoZeroEscrow
>;
/**