Implement transaction invariant checks (RIPD-1425):

Add new functionality to enforce one or more sanity checks (invariants)
on transactions. Add tests for each new invariant check. Allow
for easily adding additional invariant checks in the future.

Also Resolves
-------------

  - RIPD-1426
  - RIPD-1427
  - RIPD-1428
  - RIPD-1429
  - RIPD-1430
  - RIPD-1431
  - RIPD-1432

Release Notes
-------------

Creates a new ammendment named "EnforceInvariants" which must be
enabled in order for these new checks to run on each transaction.
This commit is contained in:
Mike Ellery
2017-02-23 08:11:44 -08:00
committed by Nik Bougalis
parent e52614ac81
commit 026a249173
20 changed files with 789 additions and 40 deletions

View File

@@ -0,0 +1,191 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/app/tx/impl/InvariantCheck.h>
#include <ripple/basics/Log.h>
namespace ripple {
void
XRPNotCreated::visitEntry(
uint256 const&,
bool isDelete,
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const& after)
{
if(before)
{
switch (before->getType())
{
case ltACCOUNT_ROOT:
drops_ -= (*before)[sfBalance].xrp().drops();
break;
case ltPAYCHAN:
drops_ -= ((*before)[sfAmount] - (*before)[sfBalance]).xrp().drops();
break;
case ltESCROW:
drops_ -= (*before)[sfAmount].xrp().drops();
break;
default:
break;
}
}
if(after)
{
switch (after->getType())
{
case ltACCOUNT_ROOT:
drops_ += (*after)[sfBalance].xrp().drops();
break;
case ltPAYCHAN:
if (! isDelete)
drops_ += ((*after)[sfAmount] - (*after)[sfBalance]).xrp().drops();
break;
case ltESCROW:
if (! isDelete)
drops_ += (*after)[sfAmount].xrp().drops();
break;
default:
break;
}
}
}
bool
XRPNotCreated::finalize(STTx const& tx, TER /*tec*/, beast::Journal const& j)
{
auto fee = tx.getFieldAmount(sfFee).xrp().drops();
if(-1*fee <= drops_ && drops_ <= 0)
return true;
JLOG(j.fatal()) << "Invariant failed: XRP net change was " << drops_ <<
" on a fee of " << fee;
return false;
}
//------------------------------------------------------------------------------
void
AccountRootsNotDeleted::visitEntry(
uint256 const&,
bool isDelete,
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const&)
{
if (isDelete && before && before->getType() == ltACCOUNT_ROOT)
accountDeleted_ = true;
}
bool
AccountRootsNotDeleted::finalize(STTx const&, TER, beast::Journal const& j)
{
if (! accountDeleted_)
return true;
JLOG(j.fatal()) << "Invariant failed: an account root was deleted";
return false;
}
//------------------------------------------------------------------------------
void
LedgerEntryTypesMatch::visitEntry(
uint256 const&,
bool,
std::shared_ptr <SLE const> const& before,
std::shared_ptr <SLE const> const& after)
{
if (before && after && before->getType() != after->getType())
typeMismatch_ = true;
if (after)
{
switch (after->getType())
{
case ltACCOUNT_ROOT:
case ltDIR_NODE:
case ltRIPPLE_STATE:
case ltTICKET:
case ltSIGNER_LIST:
case ltOFFER:
case ltLEDGER_HASHES:
case ltAMENDMENTS:
case ltFEE_SETTINGS:
case ltESCROW:
case ltPAYCHAN:
break;
default:
invalidTypeAdded_ = true;
break;
}
}
}
bool
LedgerEntryTypesMatch::finalize(STTx const&, TER, beast::Journal const& j)
{
if ((! typeMismatch_) && (! invalidTypeAdded_))
return true;
if (typeMismatch_)
{
JLOG(j.fatal()) << "Invariant failed: ledger entry type mismatch";
}
if (invalidTypeAdded_)
{
JLOG(j.fatal()) << "Invariant failed: invalid ledger entry type added";
}
return false;
}
//------------------------------------------------------------------------------
void
NoXRPTrustLines::visitEntry(
uint256 const&,
bool,
std::shared_ptr <SLE const> const&,
std::shared_ptr <SLE const> const& after)
{
if (after && after->getType() == ltRIPPLE_STATE)
{
// checking the issue directly here instead of
// relying on .native() just in case native somehow
// were systematically incorrect
xrpTrustLine_ =
after->getFieldAmount (sfLowLimit).issue() == xrpIssue() ||
after->getFieldAmount (sfHighLimit).issue() == xrpIssue();
}
}
bool
NoXRPTrustLines::finalize(STTx const&, TER, beast::Journal const& j)
{
if (! xrpTrustLine_)
return true;
JLOG(j.fatal()) << "Invariant failed: an XRP trust line was created";
return false;
}
} // ripple