Compile time check preflight returns no tec (RIPD-1624):

The six different ranges of TER codes are broken up into six
different enumerations.  A template class allows subsets of
these enumerations to be aggregated.  This technique allows
verification at compile time that no TEC codes are returned
before the signature is checked.

Conversion between TER instance and integer is provided by
named functions.  This makes accidental conversion almost
impossible and makes type abuse easier to spot in the code
base.
This commit is contained in:
Scott Schurr
2018-04-16 17:51:36 -07:00
committed by seelabs
parent 7d163a45dc
commit 118c25c0f0
54 changed files with 612 additions and 161 deletions

View File

@@ -47,12 +47,12 @@ public:
beast::Journal const j; beast::Journal const j;
/// Intermediate transaction result /// Intermediate transaction result
TER const ter; NotTEC const ter;
/// Constructor /// Constructor
template<class Context> template<class Context>
PreflightResult(Context const& ctx_, PreflightResult(Context const& ctx_,
TER ter_) NotTEC ter_)
: tx(ctx_.tx) : tx(ctx_.tx)
, rules(ctx_.rules) , rules(ctx_.rules)
, flags(ctx_.flags) , flags(ctx_.flags)

View File

@@ -104,8 +104,8 @@ ApplyContext::checkInvariantsHelper(TER terResult, std::index_sequence<Is...>)
[](auto const& b) { return b; })) [](auto const& b) { return b; }))
{ {
terResult = (terResult == tecINVARIANT_FAILED) ? terResult = (terResult == tecINVARIANT_FAILED) ?
tefINVARIANT_FAILED : TER {tefINVARIANT_FAILED} :
tecINVARIANT_FAILED ; TER {tecINVARIANT_FAILED} ;
JLOG(journal.fatal()) << JLOG(journal.fatal()) <<
"Transaction has failed one or more invariants: " << "Transaction has failed one or more invariants: " <<
to_string(tx.getJson (0)); to_string(tx.getJson (0));
@@ -114,8 +114,8 @@ ApplyContext::checkInvariantsHelper(TER terResult, std::index_sequence<Is...>)
catch(std::exception const& ex) catch(std::exception const& ex)
{ {
terResult = (terResult == tecINVARIANT_FAILED) ? terResult = (terResult == tecINVARIANT_FAILED) ?
tefINVARIANT_FAILED : TER {tefINVARIANT_FAILED} :
tecINVARIANT_FAILED ; TER {tecINVARIANT_FAILED} ;
JLOG(journal.fatal()) << JLOG(journal.fatal()) <<
"Transaction caused an exception in an invariant" << "Transaction caused an exception in an invariant" <<
", ex: " << ex.what() << ", ex: " << ex.what() <<

View File

@@ -28,13 +28,13 @@
namespace ripple { namespace ripple {
TER NotTEC
CancelCheck::preflight (PreflightContext const& ctx) CancelCheck::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled (featureChecks)) if (! ctx.rules.enabled (featureChecks))
return temDISABLED; return temDISABLED;
TER const ret {preflight1 (ctx)}; NotTEC const ret {preflight1 (ctx)};
if (! isTesSuccess (ret)) if (! isTesSuccess (ret))
return ret; return ret;

View File

@@ -34,7 +34,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -24,7 +24,7 @@
namespace ripple { namespace ripple {
TER NotTEC
CancelOffer::preflight (PreflightContext const& ctx) CancelOffer::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);

View File

@@ -37,7 +37,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -26,7 +26,7 @@
namespace ripple { namespace ripple {
TER NotTEC
CancelTicket::preflight (PreflightContext const& ctx) CancelTicket::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureTickets)) if (! ctx.rules.enabled(featureTickets))

View File

@@ -36,7 +36,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;

View File

@@ -31,13 +31,13 @@
namespace ripple { namespace ripple {
TER NotTEC
CashCheck::preflight (PreflightContext const& ctx) CashCheck::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled (featureChecks)) if (! ctx.rules.enabled (featureChecks))
return temDISABLED; return temDISABLED;
TER const ret {preflight1 (ctx)}; NotTEC const ret {preflight1 (ctx)};
if (! isTesSuccess (ret)) if (! isTesSuccess (ret))
return ret; return ret;

View File

@@ -34,7 +34,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -27,7 +27,7 @@
namespace ripple { namespace ripple {
TER NotTEC
Change::preflight (PreflightContext const& ctx) Change::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight0(ctx); auto const ret = preflight0(ctx);

View File

@@ -39,7 +39,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;

View File

@@ -28,13 +28,13 @@
namespace ripple { namespace ripple {
TER NotTEC
CreateCheck::preflight (PreflightContext const& ctx) CreateCheck::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled (featureChecks)) if (! ctx.rules.enabled (featureChecks))
return temDISABLED; return temDISABLED;
TER const ret {preflight1 (ctx)}; NotTEC const ret {preflight1 (ctx)};
if (! isTesSuccess (ret)) if (! isTesSuccess (ret))
return ret; return ret;

View File

@@ -34,7 +34,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -38,7 +38,7 @@ CreateOffer::calculateMaxSpend(STTx const& tx)
saTakerGets.xrp() : beast::zero; saTakerGets.xrp() : beast::zero;
} }
TER NotTEC
CreateOffer::preflight (PreflightContext const& ctx) CreateOffer::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);
@@ -198,7 +198,9 @@ CreateOffer::preclaim(PreclaimContext const& ctx)
// //
// The return code change is attached to featureChecks as a convenience. // The return code change is attached to featureChecks as a convenience.
// The change is not big enough to deserve its own amendment. // The change is not big enough to deserve its own amendment.
return ctx.view.rules().enabled(featureChecks) ? tecEXPIRED : tesSUCCESS; return ctx.view.rules().enabled(featureChecks)
? TER {tecEXPIRED}
: TER {tesSUCCESS};
} }
// Make sure that we are authorized to hold what the taker will pay us. // Make sure that we are authorized to hold what the taker will pay us.
@@ -231,8 +233,8 @@ CreateOffer::checkAcceptAsset(ReadView const& view,
to_string (issue.account); to_string (issue.account);
return (flags & tapRETRY) return (flags & tapRETRY)
? terNO_ACCOUNT ? TER {terNO_ACCOUNT}
: tecNO_ISSUER; : TER {tecNO_ISSUER};
} }
// This code is attached to the FlowCross amendment as a matter of // This code is attached to the FlowCross amendment as a matter of
@@ -250,8 +252,8 @@ CreateOffer::checkAcceptAsset(ReadView const& view,
if (!trustLine) if (!trustLine)
{ {
return (flags & tapRETRY) return (flags & tapRETRY)
? terNO_LINE ? TER {terNO_LINE}
: tecNO_LINE; : TER {tecNO_LINE};
} }
// Entries have a canonical representation, determined by a // Entries have a canonical representation, determined by a
@@ -268,8 +270,8 @@ CreateOffer::checkAcceptAsset(ReadView const& view,
"delay: can't receive IOUs from issuer without auth."; "delay: can't receive IOUs from issuer without auth.";
return (flags & tapRETRY) return (flags & tapRETRY)
? terNO_AUTH ? TER {terNO_AUTH}
: tecNO_AUTH; : TER {tecNO_AUTH};
} }
} }
@@ -1073,7 +1075,7 @@ CreateOffer::applyGuts (Sandbox& sb, Sandbox& sbCancel)
auto viewJ = ctx_.app.journal("View"); auto viewJ = ctx_.app.journal("View");
auto result = tesSUCCESS; TER result = tesSUCCESS;
// Process a cancellation request that's passed along with an offer. // Process a cancellation request that's passed along with an offer.
if (cancelSequence) if (cancelSequence)
@@ -1107,7 +1109,7 @@ CreateOffer::applyGuts (Sandbox& sb, Sandbox& sbCancel)
// The return code change is attached to featureChecks as a convenience. // The return code change is attached to featureChecks as a convenience.
// The change is not big enough to deserve its own amendment. // The change is not big enough to deserve its own amendment.
TER const ter {ctx_.view().rules().enabled( TER const ter {ctx_.view().rules().enabled(
featureChecks) ? tecEXPIRED : tesSUCCESS}; featureChecks) ? TER {tecEXPIRED} : TER {tesSUCCESS}};
return{ ter, true }; return{ ter, true };
} }

View File

@@ -49,7 +49,7 @@ public:
/** Enforce constraints beyond those of the Transactor base class. */ /** Enforce constraints beyond those of the Transactor base class. */
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
/** Enforce constraints beyond those of the Transactor base class. */ /** Enforce constraints beyond those of the Transactor base class. */

View File

@@ -26,7 +26,7 @@
namespace ripple { namespace ripple {
TER NotTEC
CreateTicket::preflight (PreflightContext const& ctx) CreateTicket::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureTickets)) if (! ctx.rules.enabled(featureTickets))

View File

@@ -37,7 +37,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;

View File

@@ -92,7 +92,7 @@ EscrowCreate::calculateMaxSpend(STTx const& tx)
return tx[sfAmount].xrp(); return tx[sfAmount].xrp();
} }
TER NotTEC
EscrowCreate::preflight (PreflightContext const& ctx) EscrowCreate::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureEscrow)) if (! ctx.rules.enabled(featureEscrow))
@@ -293,7 +293,7 @@ checkCondition (Slice f, Slice c)
return validate (*fulfillment, *condition); return validate (*fulfillment, *condition);
} }
TER NotTEC
EscrowFinish::preflight (PreflightContext const& ctx) EscrowFinish::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureEscrow)) if (! ctx.rules.enabled(featureEscrow))
@@ -503,7 +503,7 @@ EscrowFinish::doApply()
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
TER NotTEC
EscrowCancel::preflight (PreflightContext const& ctx) EscrowCancel::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureEscrow)) if (! ctx.rules.enabled(featureEscrow))

View File

@@ -39,7 +39,7 @@ public:
calculateMaxSpend(STTx const& tx); calculateMaxSpend(STTx const& tx);
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER TER
@@ -59,7 +59,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static
@@ -83,7 +83,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER TER

View File

@@ -154,7 +154,7 @@ closeChannel (
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
TER NotTEC
PayChanCreate::preflight (PreflightContext const& ctx) PayChanCreate::preflight (PreflightContext const& ctx)
{ {
if (!ctx.rules.enabled (featurePayChan)) if (!ctx.rules.enabled (featurePayChan))
@@ -262,7 +262,7 @@ PayChanCreate::doApply()
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
TER NotTEC
PayChanFund::preflight (PreflightContext const& ctx) PayChanFund::preflight (PreflightContext const& ctx)
{ {
if (!ctx.rules.enabled (featurePayChan)) if (!ctx.rules.enabled (featurePayChan))
@@ -347,13 +347,17 @@ PayChanFund::doApply()
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
TER NotTEC
PayChanClaim::preflight (PreflightContext const& ctx) PayChanClaim::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featurePayChan)) if (! ctx.rules.enabled(featurePayChan))
return temDISABLED; return temDISABLED;
bool const noTecs = ctx.rules.enabled(fix1512); // A search through historic MainNet ledgers by the data team found no
// occurrences of a transaction with the error that fix1512 fixed. That
// means there are no old transactions with that error that we might
// need to replay. So the check for fix1512 is removed. Apr 2018.
// bool const noTecs = ctx.rules.enabled(fix1512);
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);
if (!isTesSuccess (ret)) if (!isTesSuccess (ret))
@@ -368,12 +372,7 @@ PayChanClaim::preflight (PreflightContext const& ctx)
return temBAD_AMOUNT; return temBAD_AMOUNT;
if (bal && amt && *bal > *amt) if (bal && amt && *bal > *amt)
{ return temBAD_AMOUNT;
if (noTecs)
return temBAD_AMOUNT;
else
return tecNO_PERMISSION;
}
{ {
auto const flags = ctx.tx.getFlags(); auto const flags = ctx.tx.getFlags();
@@ -398,12 +397,7 @@ PayChanClaim::preflight (PreflightContext const& ctx)
auto const authAmt = amt ? amt->xrp() : reqBalance; auto const authAmt = amt ? amt->xrp() : reqBalance;
if (reqBalance > authAmt) if (reqBalance > authAmt)
{ return temBAD_AMOUNT;
if (noTecs)
return temBAD_AMOUNT;
else
return tecNO_PERMISSION;
}
Keylet const k (ltPAYCHAN, ctx.tx[sfPayChannel]); Keylet const k (ltPAYCHAN, ctx.tx[sfPayChannel]);
if (!publicKeyType(ctx.tx[sfPublicKey])) if (!publicKeyType(ctx.tx[sfPublicKey]))

View File

@@ -35,7 +35,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static
@@ -59,7 +59,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER TER
@@ -79,7 +79,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER TER

View File

@@ -44,7 +44,7 @@ Payment::calculateMaxSpend(STTx const& tx)
return saDstAmount.native() ? saDstAmount.xrp() : beast::zero; return saDstAmount.native() ? saDstAmount.xrp() : beast::zero;
} }
TER NotTEC
Payment::preflight (PreflightContext const& ctx) Payment::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);

View File

@@ -49,7 +49,7 @@ public:
calculateMaxSpend(STTx const& tx); calculateMaxSpend(STTx const& tx);
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -48,7 +48,7 @@ SetAccount::affectsSubsequentTransactionAuth(STTx const& tx)
*uClearFlag == asfAccountTxnID); *uClearFlag == asfAccountTxnID);
} }
TER NotTEC
SetAccount::preflight (PreflightContext const& ctx) SetAccount::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);
@@ -190,7 +190,7 @@ SetAccount::preclaim(PreclaimContext const& ctx)
keylet::ownerDir(id))) keylet::ownerDir(id)))
{ {
JLOG(ctx.j.trace()) << "Retry: Owner directory not empty."; JLOG(ctx.j.trace()) << "Retry: Owner directory not empty.";
return (ctx.flags & tapRETRY) ? terOWNERS : tecOWNERS; return (ctx.flags & tapRETRY) ? TER {terOWNERS} : TER {tecOWNERS};
} }
} }

View File

@@ -45,7 +45,7 @@ public:
affectsSubsequentTransactionAuth(STTx const& tx); affectsSubsequentTransactionAuth(STTx const& tx);
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -48,7 +48,7 @@ SetRegularKey::calculateBaseFee (
return Transactor::calculateBaseFee (ctx); return Transactor::calculateBaseFee (ctx);
} }
TER NotTEC
SetRegularKey::preflight (PreflightContext const& ctx) SetRegularKey::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);

View File

@@ -44,7 +44,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -35,7 +35,7 @@ namespace ripple {
// setting the sfSignerListID to zero in all cases. // setting the sfSignerListID to zero in all cases.
static std::uint32_t const defaultSignerListID_ = 0; static std::uint32_t const defaultSignerListID_ = 0;
std::tuple<TER, std::uint32_t, std::tuple<NotTEC, std::uint32_t,
std::vector<SignerEntries::SignerEntry>, std::vector<SignerEntries::SignerEntry>,
SetSignerList::Operation> SetSignerList::Operation>
SetSignerList::determineOperation(STTx const& tx, SetSignerList::determineOperation(STTx const& tx,
@@ -69,7 +69,7 @@ SetSignerList::determineOperation(STTx const& tx,
return std::make_tuple(tesSUCCESS, quorum, sign, op); return std::make_tuple(tesSUCCESS, quorum, sign, op);
} }
TER NotTEC
SetSignerList::preflight (PreflightContext const& ctx) SetSignerList::preflight (PreflightContext const& ctx)
{ {
if (! ctx.rules.enabled(featureMultiSign)) if (! ctx.rules.enabled(featureMultiSign))
@@ -95,7 +95,7 @@ SetSignerList::preflight (PreflightContext const& ctx)
{ {
// Validate our settings. // Validate our settings.
auto const account = ctx.tx.getAccountID(sfAccount); auto const account = ctx.tx.getAccountID(sfAccount);
TER const ter = NotTEC const ter =
validateQuorumAndSignerEntries(std::get<1>(result), validateQuorumAndSignerEntries(std::get<1>(result),
std::get<2>(result), account, ctx.j); std::get<2>(result), account, ctx.j);
if (ter != tesSUCCESS) if (ter != tesSUCCESS)
@@ -141,7 +141,7 @@ SetSignerList::preCompute()
return Transactor::preCompute(); return Transactor::preCompute();
} }
TER NotTEC
SetSignerList::validateQuorumAndSignerEntries ( SetSignerList::validateQuorumAndSignerEntries (
std::uint32_t quorum, std::uint32_t quorum,
std::vector<SignerEntries::SignerEntry> const& signers, std::vector<SignerEntries::SignerEntry> const& signers,

View File

@@ -61,7 +61,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
@@ -69,14 +69,14 @@ public:
private: private:
static static
std::tuple<TER, std::uint32_t, std::tuple<NotTEC, std::uint32_t,
std::vector<SignerEntries::SignerEntry>, std::vector<SignerEntries::SignerEntry>,
Operation> Operation>
determineOperation(STTx const& tx, determineOperation(STTx const& tx,
ApplyFlags flags, beast::Journal j); ApplyFlags flags, beast::Journal j);
static static
TER validateQuorumAndSignerEntries ( NotTEC validateQuorumAndSignerEntries (
std::uint32_t quorum, std::uint32_t quorum,
std::vector<SignerEntries::SignerEntry> const& signers, std::vector<SignerEntries::SignerEntry> const& signers,
AccountID const& account, AccountID const& account,

View File

@@ -27,7 +27,7 @@
namespace ripple { namespace ripple {
TER NotTEC
SetTrust::preflight (PreflightContext const& ctx) SetTrust::preflight (PreflightContext const& ctx)
{ {
auto const ret = preflight1 (ctx); auto const ret = preflight1 (ctx);

View File

@@ -38,7 +38,7 @@ public:
} }
static static
TER NotTEC
preflight (PreflightContext const& ctx); preflight (PreflightContext const& ctx);
static static

View File

@@ -25,11 +25,11 @@
namespace ripple { namespace ripple {
std::pair<std::vector<SignerEntries::SignerEntry>, TER> std::pair<std::vector<SignerEntries::SignerEntry>, NotTEC>
SignerEntries::deserialize ( SignerEntries::deserialize (
STObject const& obj, beast::Journal journal, std::string const& annotation) STObject const& obj, beast::Journal journal, std::string const& annotation)
{ {
std::pair<std::vector<SignerEntry>, TER> s; std::pair<std::vector<SignerEntry>, NotTEC> s;
if (!obj.isFieldPresent (sfSignerEntries)) if (!obj.isFieldPresent (sfSignerEntries))
{ {

View File

@@ -20,9 +20,10 @@
#ifndef RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED #ifndef RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED
#define RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED #define RIPPLE_TX_IMPL_SIGNER_ENTRIES_H_INCLUDED
#include <ripple/protocol/STTx.h> // STTx::maxMultiSigners #include <ripple/app/tx/impl/Transactor.h> // NotTEC
#include <ripple/protocol/UintTypes.h> // AccountID #include <ripple/protocol/STTx.h> // STTx::maxMultiSigners
#include <ripple/protocol/TER.h> // temMALFORMED #include <ripple/protocol/UintTypes.h> // AccountID
#include <ripple/protocol/TER.h> // temMALFORMED
#include <ripple/beast/utility/Journal.h> // beast::Journal #include <ripple/beast/utility/Journal.h> // beast::Journal
namespace ripple { namespace ripple {
@@ -60,7 +61,7 @@ public:
// Deserialize a SignerEntries array from the network or from the ledger. // Deserialize a SignerEntries array from the network or from the ledger.
static static
std::pair<std::vector<SignerEntry>, TER> std::pair<std::vector<SignerEntry>, NotTEC>
deserialize ( deserialize (
STObject const& obj, STObject const& obj,
beast::Journal journal, beast::Journal journal,

View File

@@ -35,7 +35,7 @@
namespace ripple { namespace ripple {
/** Performs early sanity checks on the txid */ /** Performs early sanity checks on the txid */
TER NotTEC
preflight0(PreflightContext const& ctx) preflight0(PreflightContext const& ctx)
{ {
auto const txID = ctx.tx.getTransactionID(); auto const txID = ctx.tx.getTransactionID();
@@ -51,7 +51,7 @@ preflight0(PreflightContext const& ctx)
} }
/** Performs early sanity checks on the account and fee fields */ /** Performs early sanity checks on the account and fee fields */
TER NotTEC
preflight1 (PreflightContext const& ctx) preflight1 (PreflightContext const& ctx)
{ {
auto const ret = preflight0(ctx); auto const ret = preflight0(ctx);
@@ -85,7 +85,7 @@ preflight1 (PreflightContext const& ctx)
} }
/** Checks whether the signature appears valid */ /** Checks whether the signature appears valid */
TER NotTEC
preflight2 (PreflightContext const& ctx) preflight2 (PreflightContext const& ctx)
{ {
if(!( ctx.flags & tapNO_CHECK_SIGN)) if(!( ctx.flags & tapNO_CHECK_SIGN))
@@ -226,7 +226,7 @@ TER Transactor::payFee ()
return tesSUCCESS; return tesSUCCESS;
} }
TER NotTEC
Transactor::checkSeq (PreclaimContext const& ctx) Transactor::checkSeq (PreclaimContext const& ctx)
{ {
auto const id = ctx.tx.getAccountID(sfAccount); auto const id = ctx.tx.getAccountID(sfAccount);
@@ -327,7 +327,7 @@ TER Transactor::apply ()
return doApply (); return doApply ();
} }
TER NotTEC
Transactor::checkSign (PreclaimContext const& ctx) Transactor::checkSign (PreclaimContext const& ctx)
{ {
// Make sure multisigning is enabled before we check for multisignatures. // Make sure multisigning is enabled before we check for multisignatures.
@@ -341,7 +341,7 @@ Transactor::checkSign (PreclaimContext const& ctx)
return checkSingleSign (ctx); return checkSingleSign (ctx);
} }
TER NotTEC
Transactor::checkSingleSign (PreclaimContext const& ctx) Transactor::checkSingleSign (PreclaimContext const& ctx)
{ {
auto const id = ctx.tx.getAccountID(sfAccount); auto const id = ctx.tx.getAccountID(sfAccount);
@@ -390,7 +390,8 @@ Transactor::checkSingleSign (PreclaimContext const& ctx)
return tesSUCCESS; return tesSUCCESS;
} }
TER Transactor::checkMultiSign (PreclaimContext const& ctx) NotTEC
Transactor::checkMultiSign (PreclaimContext const& ctx)
{ {
auto const id = ctx.tx.getAccountID(sfAccount); auto const id = ctx.tx.getAccountID(sfAccount);
// Get mTxnAccountID's SignerList and Quorum. // Get mTxnAccountID's SignerList and Quorum.

View File

@@ -20,6 +20,7 @@
#ifndef RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED #ifndef RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED
#define RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED #define RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED
#include <ripple/app/tx/applySteps.h>
#include <ripple/app/tx/impl/ApplyContext.h> #include <ripple/app/tx/impl/ApplyContext.h>
#include <ripple/protocol/XRPAmount.h> #include <ripple/protocol/XRPAmount.h>
#include <ripple/beast/utility/Journal.h> #include <ripple/beast/utility/Journal.h>
@@ -116,7 +117,7 @@ public:
*/ */
static static
TER NotTEC
checkSeq (PreclaimContext const& ctx); checkSeq (PreclaimContext const& ctx);
static static
@@ -124,7 +125,7 @@ public:
checkFee (PreclaimContext const& ctx, std::uint64_t baseFee); checkFee (PreclaimContext const& ctx, std::uint64_t baseFee);
static static
TER NotTEC
checkSign (PreclaimContext const& ctx); checkSign (PreclaimContext const& ctx);
// Returns the fee in fee units, not scaled for load. // Returns the fee in fee units, not scaled for load.
@@ -173,20 +174,20 @@ private:
void setSeq (); void setSeq ();
TER payFee (); TER payFee ();
void claimFee (XRPAmount& fee, TER terResult, std::vector<uint256> const& removedOffers); void claimFee (XRPAmount& fee, TER terResult, std::vector<uint256> const& removedOffers);
static TER checkSingleSign (PreclaimContext const& ctx); static NotTEC checkSingleSign (PreclaimContext const& ctx);
static TER checkMultiSign (PreclaimContext const& ctx); static NotTEC checkMultiSign (PreclaimContext const& ctx);
}; };
/** Performs early sanity checks on the txid */ /** Performs early sanity checks on the txid */
TER NotTEC
preflight0(PreflightContext const& ctx); preflight0(PreflightContext const& ctx);
/** Performs early sanity checks on the account and fee fields */ /** Performs early sanity checks on the account and fee fields */
TER NotTEC
preflight1 (PreflightContext const& ctx); preflight1 (PreflightContext const& ctx);
/** Checks whether the signature appears valid */ /** Checks whether the signature appears valid */
TER NotTEC
preflight2 (PreflightContext const& ctx); preflight2 (PreflightContext const& ctx);
} }

View File

@@ -38,7 +38,7 @@
namespace ripple { namespace ripple {
static static
TER NotTEC
invoke_preflight (PreflightContext const& ctx) invoke_preflight (PreflightContext const& ctx)
{ {
switch(ctx.tx.getTxnType()) switch(ctx.tx.getTxnType())

View File

@@ -87,7 +87,7 @@ public:
} }
TER getResultTER () const TER getResultTER () const
{ {
return static_cast<TER> (mResult); return TER::fromInt (mResult);
} }
std::uint32_t getIndex () const std::uint32_t getIndex () const
{ {

View File

@@ -257,7 +257,7 @@ STObject TxMeta::getAsObject () const
void TxMeta::addRaw (Serializer& s, TER result, std::uint32_t index) void TxMeta::addRaw (Serializer& s, TER result, std::uint32_t index)
{ {
mResult = static_cast<int> (result); mResult = TERtoInt (result);
mIndex = index; mIndex = index;
assert ((mResult == 0) || ((mResult > 100) && (mResult <= 255))); assert ((mResult == 0) || ((mResult > 100) && (mResult <= 255)));

View File

@@ -1581,8 +1581,8 @@ accountSend (ApplyView& view,
// VFALCO Its laborious to have to mutate the // VFALCO Its laborious to have to mutate the
// TER based on params everywhere // TER based on params everywhere
terResult = view.open() terResult = view.open()
? telFAILED_PROCESSING ? TER {telFAILED_PROCESSING}
: tecFAILED_PROCESSING; : TER {tecFAILED_PROCESSING};
} }
else else
{ {
@@ -1847,8 +1847,8 @@ transferXRP (ApplyView& view,
// mutating these TER everywhere // mutating these TER everywhere
// FIXME: this logic should be moved to callers maybe? // FIXME: this logic should be moved to callers maybe?
return view.open() return view.open()
? telFAILED_PROCESSING ? TER {telFAILED_PROCESSING}
: tecFAILED_PROCESSING; : TER {tecFAILED_PROCESSING};
} }
// Decrement XRP balance. // Decrement XRP balance.

View File

@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
/* /*
This file is part of rippled: https://github.com/ripple/rippled This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc. Copyright (c) 2012 - 2018 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@@ -20,7 +20,10 @@
#ifndef RIPPLE_PROTOCOL_TER_H_INCLUDED #ifndef RIPPLE_PROTOCOL_TER_H_INCLUDED
#define RIPPLE_PROTOCOL_TER_H_INCLUDED #define RIPPLE_PROTOCOL_TER_H_INCLUDED
#include <ripple/json/json_value.h>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <ostream>
#include <string> #include <string>
namespace ripple { namespace ripple {
@@ -30,9 +33,13 @@ namespace ripple {
// "Transaction Engine Result" // "Transaction Engine Result"
// or Transaction ERror. // or Transaction ERror.
// //
enum TER using TERUnderlyingType = int;
//------------------------------------------------------------------------------
enum TELcodes : TERUnderlyingType
{ {
// Note: Range is stable. Exact numbers are currently unstable. Use tokens. // Note: Range is stable. Exact numbers are unstable. Use tokens.
// -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit) // -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit)
// Only valid during non-consensus processing. // Only valid during non-consensus processing.
@@ -51,7 +58,14 @@ enum TER
telCAN_NOT_QUEUE_BLOCKS, telCAN_NOT_QUEUE_BLOCKS,
telCAN_NOT_QUEUE_BLOCKED, telCAN_NOT_QUEUE_BLOCKED,
telCAN_NOT_QUEUE_FEE, telCAN_NOT_QUEUE_FEE,
telCAN_NOT_QUEUE_FULL, telCAN_NOT_QUEUE_FULL
};
//------------------------------------------------------------------------------
enum TEMcodes : TERUnderlyingType
{
// Note: Range is stable. Exact numbers are unstable. Use tokens.
// -299 .. -200: M Malformed (bad signature) // -299 .. -200: M Malformed (bad signature)
// Causes: // Causes:
@@ -95,7 +109,14 @@ enum TER
// An intermediate result used internally, should never be returned. // An intermediate result used internally, should never be returned.
temUNCERTAIN, temUNCERTAIN,
temUNKNOWN, temUNKNOWN
};
//------------------------------------------------------------------------------
enum TEFcodes : TERUnderlyingType
{
// Note: Range is stable. Exact numbers are unstable. Use tokens.
// -199 .. -100: F // -199 .. -100: F
// Failure (sequence number previously used) // Failure (sequence number previously used)
@@ -127,6 +148,13 @@ enum TER
tefNOT_MULTI_SIGNING, tefNOT_MULTI_SIGNING,
tefBAD_AUTH_MASTER, tefBAD_AUTH_MASTER,
tefINVARIANT_FAILED, tefINVARIANT_FAILED,
};
//------------------------------------------------------------------------------
enum TERcodes : TERUnderlyingType
{
// Note: Range is stable. Exact numbers are unstable. Use tokens.
// -99 .. -1: R Retry // -99 .. -1: R Retry
// sequence too high, no funds for txn fee, originating -account // sequence too high, no funds for txn fee, originating -account
@@ -155,7 +183,15 @@ enum TER
// burden network. // burden network.
terLAST, // Process after all other transactions terLAST, // Process after all other transactions
terNO_RIPPLE, // Rippling not allowed terNO_RIPPLE, // Rippling not allowed
terQUEUED, // Transaction is being held in TxQ until fee drops terQUEUED // Transaction is being held in TxQ until fee drops
};
//------------------------------------------------------------------------------
enum TEScodes : TERUnderlyingType
{
// Note: Exact number must stay stable. This code is stored by value
// in metadata for historic transactions.
// 0: S Success (success) // 0: S Success (success)
// Causes: // Causes:
@@ -163,7 +199,15 @@ enum TER
// Implications: // Implications:
// - Applied // - Applied
// - Forwarded // - Forwarded
tesSUCCESS = 0, tesSUCCESS = 0
};
//------------------------------------------------------------------------------
enum TECcodes : TERUnderlyingType
{
// Note: Exact numbers must stay stable. These codes are stored by
// value in metadata for historic transactions.
// 100 .. 159 C // 100 .. 159 C
// Claim fee only (ripple transaction with no good paths, pay to // Claim fee only (ripple transaction with no good paths, pay to
@@ -218,6 +262,201 @@ enum TER
tecEXPIRED = 148 tecEXPIRED = 148
}; };
//------------------------------------------------------------------------------
// For generic purposes, a free function that returns the value of a TE*codes.
constexpr TERUnderlyingType TERtoInt (TELcodes v)
{ return static_cast<TERUnderlyingType>(v); }
constexpr TERUnderlyingType TERtoInt (TEMcodes v)
{ return static_cast<TERUnderlyingType>(v); }
constexpr TERUnderlyingType TERtoInt (TEFcodes v)
{ return static_cast<TERUnderlyingType>(v); }
constexpr TERUnderlyingType TERtoInt (TERcodes v)
{ return static_cast<TERUnderlyingType>(v); }
constexpr TERUnderlyingType TERtoInt (TEScodes v)
{ return static_cast<TERUnderlyingType>(v); }
constexpr TERUnderlyingType TERtoInt (TECcodes v)
{ return static_cast<TERUnderlyingType>(v); }
//------------------------------------------------------------------------------
// Template class that is specific to selected ranges of error codes. The
// Trait tells std::enable_if which ranges are allowed.
template <template<typename> class Trait>
class TERSubset
{
TERUnderlyingType code_;
public:
// Constructors
constexpr TERSubset() : code_ (tesSUCCESS) { }
constexpr TERSubset (TERSubset const& rhs) = default;
constexpr TERSubset (TERSubset&& rhs) = default;
private:
constexpr explicit TERSubset (int rhs) : code_ (rhs) { }
public:
static constexpr TERSubset fromInt (int from)
{
return TERSubset (from);
}
// Trait tells enable_if which types are allowed for construction.
template <typename T, typename = std::enable_if_t<Trait<T>::value>>
constexpr TERSubset (T rhs)
: code_ (TERtoInt (rhs))
{ }
// Assignment
constexpr TERSubset& operator=(TERSubset const& rhs) = default;
constexpr TERSubset& operator=(TERSubset&& rhs) = default;
// Trait tells enable_if which types are allowed for assignment.
template <typename T>
constexpr auto
operator= (T rhs) -> std::enable_if_t<Trait<T>::value, TERSubset&>
{
code_ = TERtoInt (rhs);
return *this;
}
// Conversion to bool.
explicit operator bool() const
{
return code_ != tesSUCCESS;
}
// Conversion to Json::Value allows assignment to Json::Objects
// without casting.
operator Json::Value() const
{
return Json::Value {code_};
}
// Streaming operator.
friend std::ostream& operator<< (std::ostream& os, TERSubset const& rhs)
{
return os << rhs.code_;
}
// Return the underlying value. Not a member so similarly named free
// functions can do the same work for the enums.
//
// It's worth noting that an explicit conversion operator was considered
// and rejected. Consider this case, taken from Status.h
//
// class Status {
// int code_;
// public:
// Status (TER ter)
// : code_ (ter) {}
// }
//
// This code compiles with no errors or warnings if TER has an explicit
// (unnamed) conversion to int. To avoid silent conversions like these
// we provide (only) a named conversion.
friend constexpr TERUnderlyingType TERtoInt (TERSubset v)
{
return v.code_;
}
};
// Comparison operators.
// Only enabled if both arguments return int if TERtiInt is called with them.
template <typename L, typename R>
constexpr auto
operator== (L const& lhs, R const& rhs) -> std::enable_if_t<
std::is_same<decltype (TERtoInt(lhs)), int>::value &&
std::is_same<decltype (TERtoInt(rhs)), int>::value, bool>
{
return TERtoInt(lhs) == TERtoInt(rhs);
}
template <typename L, typename R>
constexpr auto
operator!= (L const& lhs, R const& rhs) -> std::enable_if_t<
std::is_same<decltype (TERtoInt(lhs)), int>::value &&
std::is_same<decltype (TERtoInt(rhs)), int>::value, bool>
{
return TERtoInt(lhs) != TERtoInt(rhs);
}
template <typename L, typename R>
constexpr auto
operator< (L const& lhs, R const& rhs) -> std::enable_if_t<
std::is_same<decltype (TERtoInt(lhs)), int>::value &&
std::is_same<decltype (TERtoInt(rhs)), int>::value, bool>
{
return TERtoInt(lhs) < TERtoInt(rhs);
}
template <typename L, typename R>
constexpr auto
operator<= (L const& lhs, R const& rhs) -> std::enable_if_t<
std::is_same<decltype (TERtoInt(lhs)), int>::value &&
std::is_same<decltype (TERtoInt(rhs)), int>::value, bool>
{
return TERtoInt(lhs) <= TERtoInt(rhs);
}
template <typename L, typename R>
constexpr auto
operator> (L const& lhs, R const& rhs) -> std::enable_if_t<
std::is_same<decltype (TERtoInt(lhs)), int>::value &&
std::is_same<decltype (TERtoInt(rhs)), int>::value, bool>
{
return TERtoInt(lhs) > TERtoInt(rhs);
}
template <typename L, typename R>
constexpr auto
operator>= (L const& lhs, R const& rhs) -> std::enable_if_t<
std::is_same<decltype (TERtoInt(lhs)), int>::value &&
std::is_same<decltype (TERtoInt(rhs)), int>::value, bool>
{
return TERtoInt(lhs) >= TERtoInt(rhs);
}
//------------------------------------------------------------------------------
// Use traits to build a TERSubset that can convert from any of the TE*codes
// enums *except* TECcodes: NotTEC
// NOTE: NotTEC is useful for codes returned by preflight in transactors.
// Preflight checks occur prior to signature checking. If preflight returned
// a tec code, then a malicious user could submit a transaction with a very
// large fee and have that fee charged against an account without using that
// account's valid signature.
template <typename FROM> class CanCvtToNotTEC : public std::false_type {};
template <> class CanCvtToNotTEC<TELcodes> : public std::true_type {};
template <> class CanCvtToNotTEC<TEMcodes> : public std::true_type {};
template <> class CanCvtToNotTEC<TEFcodes> : public std::true_type {};
template <> class CanCvtToNotTEC<TERcodes> : public std::true_type {};
template <> class CanCvtToNotTEC<TEScodes> : public std::true_type {};
using NotTEC = TERSubset<CanCvtToNotTEC>;
//------------------------------------------------------------------------------
// Use traits to build a TERSubset that can convert from any of the TE*codes
// enums as well as from NotTEC.
template <typename FROM> class CanCvtToTER : public std::false_type {};
template <> class CanCvtToTER<TELcodes> : public std::true_type {};
template <> class CanCvtToTER<TEMcodes> : public std::true_type {};
template <> class CanCvtToTER<TEFcodes> : public std::true_type {};
template <> class CanCvtToTER<TERcodes> : public std::true_type {};
template <> class CanCvtToTER<TEScodes> : public std::true_type {};
template <> class CanCvtToTER<TECcodes> : public std::true_type {};
template <> class CanCvtToTER<NotTEC> : public std::true_type {};
// TER allows all of the subsets.
using TER = TERSubset<CanCvtToTER>;
//------------------------------------------------------------------------------
inline bool isTelLocal(TER x) inline bool isTelLocal(TER x)
{ {
return ((x) >= telLOCAL_ERROR && (x) < temMALFORMED); return ((x) >= telLOCAL_ERROR && (x) < temMALFORMED);
@@ -248,20 +487,15 @@ inline bool isTecClaim(TER x)
return ((x) >= tecCLAIM); return ((x) >= tecCLAIM);
} }
// VFALCO TODO group these into a shell class along with the defines above.
extern
bool bool
transResultInfo (TER code, std::string& token, std::string& text); transResultInfo (TER code, std::string& token, std::string& text);
extern
std::string std::string
transToken (TER code); transToken (TER code);
extern
std::string std::string
transHuman (TER code); transHuman (TER code);
extern
boost::optional<TER> boost::optional<TER>
transCode(std::string const& token); transCode(std::string const& token);

View File

@@ -48,7 +48,7 @@ STUInt8::getText () const
{ {
std::string token, human; std::string token, human;
if (transResultInfo (static_cast<TER> (value_), token, human)) if (transResultInfo (TER::fromInt (value_), token, human))
return human; return human;
JLOG (debugLog().error()) JLOG (debugLog().error())
@@ -66,7 +66,7 @@ STUInt8::getJson (int) const
{ {
std::string token, human; std::string token, human;
if (transResultInfo (static_cast<TER> (value_), token, human)) if (transResultInfo (TER::fromInt (value_), token, human))
return token; return token;
JLOG (debugLog().error()) JLOG (debugLog().error())

View File

@@ -202,14 +202,16 @@ static boost::optional<detail::STVar> parseLeaf (
{ {
auto ter = transCode(strValue); auto ter = transCode(strValue);
if (!ter || *ter < minValue || *ter > maxValue) if (!ter ||
TERtoInt (*ter) < minValue ||
TERtoInt (*ter) > maxValue)
{ {
error = out_of_range(json_name, fieldName); error = out_of_range(json_name, fieldName);
return ret; return ret;
} }
ret = detail::make_stvar<STUInt8>(field, ret = detail::make_stvar<STUInt8>(field,
static_cast<std::uint8_t>(*ter)); static_cast<std::uint8_t>(TERtoInt (*ter)));
} }
else else
{ {

View File

@@ -28,13 +28,13 @@ namespace detail {
static static
std::unordered_map< std::unordered_map<
std::underlying_type_t<TER>, TERUnderlyingType,
std::pair<char const* const, char const* const>> const& std::pair<char const* const, char const* const>> const&
transResults() transResults()
{ {
static static
std::unordered_map< std::unordered_map<
std::underlying_type_t<TER>, TERUnderlyingType,
std::pair<char const* const, char const* const>> const std::pair<char const* const, char const* const>> const
results results
{ {
@@ -162,8 +162,7 @@ bool transResultInfo (TER code, std::string& token, std::string& text)
{ {
auto& results = detail::transResults(); auto& results = detail::transResults();
auto const r = results.find ( auto const r = results.find (TERtoInt (code));
static_cast<std::underlying_type_t<TER>> (code));
if (r == results.end()) if (r == results.end())
return false; return false;
@@ -207,7 +206,7 @@ transCode(std::string const& token)
); );
std::unordered_map< std::unordered_map<
std::string, std::string,
std::underlying_type_t<TER>> const TERUnderlyingType> const
byToken(tRange.begin(), tRange.end()); byToken(tRange.begin(), tRange.end());
return byToken; return byToken;
}(); }();
@@ -217,7 +216,7 @@ transCode(std::string const& token)
if (r == results.end()) if (r == results.end())
return boost::none; return boost::none;
return static_cast<TER>(r->second); return TER::fromInt (r->second);
} }
} // ripple } // ripple

View File

@@ -47,23 +47,26 @@ public:
Status () = default; Status () = default;
Status (Code code, Strings d = {}) // The enable_if allows only integers (not enums). Prevents enum narrowing.
: type_ (Type::none), code_ (code), messages_ (std::move (d)) template <typename T,
typename = std::enable_if_t<std::is_integral<T>::value>>
Status (T code, Strings d = {})
: type_ (Type::none), code_ (code), messages_ (std::move (d))
{ {
} }
Status (TER ter, Strings d = {}) Status (TER ter, Strings d = {})
: type_ (Type::TER), code_ (ter), messages_ (std::move (d)) : type_ (Type::TER), code_ (TERtoInt (ter)), messages_ (std::move (d))
{ {
} }
Status (error_code_i e, Strings d = {}) Status (error_code_i e, Strings d = {})
: type_ (Type::error_code_i), code_ (e), messages_ (std::move (d)) : type_ (Type::error_code_i), code_ (e), messages_ (std::move (d))
{ {
} }
Status (error_code_i e, std::string const& s) Status (error_code_i e, std::string const& s)
: type_ (Type::error_code_i), code_ (e), messages_ ({s}) : type_ (Type::error_code_i), code_ (e), messages_ ({s})
{ {
} }
@@ -89,7 +92,7 @@ public:
TER toTER () const TER toTER () const
{ {
assert (type_ == Type::TER); assert (type_ == Type::TER);
return TER (code_); return TER::fromInt (code_);
} }
/** Returns the Status as an error_code_i. /** Returns the Status as an error_code_i.

View File

@@ -316,8 +316,9 @@ struct DepositAuth_test : public beast::unit_test::suite
if (withDepositAuth) if (withDepositAuth)
env(fset(gw1, asfDepositAuth)); env(fset(gw1, asfDepositAuth));
auto const result = TER const result = (noRippleNext && noRipplePrev)
(noRippleNext && noRipplePrev) ? tecPATH_DRY : tesSUCCESS; ? TER {tecPATH_DRY}
: TER {tesSUCCESS};
env (pay (alice, bob, USD1(10)), path (gw1), ter (result)); env (pay (alice, bob, USD1(10)), path (gw1), ter (result));
}; };
@@ -338,8 +339,9 @@ struct DepositAuth_test : public beast::unit_test::suite
if (withDepositAuth) if (withDepositAuth)
env(fset(alice, asfDepositAuth)); env(fset(alice, asfDepositAuth));
auto const result = TER const result = (noRippleNext && noRipplePrev)
(noRippleNext && noRipplePrev) ? tecPATH_DRY : tesSUCCESS; ? TER {tecPATH_DRY}
: TER {tesSUCCESS};
env (pay (gw1, gw2, USD2 (10)), env (pay (gw1, gw2, USD2 (10)),
path (alice), sendmax (USD1 (10)), ter (result)); path (alice), sendmax (USD1 (10)), ter (result));
}; };

View File

@@ -1028,7 +1028,8 @@ struct Escrow_test : public beast::unit_test::suite
auto const seq = env.seq(alice); auto const seq = env.seq(alice);
env(escrow(alice, carol, XRP(1000)), finish_time(env.now() + 1s)); env(escrow(alice, carol, XRP(1000)), finish_time(env.now() + 1s));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
auto const escrow = env.le(keylet::escrow(alice.id(), seq)); auto const escrow = env.le(keylet::escrow(alice.id(), seq));
BEAST_EXPECT(escrow); BEAST_EXPECT(escrow);
@@ -1050,7 +1051,8 @@ struct Escrow_test : public beast::unit_test::suite
env(escrow(alice, alice, XRP(1000)), env(escrow(alice, alice, XRP(1000)),
finish_time(env.now() + 1s), cancel_time(env.now() + 500s)); finish_time(env.now() + 1s), cancel_time(env.now() + 500s));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
env.close(5s); env.close(5s);
auto const aa = env.le(keylet::escrow(alice.id(), aseq)); auto const aa = env.le(keylet::escrow(alice.id(), aseq));
BEAST_EXPECT(aa); BEAST_EXPECT(aa);
@@ -1063,7 +1065,8 @@ struct Escrow_test : public beast::unit_test::suite
env(escrow(bruce, bruce, XRP(1000)), env(escrow(bruce, bruce, XRP(1000)),
finish_time(env.now() + 1s), cancel_time(env.now() + 2s)); finish_time(env.now() + 1s), cancel_time(env.now() + 2s));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
env.close(5s); env.close(5s);
auto const bb = env.le(keylet::escrow(bruce.id(), bseq)); auto const bb = env.le(keylet::escrow(bruce.id(), bseq));
BEAST_EXPECT(bb); BEAST_EXPECT(bb);
@@ -1078,7 +1081,8 @@ struct Escrow_test : public beast::unit_test::suite
env(finish(alice, alice, aseq)); env(finish(alice, alice, aseq));
{ {
BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq))); BEAST_EXPECT(!env.le(keylet::escrow(alice.id(), aseq)));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id())); ripple::Dir aod(*env.current(), keylet::ownerDir(alice.id()));
BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0); BEAST_EXPECT(std::distance(aod.begin(), aod.end()) == 0);
@@ -1093,7 +1097,8 @@ struct Escrow_test : public beast::unit_test::suite
env(cancel(bruce, bruce, bseq)); env(cancel(bruce, bruce, bseq));
{ {
BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq))); BEAST_EXPECT(!env.le(keylet::escrow(bruce.id(), bseq)));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id())); ripple::Dir bod(*env.current(), keylet::ownerDir(bruce.id()));
BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0); BEAST_EXPECT(std::distance(bod.begin(), bod.end()) == 0);
@@ -1109,11 +1114,13 @@ struct Escrow_test : public beast::unit_test::suite
auto const bseq = env.seq(bruce); auto const bseq = env.seq(bruce);
env(escrow(alice, bruce, XRP(1000)), finish_time(env.now() + 1s)); env(escrow(alice, bruce, XRP(1000)), finish_time(env.now() + 1s));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
env.close(5s); env.close(5s);
env(escrow(bruce, carol, XRP(1000)), env(escrow(bruce, carol, XRP(1000)),
finish_time(env.now() + 1s), cancel_time(env.now() + 2s)); finish_time(env.now() + 1s), cancel_time(env.now() + 2s));
BEAST_EXPECT((*env.meta())[sfTransactionResult] == tesSUCCESS); BEAST_EXPECT((*env.meta())[sfTransactionResult] ==
static_cast<std::uint8_t>(tesSUCCESS));
env.close(5s); env.close(5s);
auto const ab = env.le(keylet::escrow(alice.id(), aseq)); auto const ab = env.le(keylet::escrow(alice.id(), aseq));

View File

@@ -753,8 +753,9 @@ struct Flow_test : public beast::unit_test::suite
env (offer (bob, XRP (50), USD (50))); env (offer (bob, XRP (50), USD (50)));
env (offer (bob, XRP (100), USD (50))); env (offer (bob, XRP (100), USD (50)));
auto expectedResult = TER const expectedResult = closeTime < fix1141Time ()
closeTime < fix1141Time () ? tecPATH_DRY : tesSUCCESS; ? TER {tecPATH_DRY}
: TER {tesSUCCESS};
env (pay (alice, carol, USD (100)), path (~USD), sendmax (XRP (100)), env (pay (alice, carol, USD (100)), path (~USD), sendmax (XRP (100)),
txflags (tfNoRippleDirect | tfPartialPayment | tfLimitQuality), txflags (tfNoRippleDirect | tfPartialPayment | tfLimitQuality),
ter (expectedResult)); ter (expectedResult));
@@ -1131,7 +1132,7 @@ struct Flow_test : public beast::unit_test::suite
env(pay(alice, alice, XRP(1)), path(gw, bob, ~XRP), env(pay(alice, alice, XRP(1)), path(gw, bob, ~XRP),
sendmax(gw["USD"](1000)), txflags(tfNoRippleDirect), sendmax(gw["USD"](1000)), txflags(tfNoRippleDirect),
ter(withFix ? tecPATH_DRY : tesSUCCESS)); ter(withFix ? TER {tecPATH_DRY} : TER {tesSUCCESS}));
env.close(); env.close();
if (withFix) if (withFix)
@@ -1145,7 +1146,7 @@ struct Flow_test : public beast::unit_test::suite
env(pay (carol, carol, gw["USD"](1000)), path(~bob["USD"], gw), env(pay (carol, carol, gw["USD"](1000)), path(~bob["USD"], gw),
sendmax(XRP(100000)), txflags(tfNoRippleDirect), sendmax(XRP(100000)), txflags(tfNoRippleDirect),
ter(withFix ? tecPATH_DRY : tesSUCCESS)); ter(withFix ? TER {tecPATH_DRY} : TER {tesSUCCESS}));
env.close(); env.close();
pass(); pass();
@@ -1189,7 +1190,7 @@ struct Flow_test : public beast::unit_test::suite
env(pay(alice, alice, USD(1000)), path(~bob["USD"], bob, gw), env(pay(alice, alice, USD(1000)), path(~bob["USD"], bob, gw),
sendmax(XRP(1)), txflags(tfNoRippleDirect), sendmax(XRP(1)), txflags(tfNoRippleDirect),
ter(withFix ? tecPATH_DRY : tesSUCCESS)); ter(withFix ? TER {tecPATH_DRY} : TER {tesSUCCESS}));
env.close(); env.close();
} }

View File

@@ -239,7 +239,7 @@ public:
env (offer (alice, XRP (5), USD (2)), env (offer (alice, XRP (5), USD (2)),
json (sfExpiration.fieldName, lastClose(env)), json (sfExpiration.fieldName, lastClose(env)),
json (jss::OfferSequence, offer2Seq), json (jss::OfferSequence, offer2Seq),
ter (featChecks ? tecEXPIRED : tesSUCCESS)); ter (featChecks ? TER {tecEXPIRED} : TER {tesSUCCESS}));
env.close(); env.close();
env.require (offers (alice, 2)); env.require (offers (alice, 2));
@@ -289,8 +289,8 @@ public:
(hasFeature(env, featureFeeEscalation) && (hasFeature(env, featureFeeEscalation) &&
!hasFeature(env, fix1513)); !hasFeature(env, fix1513));
// Will fail without the underflow fix // Will fail without the underflow fix
auto expectedResult = *stAmountCalcSwitchover ? TER const expectedResult = *stAmountCalcSwitchover ?
tesSUCCESS : tecPATH_PARTIAL; TER {tesSUCCESS} : TER {tecPATH_PARTIAL};
env (pay (alice, bob, EUR (epsilon)), path (~EUR), env (pay (alice, bob, EUR (epsilon)), path (~EUR),
sendmax (USD (100)), ter (expectedResult)); sendmax (USD (100)), ter (expectedResult));
} }
@@ -959,7 +959,7 @@ public:
env (offer (alice, xrpOffer, usdOffer), env (offer (alice, xrpOffer, usdOffer),
json (sfExpiration.fieldName, lastClose(env)), json (sfExpiration.fieldName, lastClose(env)),
ter (featChecks ? tecEXPIRED : tesSUCCESS)); ter (featChecks ? TER {tecEXPIRED} : TER {tesSUCCESS}));
env.require ( env.require (
balance (alice, startBalance - f - f), balance (alice, startBalance - f - f),
@@ -3422,7 +3422,7 @@ public:
// Determine which TEC code we expect. // Determine which TEC code we expect.
TER const tecExpect = TER const tecExpect =
features[featureFlow] ? temBAD_PATH : tecPATH_DRY; features[featureFlow] ? TER {temBAD_PATH} : TER {tecPATH_DRY};
// This payment caused the assert. // This payment caused the assert.
env (pay (ann, ann, D_BUX(30)), env (pay (ann, ann, D_BUX(30)),
@@ -4397,7 +4397,7 @@ public:
// create an offer to buy their own currency. After FlowCross // create an offer to buy their own currency. After FlowCross
// they can. // they can.
env (offer (gw, gwUSD(40), XRP(4000)), env (offer (gw, gwUSD(40), XRP(4000)),
ter (flowCross ? tesSUCCESS : tecNO_LINE)); ter (flowCross ? TER {tesSUCCESS} : TER {tecNO_LINE}));
env.close(); env.close();
env.require (offers (gw, flowCross ? 1 : 0)); env.require (offers (gw, flowCross ? 1 : 0));

View File

@@ -1375,7 +1375,7 @@ struct PayStrand_test : public beast::unit_test::suite
env(offer(bob, XRP(100), USD(100)), txflags(tfPassive)); env(offer(bob, XRP(100), USD(100)), txflags(tfPassive));
env(offer(bob, USD(100), XRP(100)), txflags(tfPassive)); env(offer(bob, USD(100), XRP(100)), txflags(tfPassive));
auto const expectedResult = [&] { auto const expectedResult = [&] () -> TER {
if (features[featureFlow] && !features[fix1373]) if (features[featureFlow] && !features[fix1373])
return tesSUCCESS; return tesSUCCESS;
return temBAD_PATH_LOOP; return temBAD_PATH_LOOP;

View File

@@ -42,7 +42,7 @@ struct JTx
{ {
Json::Value jv; Json::Value jv;
requires_t requires; requires_t requires;
boost::optional<TER> ter = tesSUCCESS; boost::optional<TER> ter = TER {tesSUCCESS};
bool fill_fee = true; bool fill_fee = true;
bool fill_seq = true; bool fill_seq = true;
bool fill_sig = true; bool fill_sig = true;

View File

@@ -278,8 +278,7 @@ Env::parseResult(Json::Value const& jr)
TER ter; TER ter;
if (jr.isObject() && jr.isMember(jss::result) && if (jr.isObject() && jr.isMember(jss::result) &&
jr[jss::result].isMember(jss::engine_result_code)) jr[jss::result].isMember(jss::engine_result_code))
ter = static_cast<TER>( ter = TER::fromInt (jr[jss::result][jss::engine_result_code].asInt());
jr[jss::result][jss::engine_result_code].asInt());
else else
ter = temINVALID; ter = temINVALID;
return std::make_pair(ter, return std::make_pair(ter,

View File

@@ -94,15 +94,16 @@ class Invariants_test : public beast::unit_test::suite
BEAST_EXPECT(precheck(A1, A2, ac)); BEAST_EXPECT(precheck(A1, A2, ac));
auto tr = tesSUCCESS; TER tr = tesSUCCESS;
// invoke check twice to cover tec and tef cases // invoke check twice to cover tec and tef cases
for (auto i : {0,1}) for (auto i : {0,1})
{ {
tr = ac.checkInvariants(tr); tr = ac.checkInvariants(tr);
if (enabled) if (enabled)
{ {
BEAST_EXPECT( BEAST_EXPECT(tr == (i == 0
tr == (i == 0 ? tecINVARIANT_FAILED : tefINVARIANT_FAILED)); ? TER {tecINVARIANT_FAILED}
: TER {tefINVARIANT_FAILED}));
BEAST_EXPECT( BEAST_EXPECT(
boost::starts_with(sink.strm_.str(), "Invariant failed:") || boost::starts_with(sink.strm_.str(), "Invariant failed:") ||
boost::starts_with(sink.strm_.str(), boost::starts_with(sink.strm_.str(),

View File

@@ -20,6 +20,9 @@
#include <ripple/protocol/TER.h> #include <ripple/protocol/TER.h>
#include <ripple/beast/unit_test.h> #include <ripple/beast/unit_test.h>
#include <tuple>
#include <type_traits>
namespace ripple { namespace ripple {
struct TER_test : public beast::unit_test::suite struct TER_test : public beast::unit_test::suite
@@ -29,7 +32,7 @@ struct TER_test : public beast::unit_test::suite
{ {
for (auto i = -400; i < 400; ++i) for (auto i = -400; i < 400; ++i)
{ {
TER t = static_cast<TER>(i); TER t = TER::fromInt (i);
auto inRange = isTelLocal(t) || auto inRange = isTelLocal(t) ||
isTemMalformed(t) || isTemMalformed(t) ||
isTefFailure(t) || isTefFailure(t) ||
@@ -49,13 +52,214 @@ struct TER_test : public beast::unit_test::suite
} }
} }
// Helper template that makes sure two types are not convertible or
// assignable if not the same.
// o I1 one tuple index.
// o I2 other tuple index.
// o Tup is expected to be a tuple.
// It's a functor, rather than a function template, since a class template
// can be a template argument without being full specified.
template<std::size_t I1, std::size_t I2>
class NotConvertible
{
public:
template<typename Tup>
void operator()(Tup const& tup, beast::unit_test::suite&) const
{
// Entries in the tuple should not be convertible or assignable
// unless they are the same types.
using To_t = std::decay_t<decltype (std::get<I1>(tup))>;
using From_t = std::decay_t<decltype (std::get<I2>(tup))>;
static_assert (std::is_same<From_t, To_t>::value ==
std::is_convertible<From_t, To_t>::value, "Convert err");
static_assert (std::is_same<To_t, From_t>::value ==
std::is_constructible<To_t, From_t>::value, "Construct err");
static_assert (std::is_same <To_t, From_t>::value ==
std::is_assignable<To_t&, From_t const&>::value, "Assign err");
// Assignment or conversion from integer to type should never work.
static_assert (
! std::is_convertible<int, To_t>::value, "Convert err");
static_assert (
! std::is_constructible<To_t, int>::value, "Construct err");
static_assert (
! std::is_assignable<To_t&, int const&>::value, "Assign err");
}
};
// Fast iteration over the tuple.
template<std::size_t I1, std::size_t I2,
template<std::size_t, std::size_t> class Func, typename Tup>
std::enable_if_t<I1 != 0>
testIterate (Tup const& tup, beast::unit_test::suite& suite)
{
Func<I1, I2> func;
func (tup, suite);
testIterate<I1 - 1, I2, Func>(tup, suite);
}
// Slow iteration over the tuple.
template<std::size_t I1, std::size_t I2,
template<std::size_t, std::size_t> class Func, typename Tup>
std::enable_if_t<I1 == 0 && I2 != 0>
testIterate (Tup const& tup, beast::unit_test::suite& suite)
{
Func<I1, I2> func;
func (tup, suite);
testIterate<std::tuple_size<Tup>::value - 1, I2 - 1, Func>(tup, suite);
}
// Finish iteration over the tuple.
template<std::size_t I1, std::size_t I2,
template<std::size_t, std::size_t> class Func, typename Tup>
std::enable_if_t<I1 == 0 && I2 == 0>
testIterate (Tup const& tup, beast::unit_test::suite& suite)
{
Func<I1, I2> func;
func (tup, suite);
}
void testConversion()
{
// Verify that valid conversions are valid and invalid conversions
// are not valid.
// Examples of each kind of enum.
static auto const terEnums = std::make_tuple (telLOCAL_ERROR,
temMALFORMED, tefFAILURE, terRETRY, tesSUCCESS, tecCLAIM);
static const int hiIndex {
std::tuple_size<decltype (terEnums)>::value - 1};
// Verify that enums cannot be converted to other enum types.
testIterate<hiIndex, hiIndex, NotConvertible> (terEnums, *this);
// Lambda that verifies assignability and convertibility.
auto isConvertable = [] (auto from, auto to)
{
using From_t = std::decay_t<decltype (from)>;
using To_t = std::decay_t<decltype (to)>;
static_assert (
std::is_convertible<From_t, To_t>::value, "Convert err");
static_assert (
std::is_constructible<To_t, From_t>::value, "Construct err");
static_assert (
std::is_assignable<To_t&, From_t const&>::value, "Assign err");
};
// Verify the right types convert to NotTEC.
NotTEC const notTec;
isConvertable (telLOCAL_ERROR, notTec);
isConvertable (temMALFORMED, notTec);
isConvertable (tefFAILURE, notTec);
isConvertable (terRETRY, notTec);
isConvertable (tesSUCCESS, notTec);
isConvertable (notTec, notTec);
// Lambda that verifies types and not assignable or convertible.
auto notConvertible = [] (auto from, auto to)
{
using To_t = std::decay_t<decltype (to)>;
using From_t = std::decay_t<decltype (from)>;
static_assert (
!std::is_convertible<From_t, To_t>::value, "Convert err");
static_assert (
!std::is_constructible<To_t, From_t>::value, "Construct err");
static_assert (
!std::is_assignable<To_t&, From_t const&>::value, "Assign err");
};
// Verify types that shouldn't convert to NotTEC.
TER const ter;
notConvertible (tecCLAIM, notTec);
notConvertible (ter, notTec);
notConvertible (4, notTec);
// Verify the right types convert to TER.
isConvertable (telLOCAL_ERROR, ter);
isConvertable (temMALFORMED, ter);
isConvertable (tefFAILURE, ter);
isConvertable (terRETRY, ter);
isConvertable (tesSUCCESS, ter);
isConvertable (tecCLAIM, ter);
isConvertable (notTec, ter);
isConvertable (ter, ter);
// Verify that you can't convert from int to ter.
notConvertible (4, ter);
}
// Helper template that makes sure two types are comparable. Also
// verifies that one of the types does not compare to int.
// o I1 one tuple index.
// o I2 other tuple index.
// o Tup is expected to be a tuple.
// It's a functor, rather than a function template, since a class template
// can be a template argument without being full specified.
template<std::size_t I1, std::size_t I2>
class CheckComparable
{
public:
template<typename Tup>
void operator()(Tup const& tup, beast::unit_test::suite& suite) const
{
// All entries in the tuple should be comparable one to the other.
auto const lhs = std::get<I1>(tup);
auto const rhs = std::get<I2>(tup);
static_assert (std::is_same<
decltype (operator== (lhs, rhs)), bool>::value, "== err");
static_assert (std::is_same<
decltype (operator!= (lhs, rhs)), bool>::value, "!= err");
static_assert (std::is_same<
decltype (operator< (lhs, rhs)), bool>::value, "< err");
static_assert (std::is_same<
decltype (operator<= (lhs, rhs)), bool>::value, "<= err");
static_assert (std::is_same<
decltype (operator> (lhs, rhs)), bool>::value, "> err");
static_assert (std::is_same<
decltype (operator>= (lhs, rhs)), bool>::value, ">= err");
// Make sure a sampling of TER types exhibit the expected behavior
// for all comparison operators.
suite.expect ((lhs == rhs) == (TERtoInt (lhs) == TERtoInt (rhs)));
suite.expect ((lhs != rhs) == (TERtoInt (lhs) != TERtoInt (rhs)));
suite.expect ((lhs < rhs) == (TERtoInt (lhs) < TERtoInt (rhs)));
suite.expect ((lhs <= rhs) == (TERtoInt (lhs) <= TERtoInt (rhs)));
suite.expect ((lhs > rhs) == (TERtoInt (lhs) > TERtoInt (rhs)));
suite.expect ((lhs >= rhs) == (TERtoInt (lhs) >= TERtoInt (rhs)));
}
};
void testComparison()
{
// All of the TER-related types should be comparable.
// Examples of all the types we expect to successfully compare.
static auto const ters = std::make_tuple (telLOCAL_ERROR, temMALFORMED,
tefFAILURE, terRETRY, tesSUCCESS, tecCLAIM,
NotTEC {telLOCAL_ERROR}, TER {tecCLAIM});
static const int hiIndex {
std::tuple_size<decltype (ters)>::value - 1};
// Verify that all types in the ters tuple can be compared with all
// the other types in ters.
testIterate<hiIndex, hiIndex, CheckComparable> (ters, *this);
}
void void
run() override run() override
{ {
testTransResultInfo(); testTransResultInfo();
testConversion();
testComparison();
} }
}; };
BEAST_DEFINE_TESTSUITE(TER,protocol,ripple); BEAST_DEFINE_TESTSUITE(TER,protocol,ripple);
} } //namespace ripple