Add preflight functionality to transactors:

The preflight() function performs static validity
analysis of transactions without requiring a ledger.

* Use tx in ApplyContext
* Remove unused journal
* Document apply()
* Add preflight(), which takes an OpenView, uses its rules.
* Change `TER preCheck` to `void preCompute` since it can no longer fail.
This commit is contained in:
Edward Hennis
2015-07-13 09:50:17 -07:00
parent 36a62f110c
commit c15394c42a
25 changed files with 684 additions and 488 deletions

View File

@@ -30,12 +30,55 @@
namespace ripple { namespace ripple {
/** Gate a transaction based on static information.
The transaction is checked against all possible
validity constraints that do not require a ledger.
@return The TER code (a `tem` or tesSUCCESS)
*/
TER
preflight (Rules const& rules, STTx const& tx,
ApplyFlags flags,
Config const& config, beast::Journal j);
/** Apply a prechecked transaction to an OpenView.
See also: apply()
Precondition: The transaction has been checked
and validated using the above function(s).
@return A pair with the TER and a bool indicating
whether or not the transaction was applied.
*/
std::pair<TER, bool>
doapply(OpenView& view, STTx const& tx,
ApplyFlags flags, Config const& config,
beast::Journal j);
/** Apply a transaction to a ReadView. /** Apply a transaction to a ReadView.
Throws: Throws:
Does not throw. Exceptions generated during Does not throw.
tx application will return tefEXCEPTION.
For open ledgers, the Transactor will catch and
return tefEXCEPTION. For closed ledgers, the
Transactor will attempt to only charge a fee,
and return tecFAILED_PROCESSING.
If the Transactor gets an exception while trying
to charge the fee, it will be caught here and
turned into tefEXCEPTION.
This try/catch handler is the last resort, any
uncaught exceptions will be turned into
tefEXCEPTION.
For network health, a Transactor makes its
best effort to at least charge a fee if the
ledger is closed.
@return A pair with the TER and a bool indicating @return A pair with the TER and a bool indicating
whether or not the transaction was applied. whether or not the transaction was applied.

View File

@@ -88,7 +88,6 @@ public:
private: private:
OpenView& base_; OpenView& base_;
ApplyFlags flags_; ApplyFlags flags_;
beast::Journal j_;
boost::optional<ApplyViewImpl> view_; boost::optional<ApplyViewImpl> view_;
}; };

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------
/* /*
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, 2013 Ripple Labs Inc.
@@ -27,33 +27,34 @@
namespace ripple { namespace ripple {
TER TER
CancelOffer::preCheck () CancelOffer::preflight (PreflightContext const& ctx)
{ {
std::uint32_t const uTxFlags (mTxn.getFlags ()); auto const uTxFlags = ctx.tx.getFlags();
if (uTxFlags & tfUniversalMask) if (uTxFlags & tfUniversalMask)
{ {
j_.trace << "Malformed transaction: " << JLOG(ctx.j.trace) << "Malformed transaction: " <<
"Invalid flags set."; "Invalid flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
std::uint32_t const uOfferSequence = mTxn.getFieldU32 (sfOfferSequence); auto const seq = ctx.tx.getFieldU32 (sfOfferSequence);
if (! seq)
if (!uOfferSequence)
{ {
j_.trace << "Malformed transaction: " << JLOG(ctx.j.trace) <<
"No sequence specified."; "CancelOffer::preflight: missing sequence";
return temBAD_SEQUENCE; return temBAD_SEQUENCE;
} }
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
//------------------------------------------------------------------------------
TER TER
CancelOffer::doApply () CancelOffer::doApply ()
{ {
std::uint32_t const uOfferSequence = mTxn.getFieldU32 (sfOfferSequence); std::uint32_t const uOfferSequence = tx().getFieldU32 (sfOfferSequence);
auto const sle = view().read( auto const sle = view().read(
keylet::account(account_)); keylet::account(account_));

View File

@@ -31,14 +31,14 @@ class CancelOffer
: public Transactor : public Transactor
{ {
public: public:
template <class... Args> CancelOffer (ApplyContext& ctx)
CancelOffer (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
TER preCheck () override; static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
}; };

View File

@@ -26,19 +26,19 @@
namespace ripple { namespace ripple {
TER TER
CancelTicket::preCheck() CancelTicket::preflight (PreflightContext const& ctx)
{ {
#if ! RIPPLE_ENABLE_TICKETS #if ! RIPPLE_ENABLE_TICKETS
if (! (view().flags() & tapENABLE_TESTING)) if (! (ctx.flags & tapENABLE_TESTING))
return temDISABLED; return temDISABLED;
#endif #endif
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
TER TER
CancelTicket::doApply () CancelTicket::doApply ()
{ {
uint256 const ticketId = mTxn.getFieldH256 (sfTicketID); uint256 const ticketId = tx().getFieldH256 (sfTicketID);
// VFALCO This is highly suspicious, we're requiring that the // VFALCO This is highly suspicious, we're requiring that the
// transaction provide the return value of getTicketIndex? // transaction provide the return value of getTicketIndex?

View File

@@ -30,14 +30,15 @@ class CancelTicket
: public Transactor : public Transactor
{ {
public: public:
template <class... Args> CancelTicket (ApplyContext& ctx)
CancelTicket (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
TER preCheck() override; static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
}; };

View File

@@ -28,13 +28,35 @@
namespace ripple { namespace ripple {
TER
Change::preflight (PreflightContext const& ctx)
{
auto account = ctx.tx.getAccountID(sfAccount);
if (account.isNonZero ())
{
JLOG(ctx.j.warning) << "Bad source id";
return temBAD_SRC_ACCOUNT;
}
return tesSUCCESS;
}
TER TER
Change::doApply() Change::doApply()
{ {
if (mTxn.getTxnType () == ttAMENDMENT) // If tapOPEN_LEDGER is resurrected into ApplyFlags,
// this block can be moved to preflight.
if (view().open())
{
j_.warning << "Change transaction against open ledger";
return temINVALID;
}
if (tx().getTxnType () == ttAMENDMENT)
return applyAmendment (); return applyAmendment ();
if (mTxn.getTxnType () == ttFEE) if (tx().getTxnType () == ttFEE)
return applyFee (); return applyFee ();
return temUNKNOWN; return temUNKNOWN;
@@ -43,13 +65,7 @@ Change::doApply()
TER TER
Change::checkSign() Change::checkSign()
{ {
if (mTxn.getAccountID (sfAccount).isNonZero ()) if (!tx().getSigningPubKey ().empty () || !tx().getSignature ().empty ())
{
j_.warning << "Bad source account";
return temBAD_SRC_ACCOUNT;
}
if (!mTxn.getSigningPubKey ().empty () || !mTxn.getSignature ().empty ())
{ {
j_.warning << "Bad signature"; j_.warning << "Bad signature";
return temBAD_SIGNATURE; return temBAD_SIGNATURE;
@@ -61,7 +77,7 @@ Change::checkSign()
TER TER
Change::checkSeq() Change::checkSeq()
{ {
if ((mTxn.getSequence () != 0) || mTxn.isFieldPresent (sfPreviousTxnID)) if ((tx().getSequence () != 0) || tx().isFieldPresent (sfPreviousTxnID))
{ {
j_.warning << "Bad sequence"; j_.warning << "Bad sequence";
return temBAD_SEQUENCE; return temBAD_SEQUENCE;
@@ -73,7 +89,7 @@ Change::checkSeq()
TER TER
Change::payFee() Change::payFee()
{ {
if (mTxn.getTransactionFee () != STAmount ()) if (tx().getTransactionFee () != STAmount ())
{ {
j_.warning << "Non-zero fee"; j_.warning << "Non-zero fee";
return temBAD_FEE; return temBAD_FEE;
@@ -82,30 +98,17 @@ Change::payFee()
return tesSUCCESS; return tesSUCCESS;
} }
TER void
Change::preCheck() Change::preCompute()
{ {
account_ = mTxn.getAccountID(sfAccount); account_ = tx().getAccountID(sfAccount);
assert(account_ == zero);
if (account_.isNonZero ())
{
j_.warning << "Bad source id";
return temBAD_SRC_ACCOUNT;
}
if (view().open())
{
j_.warning << "Change transaction against open ledger";
return temINVALID;
}
return tesSUCCESS;
} }
TER TER
Change::applyAmendment() Change::applyAmendment()
{ {
uint256 amendment (mTxn.getFieldH256 (sfAmendment)); uint256 amendment (tx().getFieldH256 (sfAmendment));
auto const k = keylet::amendments(); auto const k = keylet::amendments();
@@ -125,7 +128,7 @@ Change::applyAmendment()
amendment) != amendments.end ()) amendment) != amendments.end ())
return tefALREADY; return tefALREADY;
auto flags = mTxn.getFlags (); auto flags = tx().getFlags ();
const bool gotMajority = (flags & tfGotMajority) != 0; const bool gotMajority = (flags & tfGotMajority) != 0;
const bool lostMajority = (flags & tfLostMajority) != 0; const bool lostMajority = (flags & tfLostMajority) != 0;
@@ -207,13 +210,13 @@ Change::applyFee()
// "Previous fee object: " << feeObject->getJson (0); // "Previous fee object: " << feeObject->getJson (0);
feeObject->setFieldU64 ( feeObject->setFieldU64 (
sfBaseFee, mTxn.getFieldU64 (sfBaseFee)); sfBaseFee, tx().getFieldU64 (sfBaseFee));
feeObject->setFieldU32 ( feeObject->setFieldU32 (
sfReferenceFeeUnits, mTxn.getFieldU32 (sfReferenceFeeUnits)); sfReferenceFeeUnits, tx().getFieldU32 (sfReferenceFeeUnits));
feeObject->setFieldU32 ( feeObject->setFieldU32 (
sfReserveBase, mTxn.getFieldU32 (sfReserveBase)); sfReserveBase, tx().getFieldU32 (sfReserveBase));
feeObject->setFieldU32 ( feeObject->setFieldU32 (
sfReserveIncrement, mTxn.getFieldU32 (sfReserveIncrement)); sfReserveIncrement, tx().getFieldU32 (sfReserveIncrement));
view().update (feeObject); view().update (feeObject);

View File

@@ -33,18 +33,20 @@ class Change
: public Transactor : public Transactor
{ {
public: public:
template <class... Args> Change (ApplyContext& ctx)
Change (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
TER checkSign () override; TER checkSign () override;
TER checkSeq () override; TER checkSeq () override;
TER payFee () override; TER payFee () override;
TER preCheck () override; void preCompute() override;
private: private:
TER applyAmendment (); TER applyAmendment ();

View File

@@ -33,6 +33,99 @@
namespace ripple { namespace ripple {
TER
CreateOffer::preflight (PreflightContext const& ctx)
{
auto& tx = ctx.tx;
auto& j = ctx.j;
std::uint32_t const uTxFlags = tx.getFlags ();
if (uTxFlags & tfOfferCreateMask)
{
JLOG(j.debug) <<
"Malformed transaction: Invalid flags set.";
return temINVALID_FLAG;
}
bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel);
bool const bFillOrKill (uTxFlags & tfFillOrKill);
if (bImmediateOrCancel && bFillOrKill)
{
JLOG(j.debug) <<
"Malformed transaction: both IoC and FoK set.";
return temINVALID_FLAG;
}
bool const bHaveExpiration (tx.isFieldPresent (sfExpiration));
if (bHaveExpiration && (tx.getFieldU32 (sfExpiration) == 0))
{
JLOG(j.debug) <<
"Malformed offer: bad expiration";
return temBAD_EXPIRATION;
}
bool const bHaveCancel (tx.isFieldPresent (sfOfferSequence));
if (bHaveCancel && (tx.getFieldU32 (sfOfferSequence) == 0))
{
JLOG(j.debug) <<
"Malformed offer: bad cancel sequence";
return temBAD_SEQUENCE;
}
STAmount saTakerPays = tx.getFieldAmount (sfTakerPays);
STAmount saTakerGets = tx.getFieldAmount (sfTakerGets);
if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets))
return temBAD_AMOUNT;
if (saTakerPays.native () && saTakerGets.native ())
{
JLOG(j.debug) <<
"Malformed offer: XRP for XRP";
return temBAD_OFFER;
}
if (saTakerPays <= zero || saTakerGets <= zero)
{
JLOG(j.debug) <<
"Malformed offer: bad amount";
return temBAD_OFFER;
}
auto const& uPaysIssuerID = saTakerPays.getIssuer ();
auto const& uPaysCurrency = saTakerPays.getCurrency ();
auto const& uGetsIssuerID = saTakerGets.getIssuer ();
auto const& uGetsCurrency = saTakerGets.getCurrency ();
if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
{
JLOG(j.debug) <<
"Malformed offer: redundant offer";
return temREDUNDANT;
}
// We don't allow a non-native currency to use the currency code XRP.
if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency)
{
JLOG(j.debug) <<
"Malformed offer: Bad currency.";
return temBAD_CURRENCY;
}
if (saTakerPays.native () != !uPaysIssuerID ||
saTakerGets.native () != !uGetsIssuerID)
{
JLOG(j.warning) <<
"Malformed offer: bad issuer";
return temBAD_ISSUER;
}
return Transactor::preflight(ctx);
}
TER TER
CreateOffer::checkAcceptAsset(IssueRef issue) const CreateOffer::checkAcceptAsset(IssueRef issue) const
{ {
@@ -388,7 +481,7 @@ CreateOffer::cross (
beast::WrappedSink takerSink (j_, "Taker "); beast::WrappedSink takerSink (j_, "Taker ");
Taker taker (cross_type_, view, account_, taker_amount, Taker taker (cross_type_, view, account_, taker_amount,
mTxn.getFlags(), ctx_.config, beast::Journal (takerSink)); tx().getFlags(), ctx_.config, beast::Journal (takerSink));
try try
{ {
@@ -432,118 +525,34 @@ CreateOffer::getAccountReserve (SLE::pointer account)
account->getFieldU32 (sfOwnerCount) + 1)); account->getFieldU32 (sfOwnerCount) + 1));
} }
TER void
CreateOffer::preCheck () CreateOffer::preCompute()
{ {
cross_type_ = CrossType::IouToIou; cross_type_ = CrossType::IouToIou;
bool const pays_xrp = bool const pays_xrp =
mTxn.getFieldAmount (sfTakerPays).native (); tx().getFieldAmount (sfTakerPays).native ();
bool const gets_xrp = bool const gets_xrp =
mTxn.getFieldAmount (sfTakerGets).native (); tx().getFieldAmount (sfTakerGets).native ();
if (pays_xrp && !gets_xrp) if (pays_xrp && !gets_xrp)
cross_type_ = CrossType::IouToXrp; cross_type_ = CrossType::IouToXrp;
else if (gets_xrp && !pays_xrp) else if (gets_xrp && !pays_xrp)
cross_type_ = CrossType::XrpToIou; cross_type_ = CrossType::XrpToIou;
std::uint32_t const uTxFlags = mTxn.getFlags (); return Transactor::preCompute();
if (uTxFlags & tfOfferCreateMask)
{
if (j_.debug) j_.debug <<
"Malformed transaction: Invalid flags set.";
return temINVALID_FLAG;
}
bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel);
bool const bFillOrKill (uTxFlags & tfFillOrKill);
if (bImmediateOrCancel && bFillOrKill)
{
if (j_.debug) j_.debug <<
"Malformed transaction: both IoC and FoK set.";
return temINVALID_FLAG;
}
bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration));
if (bHaveExpiration && (mTxn.getFieldU32 (sfExpiration) == 0))
{
if (j_.debug) j_.warning <<
"Malformed offer: bad expiration";
return temBAD_EXPIRATION;
}
bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence));
if (bHaveCancel && (mTxn.getFieldU32 (sfOfferSequence) == 0))
{
if (j_.debug) j_.debug <<
"Malformed offer: bad cancel sequence";
return temBAD_SEQUENCE;
}
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays);
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets);
if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets))
return temBAD_AMOUNT;
if (saTakerPays.native () && saTakerGets.native ())
{
if (j_.debug) j_.warning <<
"Malformed offer: XRP for XRP";
return temBAD_OFFER;
}
if (saTakerPays <= zero || saTakerGets <= zero)
{
if (j_.debug) j_.warning <<
"Malformed offer: bad amount";
return temBAD_OFFER;
}
auto const& uPaysIssuerID = saTakerPays.getIssuer ();
auto const& uPaysCurrency = saTakerPays.getCurrency ();
auto const& uGetsIssuerID = saTakerGets.getIssuer ();
auto const& uGetsCurrency = saTakerGets.getCurrency ();
if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID)
{
if (j_.debug) j_.debug <<
"Malformed offer: redundant offer";
return temREDUNDANT;
}
// We don't allow a non-native currency to use the currency code XRP.
if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency)
{
if (j_.debug) j_.warning <<
"Malformed offer: Bad currency.";
return temBAD_CURRENCY;
}
if (saTakerPays.native () != !uPaysIssuerID ||
saTakerGets.native () != !uGetsIssuerID)
{
if (j_.warning) j_.warning <<
"Malformed offer: bad issuer";
return temBAD_ISSUER;
}
return Transactor::preCheck ();
} }
std::pair<TER, bool> std::pair<TER, bool>
CreateOffer::applyGuts (ApplyView& view, ApplyView& view_cancel) CreateOffer::applyGuts (ApplyView& view, ApplyView& view_cancel)
{ {
std::uint32_t const uTxFlags = mTxn.getFlags (); std::uint32_t const uTxFlags = tx().getFlags ();
bool const bPassive (uTxFlags & tfPassive); bool const bPassive (uTxFlags & tfPassive);
bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel); bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel);
bool const bFillOrKill (uTxFlags & tfFillOrKill); bool const bFillOrKill (uTxFlags & tfFillOrKill);
bool const bSell (uTxFlags & tfSell); bool const bSell (uTxFlags & tfSell);
STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays); STAmount saTakerPays = tx().getFieldAmount (sfTakerPays);
STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets); STAmount saTakerGets = tx().getFieldAmount (sfTakerGets);
if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets)) if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets))
return { temBAD_AMOUNT, true }; return { temBAD_AMOUNT, true };
@@ -553,11 +562,11 @@ CreateOffer::applyGuts (ApplyView& view, ApplyView& view_cancel)
auto const& uGetsIssuerID = saTakerGets.getIssuer (); auto const& uGetsIssuerID = saTakerGets.getIssuer ();
bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration)); bool const bHaveExpiration (tx().isFieldPresent (sfExpiration));
bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence)); bool const bHaveCancel (tx().isFieldPresent (sfOfferSequence));
std::uint32_t const uExpiration = mTxn.getFieldU32 (sfExpiration); std::uint32_t const uExpiration = tx().getFieldU32 (sfExpiration);
std::uint32_t const uCancelSequence = mTxn.getFieldU32 (sfOfferSequence); std::uint32_t const uCancelSequence = tx().getFieldU32 (sfOfferSequence);
// FIXME understand why we use SequenceNext instead of current transaction // FIXME understand why we use SequenceNext instead of current transaction
// sequence to determine the transaction. Why is the offer sequence // sequence to determine the transaction. Why is the offer sequence
@@ -568,7 +577,7 @@ CreateOffer::applyGuts (ApplyView& view, ApplyView& view_cancel)
deprecatedWrongOwnerCount_ = sleCreator->getFieldU32(sfOwnerCount); deprecatedWrongOwnerCount_ = sleCreator->getFieldU32(sfOwnerCount);
std::uint32_t const uAccountSequenceNext = sleCreator->getFieldU32 (sfSequence); std::uint32_t const uAccountSequenceNext = sleCreator->getFieldU32 (sfSequence);
std::uint32_t const uSequence = mTxn.getSequence (); std::uint32_t const uSequence = tx().getSequence ();
// This is the original rate of the offer, and is the rate at which // This is the original rate of the offer, and is the rate at which
// it will be placed, even if crossing offers change the amounts that // it will be placed, even if crossing offers change the amounts that

View File

@@ -39,20 +39,22 @@ class CreateOffer
: public Transactor : public Transactor
{ {
public: public:
template <class... Args> CreateOffer (ApplyContext& ctx)
CreateOffer (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
static
TER
preflight (PreflightContext const& ctx);
/** Returns the reserve the account would have if an offer was added. */ /** Returns the reserve the account would have if an offer was added. */
// VFALCO This function is not needed just inline the behavior // VFALCO This function is not needed just inline the behavior
STAmount STAmount
getAccountReserve (SLE::pointer account); // const? getAccountReserve (SLE::pointer account); // const?
TER void
preCheck () override; preCompute() override;
std::pair<TER, bool> std::pair<TER, bool>
applyGuts (ApplyView& view, ApplyView& view_cancel); applyGuts (ApplyView& view, ApplyView& view_cancel);

View File

@@ -26,24 +26,24 @@
namespace ripple { namespace ripple {
TER TER
CreateTicket::preCheck () CreateTicket::preflight (PreflightContext const& ctx)
{ {
#if ! RIPPLE_ENABLE_TICKETS #if ! RIPPLE_ENABLE_TICKETS
if (! (view().flags() & tapENABLE_TESTING)) if (! (ctx.flags & tapENABLE_TESTING))
return temDISABLED; return temDISABLED;
#endif #endif
if (mTxn.isFieldPresent (sfExpiration)) if (ctx.tx.isFieldPresent (sfExpiration))
{ {
if (mTxn.getFieldU32 (sfExpiration) == 0) if (ctx.tx.getFieldU32 (sfExpiration) == 0)
{ {
j_.warning << JLOG(ctx.j.warning) <<
"Malformed transaction: bad expiration"; "Malformed transaction: bad expiration";
return temBAD_EXPIRATION; return temBAD_EXPIRATION;
} }
} }
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
STAmount STAmount
@@ -65,25 +65,25 @@ CreateTicket::doApply ()
std::uint32_t expiration (0); std::uint32_t expiration (0);
if (mTxn.isFieldPresent (sfExpiration)) if (tx().isFieldPresent (sfExpiration))
{ {
expiration = mTxn.getFieldU32 (sfExpiration); expiration = tx().getFieldU32 (sfExpiration);
if (view().parentCloseTime() >= expiration) if (view().parentCloseTime() >= expiration)
return tesSUCCESS; return tesSUCCESS;
} }
SLE::pointer sleTicket = std::make_shared<SLE>(ltTICKET, SLE::pointer sleTicket = std::make_shared<SLE>(ltTICKET,
getTicketIndex (account_, mTxn.getSequence ())); getTicketIndex (account_, tx().getSequence ()));
sleTicket->setAccountID (sfAccount, account_); sleTicket->setAccountID (sfAccount, account_);
sleTicket->setFieldU32 (sfSequence, mTxn.getSequence ()); sleTicket->setFieldU32 (sfSequence, tx().getSequence ());
if (expiration != 0) if (expiration != 0)
sleTicket->setFieldU32 (sfExpiration, expiration); sleTicket->setFieldU32 (sfExpiration, expiration);
view().insert (sleTicket); view().insert (sleTicket);
if (mTxn.isFieldPresent (sfTarget)) if (tx().isFieldPresent (sfTarget))
{ {
AccountID const target_account (mTxn.getAccountID (sfTarget)); AccountID const target_account (tx().getAccountID (sfTarget));
SLE::pointer sleTarget = view().peek (keylet::account(target_account)); SLE::pointer sleTarget = view().peek (keylet::account(target_account));

View File

@@ -31,15 +31,14 @@ class CreateTicket
: public Transactor : public Transactor
{ {
public: public:
template <class... Args> CreateTicket (ApplyContext& ctx)
CreateTicket (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
static
TER TER
preCheck () override; preflight (PreflightContext const& ctx);
/** Returns the reserve the account would have if an offer was added. */ /** Returns the reserve the account would have if an offer was added. */
// VFALCO Not needed, just inline the behavior. // VFALCO Not needed, just inline the behavior.

View File

@@ -39,13 +39,16 @@ allowDeliverMin (ApplyView const& view)
} }
TER TER
Payment::preCheck () Payment::preflight (PreflightContext const& ctx)
{ {
std::uint32_t const uTxFlags = mTxn.getFlags (); auto& tx = ctx.tx;
auto& j = ctx.j;
std::uint32_t const uTxFlags = tx.getFlags ();
if (uTxFlags & tfPaymentMask) if (uTxFlags & tfPaymentMask)
{ {
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"Invalid flags set."; "Invalid flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
@@ -53,20 +56,21 @@ Payment::preCheck ()
bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
bool const limitQuality = uTxFlags & tfLimitQuality; bool const limitQuality = uTxFlags & tfLimitQuality;
bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
bool const bPaths = mTxn.isFieldPresent (sfPaths); bool const bPaths = tx.isFieldPresent (sfPaths);
bool const bMax = mTxn.isFieldPresent (sfSendMax); bool const bMax = tx.isFieldPresent (sfSendMax);
STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount)); STAmount const saDstAmount (tx.getFieldAmount (sfAmount));
STAmount maxSourceAmount; STAmount maxSourceAmount;
auto const account = tx.getAccountID(sfAccount);
if (bMax) if (bMax)
maxSourceAmount = mTxn.getFieldAmount (sfSendMax); maxSourceAmount = tx.getFieldAmount (sfSendMax);
else if (saDstAmount.native ()) else if (saDstAmount.native ())
maxSourceAmount = saDstAmount; maxSourceAmount = saDstAmount;
else else
maxSourceAmount = STAmount ( maxSourceAmount = STAmount (
{ saDstAmount.getCurrency (), account_ }, { saDstAmount.getCurrency (), account },
saDstAmount.mantissa(), saDstAmount.exponent (), saDstAmount.mantissa(), saDstAmount.exponent (),
saDstAmount < zero); saDstAmount < zero);
@@ -79,134 +83,137 @@ Payment::preCheck ()
if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount)) if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount))
return temBAD_AMOUNT; return temBAD_AMOUNT;
AccountID const uDstAccountID (mTxn.getAccountID (sfDestination)); auto const uDstAccountID = tx.getAccountID (sfDestination);
if (!uDstAccountID) if (!uDstAccountID)
{ {
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"Payment destination account not specified."; "Payment destination account not specified.";
return temDST_NEEDED; return temDST_NEEDED;
} }
if (bMax && maxSourceAmount <= zero) if (bMax && maxSourceAmount <= zero)
{ {
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"bad max amount: " << maxSourceAmount.getFullText (); "bad max amount: " << maxSourceAmount.getFullText ();
return temBAD_AMOUNT; return temBAD_AMOUNT;
} }
if (saDstAmount <= zero) if (saDstAmount <= zero)
{ {
j_.trace << "Malformed transaction: "<< JLOG(j.trace) << "Malformed transaction: "<<
"bad dst amount: " << saDstAmount.getFullText (); "bad dst amount: " << saDstAmount.getFullText ();
return temBAD_AMOUNT; return temBAD_AMOUNT;
} }
if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency) if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency)
{ {
j_.trace <<"Malformed transaction: " << JLOG(j.trace) <<"Malformed transaction: " <<
"Bad currency."; "Bad currency.";
return temBAD_CURRENCY; return temBAD_CURRENCY;
} }
if (account_ == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) if (account == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths)
{ {
// You're signing yourself a payment. // You're signing yourself a payment.
// If bPaths is true, you might be trying some arbitrage. // If bPaths is true, you might be trying some arbitrage.
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"Redundant payment from " << to_string (account_) << "Redundant payment from " << to_string (account) <<
" to self without path for " << to_string (uDstCurrency); " to self without path for " << to_string (uDstCurrency);
return temREDUNDANT; return temREDUNDANT;
} }
if (bXRPDirect && bMax) if (bXRPDirect && bMax)
{ {
// Consistent but redundant transaction. // Consistent but redundant transaction.
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"SendMax specified for XRP to XRP."; "SendMax specified for XRP to XRP.";
return temBAD_SEND_XRP_MAX; return temBAD_SEND_XRP_MAX;
} }
if (bXRPDirect && bPaths) if (bXRPDirect && bPaths)
{ {
// XRP is sent without paths. // XRP is sent without paths.
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"Paths specified for XRP to XRP."; "Paths specified for XRP to XRP.";
return temBAD_SEND_XRP_PATHS; return temBAD_SEND_XRP_PATHS;
} }
if (bXRPDirect && partialPaymentAllowed) if (bXRPDirect && partialPaymentAllowed)
{ {
// Consistent but redundant transaction. // Consistent but redundant transaction.
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"Partial payment specified for XRP to XRP."; "Partial payment specified for XRP to XRP.";
return temBAD_SEND_XRP_PARTIAL; return temBAD_SEND_XRP_PARTIAL;
} }
if (bXRPDirect && limitQuality) if (bXRPDirect && limitQuality)
{ {
// Consistent but redundant transaction. // Consistent but redundant transaction.
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"Limit quality specified for XRP to XRP."; "Limit quality specified for XRP to XRP.";
return temBAD_SEND_XRP_LIMIT; return temBAD_SEND_XRP_LIMIT;
} }
if (bXRPDirect && !defaultPathsAllowed) if (bXRPDirect && !defaultPathsAllowed)
{ {
// Consistent but redundant transaction. // Consistent but redundant transaction.
j_.trace << "Malformed transaction: " << JLOG(j.trace) << "Malformed transaction: " <<
"No ripple direct specified for XRP to XRP."; "No ripple direct specified for XRP to XRP.";
return temBAD_SEND_XRP_NO_DIRECT; return temBAD_SEND_XRP_NO_DIRECT;
} }
if (mTxn.isFieldPresent(sfDeliverMin)) if (tx.isFieldPresent(sfDeliverMin))
{ {
if (! allowDeliverMin(view()))
return temMALFORMED;
if (! partialPaymentAllowed) if (! partialPaymentAllowed)
{ {
j_.trace << "Malformed transaction: Partial payment not " JLOG(j.trace) << "Malformed transaction: Partial payment not "
"specified for " << jss::DeliverMin.c_str() << "."; "specified for " << jss::DeliverMin.c_str() << ".";
return temBAD_AMOUNT; return temBAD_AMOUNT;
} }
auto const dMin = mTxn.getFieldAmount(sfDeliverMin); auto const dMin = tx.getFieldAmount(sfDeliverMin);
if (! isLegalNet(dMin) || dMin <= zero) if (!isLegalNet(dMin) || dMin <= zero)
{ {
j_.trace << "Malformed transaction: Invalid " << JLOG(j.trace) << "Malformed transaction: Invalid " <<
jss::DeliverMin.c_str() << " amount. " << jss::DeliverMin.c_str() << " amount. " <<
dMin.getFullText(); dMin.getFullText();
return temBAD_AMOUNT; return temBAD_AMOUNT;
} }
if (dMin.issue() != saDstAmount.issue()) if (dMin.issue() != saDstAmount.issue())
{ {
j_.trace << "Malformed transaction: Dst issue differs " JLOG(j.trace) << "Malformed transaction: Dst issue differs "
"from " << jss::DeliverMin.c_str() << ". " << "from " << jss::DeliverMin.c_str() << ". " <<
dMin.getFullText(); dMin.getFullText();
return temBAD_AMOUNT; return temBAD_AMOUNT;
} }
if (dMin > saDstAmount) if (dMin > saDstAmount)
{ {
j_.trace << "Malformed transaction: Dst amount less than " << JLOG(j.trace) << "Malformed transaction: Dst amount less than " <<
jss::DeliverMin.c_str() << ". " << jss::DeliverMin.c_str() << ". " <<
dMin.getFullText(); dMin.getFullText();
return temBAD_AMOUNT; return temBAD_AMOUNT;
} }
} }
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
TER TER
Payment::doApply () Payment::doApply ()
{ {
bool const deliverMin = tx().isFieldPresent(sfDeliverMin);
if (deliverMin)
{
if (! allowDeliverMin(view()))
return temMALFORMED;
}
// Ripple if source or destination is non-native or if there are paths. // Ripple if source or destination is non-native or if there are paths.
std::uint32_t const uTxFlags = mTxn.getFlags (); std::uint32_t const uTxFlags = tx().getFlags ();
bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
bool const limitQuality = uTxFlags & tfLimitQuality; bool const limitQuality = uTxFlags & tfLimitQuality;
bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
bool const bPaths = mTxn.isFieldPresent (sfPaths); bool const bPaths = tx().isFieldPresent (sfPaths);
bool const bMax = mTxn.isFieldPresent (sfSendMax); bool const bMax = tx().isFieldPresent (sfSendMax);
bool const deliverMin = mTxn.isFieldPresent(sfDeliverMin) &&
allowDeliverMin(view());
AccountID const uDstAccountID (mTxn.getAccountID (sfDestination)); AccountID const uDstAccountID (tx().getAccountID (sfDestination));
STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount)); STAmount const saDstAmount (tx().getFieldAmount (sfAmount));
STAmount maxSourceAmount; STAmount maxSourceAmount;
if (bMax) if (bMax)
maxSourceAmount = mTxn.getFieldAmount (sfSendMax); maxSourceAmount = tx().getFieldAmount (sfSendMax);
else if (saDstAmount.native ()) else if (saDstAmount.native ())
maxSourceAmount = saDstAmount; maxSourceAmount = saDstAmount;
else else
@@ -269,7 +276,7 @@ Payment::doApply ()
view().insert(sleDst); view().insert(sleDst);
} }
else if ((sleDst->getFlags () & lsfRequireDestTag) && else if ((sleDst->getFlags () & lsfRequireDestTag) &&
!mTxn.isFieldPresent (sfDestinationTag)) !tx().isFieldPresent (sfDestinationTag))
{ {
// The tag is basically account-specific information we don't // The tag is basically account-specific information we don't
// understand, but we can require someone to fill it in. // understand, but we can require someone to fill it in.
@@ -299,7 +306,7 @@ Payment::doApply ()
// transitive balances. // transitive balances.
// Copy paths into an editable class. // Copy paths into an editable class.
STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths); STPathSet spsPaths = tx().getFieldPathSet (sfPaths);
try try
{ {
@@ -345,7 +352,7 @@ Payment::doApply ()
rc.actualAmountOut != saDstAmount) rc.actualAmountOut != saDstAmount)
{ {
if (deliverMin && rc.actualAmountOut < if (deliverMin && rc.actualAmountOut <
mTxn.getFieldAmount (sfDeliverMin)) tx().getFieldAmount (sfDeliverMin))
rc.setResult (tecPATH_PARTIAL); rc.setResult (tecPATH_PARTIAL);
else else
ctx_.deliver (rc.actualAmountOut); ctx_.deliver (rc.actualAmountOut);
@@ -383,7 +390,7 @@ Payment::doApply ()
// mPriorBalance is the balance on the sending account BEFORE the // mPriorBalance is the balance on the sending account BEFORE the
// fees were charged. We want to make sure we have enough reserve // fees were charged. We want to make sure we have enough reserve
// to send. Allow final spend to use reserve for fee. // to send. Allow final spend to use reserve for fee.
auto const mmm = std::max(mTxn.getTransactionFee (), auto const mmm = std::max(tx().getTransactionFee (),
STAmount (uReserve)); STAmount (uReserve));
if (mPriorBalance < saDstAmount + mmm) if (mPriorBalance < saDstAmount + mmm)

View File

@@ -39,14 +39,15 @@ class Payment
static std::size_t const MaxPathLength = 8; static std::size_t const MaxPathLength = 8;
public: public:
template <class... Args> Payment (ApplyContext& ctx)
Payment (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
TER preCheck () override; static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
}; };

View File

@@ -29,22 +29,25 @@
namespace ripple { namespace ripple {
TER TER
SetAccount::preCheck () SetAccount::preflight (PreflightContext const& ctx)
{ {
std::uint32_t const uTxFlags = mTxn.getFlags (); auto& tx = ctx.tx;
auto& j = ctx.j;
std::uint32_t const uTxFlags = tx.getFlags ();
if (uTxFlags & tfAccountSetMask) if (uTxFlags & tfAccountSetMask)
{ {
j_.trace << "Malformed transaction: Invalid flags set."; JLOG(j.trace) << "Malformed transaction: Invalid flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
std::uint32_t const uSetFlag = mTxn.getFieldU32 (sfSetFlag); std::uint32_t const uSetFlag = tx.getFieldU32 (sfSetFlag);
std::uint32_t const uClearFlag = mTxn.getFieldU32 (sfClearFlag); std::uint32_t const uClearFlag = tx.getFieldU32 (sfClearFlag);
if ((uSetFlag != 0) && (uSetFlag == uClearFlag)) if ((uSetFlag != 0) && (uSetFlag == uClearFlag))
{ {
j_.trace << "Malformed transaction: Set and clear same flag."; JLOG(j.trace) << "Malformed transaction: Set and clear same flag.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
@@ -56,7 +59,7 @@ SetAccount::preCheck ()
if (bSetRequireAuth && bClearRequireAuth) if (bSetRequireAuth && bClearRequireAuth)
{ {
j_.trace << "Malformed transaction: Contradictory flags set."; JLOG(j.trace) << "Malformed transaction: Contradictory flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
@@ -68,7 +71,7 @@ SetAccount::preCheck ()
if (bSetRequireDest && bClearRequireDest) if (bSetRequireDest && bClearRequireDest)
{ {
j_.trace << "Malformed transaction: Contradictory flags set."; JLOG(j.trace) << "Malformed transaction: Contradictory flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
@@ -80,29 +83,29 @@ SetAccount::preCheck ()
if (bSetDisallowXRP && bClearDisallowXRP) if (bSetDisallowXRP && bClearDisallowXRP)
{ {
j_.trace << "Malformed transaction: Contradictory flags set."; JLOG(j.trace) << "Malformed transaction: Contradictory flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
// TransferRate // TransferRate
if (mTxn.isFieldPresent (sfTransferRate)) if (tx.isFieldPresent (sfTransferRate))
{ {
std::uint32_t uRate = mTxn.getFieldU32 (sfTransferRate); std::uint32_t uRate = tx.getFieldU32 (sfTransferRate);
if (uRate && (uRate < QUALITY_ONE)) if (uRate && (uRate < QUALITY_ONE))
{ {
j_.trace << "Malformed transaction: Bad transfer rate."; JLOG(j.trace) << "Malformed transaction: Bad transfer rate.";
return temBAD_TRANSFER_RATE; return temBAD_TRANSFER_RATE;
} }
} }
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
TER TER
SetAccount::doApply () SetAccount::doApply ()
{ {
std::uint32_t const uTxFlags = mTxn.getFlags (); std::uint32_t const uTxFlags = tx().getFlags ();
auto const sle = view().peek( auto const sle = view().peek(
keylet::account(account_)); keylet::account(account_));
@@ -110,8 +113,8 @@ SetAccount::doApply ()
std::uint32_t const uFlagsIn = sle->getFieldU32 (sfFlags); std::uint32_t const uFlagsIn = sle->getFieldU32 (sfFlags);
std::uint32_t uFlagsOut = uFlagsIn; std::uint32_t uFlagsOut = uFlagsIn;
std::uint32_t const uSetFlag = mTxn.getFieldU32 (sfSetFlag); std::uint32_t const uSetFlag = tx().getFieldU32 (sfSetFlag);
std::uint32_t const uClearFlag = mTxn.getFieldU32 (sfClearFlag); std::uint32_t const uClearFlag = tx().getFieldU32 (sfClearFlag);
// legacy AccountSet flags // legacy AccountSet flags
bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest); bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest);
@@ -259,9 +262,9 @@ SetAccount::doApply ()
// //
// EmailHash // EmailHash
// //
if (mTxn.isFieldPresent (sfEmailHash)) if (tx().isFieldPresent (sfEmailHash))
{ {
uint128 const uHash = mTxn.getFieldH128 (sfEmailHash); uint128 const uHash = tx().getFieldH128 (sfEmailHash);
if (!uHash) if (!uHash)
{ {
@@ -278,9 +281,9 @@ SetAccount::doApply ()
// //
// WalletLocator // WalletLocator
// //
if (mTxn.isFieldPresent (sfWalletLocator)) if (tx().isFieldPresent (sfWalletLocator))
{ {
uint256 const uHash = mTxn.getFieldH256 (sfWalletLocator); uint256 const uHash = tx().getFieldH256 (sfWalletLocator);
if (!uHash) if (!uHash)
{ {
@@ -297,9 +300,9 @@ SetAccount::doApply ()
// //
// MessageKey // MessageKey
// //
if (mTxn.isFieldPresent (sfMessageKey)) if (tx().isFieldPresent (sfMessageKey))
{ {
Blob const messageKey = mTxn.getFieldVL (sfMessageKey); Blob const messageKey = tx().getFieldVL (sfMessageKey);
if (messageKey.size () > PUBLIC_BYTES_MAX) if (messageKey.size () > PUBLIC_BYTES_MAX)
{ {
@@ -322,9 +325,9 @@ SetAccount::doApply ()
// //
// Domain // Domain
// //
if (mTxn.isFieldPresent (sfDomain)) if (tx().isFieldPresent (sfDomain))
{ {
Blob const domain = mTxn.getFieldVL (sfDomain); Blob const domain = tx().getFieldVL (sfDomain);
if (domain.size () > DOMAIN_BYTES_MAX) if (domain.size () > DOMAIN_BYTES_MAX)
{ {
@@ -347,9 +350,9 @@ SetAccount::doApply ()
// //
// TransferRate // TransferRate
// //
if (mTxn.isFieldPresent (sfTransferRate)) if (tx().isFieldPresent (sfTransferRate))
{ {
std::uint32_t uRate = mTxn.getFieldU32 (sfTransferRate); std::uint32_t uRate = tx().getFieldU32 (sfTransferRate);
if (uRate == 0 || uRate == QUALITY_ONE) if (uRate == 0 || uRate == QUALITY_ONE)
{ {

View File

@@ -36,14 +36,15 @@ class SetAccount
static std::size_t const PUBLIC_BYTES_MAX = 33; static std::size_t const PUBLIC_BYTES_MAX = 33;
public: public:
template <class... Args> SetAccount (ApplyContext& ctx)
SetAccount (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
TER preCheck () override; static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
}; };

View File

@@ -42,19 +42,19 @@ SetRegularKey::calculateBaseFee ()
} }
TER TER
SetRegularKey::preCheck () SetRegularKey::preflight (PreflightContext const& ctx)
{ {
std::uint32_t const uTxFlags = mTxn.getFlags (); std::uint32_t const uTxFlags = ctx.tx.getFlags ();
if (uTxFlags & tfUniversalMask) if (uTxFlags & tfUniversalMask)
{ {
if (j_.trace) j_.trace << JLOG(ctx.j.trace) <<
"Malformed transaction: Invalid flags set."; "Malformed transaction: Invalid flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
TER TER
@@ -66,10 +66,10 @@ SetRegularKey::doApply ()
if (mFeeDue == zero) if (mFeeDue == zero)
sle->setFlag (lsfPasswordSpent); sle->setFlag (lsfPasswordSpent);
if (mTxn.isFieldPresent (sfRegularKey)) if (tx().isFieldPresent (sfRegularKey))
{ {
sle->setAccountID (sfRegularKey, sle->setAccountID (sfRegularKey,
mTxn.getAccountID (sfRegularKey)); tx().getAccountID (sfRegularKey));
} }
else else
{ {

View File

@@ -33,14 +33,15 @@ class SetRegularKey
std::uint64_t calculateBaseFee () override; std::uint64_t calculateBaseFee () override;
public: public:
template <class... Args> SetRegularKey (ApplyContext& ctx)
SetRegularKey (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
TER preCheck () override; static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
}; };

View File

@@ -32,6 +32,79 @@
namespace ripple { namespace ripple {
std::tuple<TER, std::uint32_t,
std::vector<SignerEntries::SignerEntry>,
SetSignerList::Operation>
SetSignerList::determineOperation(STTx const& tx,
ApplyFlags flags, beast::Journal j)
{
// Check the quorum. A non-zero quorum means we're creating or replacing
// the list. A zero quorum means we're destroying the list.
auto const quorum = tx.getFieldU32(sfSignerQuorum);
std::vector<SignerEntries::SignerEntry> sign;
Operation op = unknown;
bool const hasSignerEntries(tx.isFieldPresent(sfSignerEntries));
if (quorum && hasSignerEntries)
{
SignerEntries::Decoded signers(
SignerEntries::deserialize(tx, j, "transaction"));
if (signers.ter != tesSUCCESS)
return std::make_tuple(signers.ter,
quorum, sign, op);
std::sort(signers.vec.begin(), signers.vec.end());
// Save deserialized list for later.
sign = std::move(signers.vec);
op = set;
}
else if ((quorum == 0) && !hasSignerEntries)
{
op = destroy;
}
return std::make_tuple(tesSUCCESS,
quorum, sign, op);
}
TER
SetSignerList::preflight (PreflightContext const& ctx)
{
#if ! RIPPLE_ENABLE_MULTI_SIGN
if (! (ctx.flags & tapENABLE_TESTING))
return temDISABLED;
#endif
auto const result = determineOperation(ctx.tx, ctx.flags, ctx.j);
if (std::get<0>(result) != tesSUCCESS)
return std::get<0>(result);
if (std::get<3>(result) == unknown)
{
// Neither a set nor a destroy. Malformed.
JLOG(ctx.j.trace) <<
"Malformed transaction: Invalid signer set list format.";
return temMALFORMED;
}
if (std::get<3>(result) == set)
{
// Validate our settings.
auto const account = ctx.tx.getAccountID(sfAccount);
TER const ter =
validateQuorumAndSignerEntries(std::get<1>(result),
std::get<2>(result), account, ctx.j);
if (ter != tesSUCCESS)
{
return ter;
}
}
return Transactor::preflight(ctx);
}
TER TER
SetSignerList::doApply () SetSignerList::doApply ()
{ {
@@ -39,7 +112,7 @@ SetSignerList::doApply ()
// to our handlers. // to our handlers.
uint256 const index = getSignerListIndex (account_); uint256 const index = getSignerListIndex (account_);
// Perform the operation preCheck() decided on. // Perform the operation preCompute() decided on.
switch (do_) switch (do_)
{ {
case set: case set:
@@ -56,60 +129,27 @@ SetSignerList::doApply ()
return temMALFORMED; return temMALFORMED;
} }
TER void
SetSignerList::preCheck() SetSignerList::preCompute()
{ {
#if ! RIPPLE_ENABLE_MULTI_SIGN // Get the quorum and operation info.
if (! (view().flags() & tapENABLE_TESTING)) auto result = determineOperation(tx(), view().flags(), j_);
return temDISABLED; assert(std::get<0>(result) == tesSUCCESS);
#endif assert(std::get<3>(result) != unknown);
// We need the account ID later, so do this check first. quorum_ = std::get<1>(result);
preCheckAccount (); signers_ = std::get<2>(result);
do_ = std::get<3>(result);
// Check the quorum. A non-zero quorum means we're creating or replacing return Transactor::preCompute();
// the list. A zero quorum means we're destroying the list.
quorum_ = (mTxn.getFieldU32 (sfSignerQuorum));
bool const hasSignerEntries (mTxn.isFieldPresent (sfSignerEntries));
if (quorum_ && hasSignerEntries)
{
SignerEntries::Decoded signers (
SignerEntries::deserialize (mTxn, j_, "transaction"));
if (signers.ter != tesSUCCESS)
return signers.ter;
// Validate our settings.
if (TER const ter =
validateQuorumAndSignerEntries (quorum_, signers.vec))
{
return ter;
}
// Save deserialized and validated list for later.
signers_ = std::move (signers.vec);
do_ = set;
}
else if ((quorum_ == 0) && !hasSignerEntries)
{
do_ = destroy;
}
else
{
// Neither a set nor a destroy. Malformed.
if (j_.trace) j_.trace <<
"Malformed transaction: Invalid signer set list format.";
return temMALFORMED;
}
return preCheckSigningKey ();
} }
TER TER
SetSignerList::validateQuorumAndSignerEntries ( SetSignerList::validateQuorumAndSignerEntries (
std::uint32_t quorum, std::uint32_t quorum,
std::vector<SignerEntries::SignerEntry>& signers) const std::vector<SignerEntries::SignerEntry> const& signers,
AccountID const& account,
beast::Journal j)
{ {
// Reject if there are too many or too few entries in the list. // Reject if there are too many or too few entries in the list.
{ {
@@ -117,18 +157,18 @@ SetSignerList::validateQuorumAndSignerEntries (
if ((signerCount < SignerEntries::minEntries) if ((signerCount < SignerEntries::minEntries)
|| (signerCount > SignerEntries::maxEntries)) || (signerCount > SignerEntries::maxEntries))
{ {
if (j_.trace) j_.trace << if (j.trace) j.trace <<
"Too many or too few signers in signer list."; "Too many or too few signers in signer list.";
return temMALFORMED; return temMALFORMED;
} }
} }
// Make sure there are no duplicate signers. // Make sure there are no duplicate signers.
std::sort (signers.begin (), signers.end ()); assert(std::is_sorted(signers.begin(), signers.end()));
if (std::adjacent_find ( if (std::adjacent_find (
signers.begin (), signers.end ()) != signers.end ()) signers.begin (), signers.end ()) != signers.end ())
{ {
if (j_.trace) j_.trace << if (j.trace) j.trace <<
"Duplicate signers in signer list"; "Duplicate signers in signer list";
return temBAD_SIGNER; return temBAD_SIGNER;
} }
@@ -140,9 +180,9 @@ SetSignerList::validateQuorumAndSignerEntries (
{ {
allSignersWeight += signer.weight; allSignersWeight += signer.weight;
if (signer.account == account_) if (signer.account == account)
{ {
if (j_.trace) j_.trace << if (j.trace) j.trace <<
"A signer may not self reference account."; "A signer may not self reference account.";
return temBAD_SIGNER; return temBAD_SIGNER;
} }
@@ -152,7 +192,7 @@ SetSignerList::validateQuorumAndSignerEntries (
} }
if ((quorum <= 0) || (allSignersWeight < quorum)) if ((quorum <= 0) || (allSignersWeight < quorum))
{ {
if (j_.trace) j_.trace << if (j.trace) j.trace <<
"Quorum is unreachable"; "Quorum is unreachable";
return temBAD_QUORUM; return temBAD_QUORUM;
} }

View File

@@ -42,28 +42,39 @@ this class implements.
class SetSignerList : public Transactor class SetSignerList : public Transactor
{ {
private: private:
// Values determined during preCheck for use later. // Values determined during preCompute for use later.
enum Operation {unknown, set, destroy}; enum Operation {unknown, set, destroy};
Operation do_ {unknown}; Operation do_ {unknown};
std::uint32_t quorum_ {0}; std::uint32_t quorum_ {0};
std::vector<SignerEntries::SignerEntry> signers_; std::vector<SignerEntries::SignerEntry> signers_;
public: public:
template <class... Args> SetSignerList (ApplyContext& ctx)
SetSignerList (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
TER preCheck () override; void preCompute() override;
private: private:
// `signers` is sorted on return static
std::tuple<TER, std::uint32_t,
std::vector<SignerEntries::SignerEntry>,
Operation>
determineOperation(STTx const& tx,
ApplyFlags flags, beast::Journal j);
static
TER validateQuorumAndSignerEntries ( TER validateQuorumAndSignerEntries (
std::uint32_t quorum, std::uint32_t quorum,
std::vector<SignerEntries::SignerEntry>& signers) const; std::vector<SignerEntries::SignerEntry> const& signers,
AccountID const& account,
beast::Journal j);
TER replaceSignerList (uint256 const& index); TER replaceSignerList (uint256 const& index);
TER destroySignerList (uint256 const& index); TER destroySignerList (uint256 const& index);

View File

@@ -28,25 +28,28 @@
namespace ripple { namespace ripple {
TER TER
SetTrust::preCheck () SetTrust::preflight (PreflightContext const& ctx)
{ {
std::uint32_t const uTxFlags = mTxn.getFlags (); auto& tx = ctx.tx;
auto& j = ctx.j;
std::uint32_t const uTxFlags = tx.getFlags ();
if (uTxFlags & tfTrustSetMask) if (uTxFlags & tfTrustSetMask)
{ {
j_.trace << JLOG(j.trace) <<
"Malformed transaction: Invalid flags set."; "Malformed transaction: Invalid flags set.";
return temINVALID_FLAG; return temINVALID_FLAG;
} }
STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount)); STAmount const saLimitAmount (tx.getFieldAmount (sfLimitAmount));
if (!isLegalNet (saLimitAmount)) if (!isLegalNet (saLimitAmount))
return temBAD_AMOUNT; return temBAD_AMOUNT;
if (saLimitAmount.native ()) if (saLimitAmount.native ())
{ {
if (j_.trace) j_.trace << JLOG(j.trace) <<
"Malformed transaction: specifies native limit " << "Malformed transaction: specifies native limit " <<
saLimitAmount.getFullText (); saLimitAmount.getFullText ();
return temBAD_LIMIT; return temBAD_LIMIT;
@@ -54,14 +57,14 @@ SetTrust::preCheck ()
if (badCurrency() == saLimitAmount.getCurrency ()) if (badCurrency() == saLimitAmount.getCurrency ())
{ {
if (j_.trace) j_.trace << JLOG(j.trace) <<
"Malformed transaction: specifies XRP as IOU"; "Malformed transaction: specifies XRP as IOU";
return temBAD_CURRENCY; return temBAD_CURRENCY;
} }
if (saLimitAmount < zero) if (saLimitAmount < zero)
{ {
if (j_.trace) j_.trace << JLOG(j.trace) <<
"Malformed transaction: Negative credit limit."; "Malformed transaction: Negative credit limit.";
return temBAD_LIMIT; return temBAD_LIMIT;
} }
@@ -71,12 +74,12 @@ SetTrust::preCheck ()
if (!issuer || issuer == noAccount()) if (!issuer || issuer == noAccount())
{ {
if (j_.trace) j_.trace << JLOG(j.trace) <<
"Malformed transaction: no destination account."; "Malformed transaction: no destination account.";
return temDST_NEEDED; return temDST_NEEDED;
} }
return Transactor::preCheck (); return Transactor::preflight(ctx);
} }
TER TER
@@ -84,9 +87,9 @@ SetTrust::doApply ()
{ {
TER terResult = tesSUCCESS; TER terResult = tesSUCCESS;
STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount)); STAmount const saLimitAmount (tx().getFieldAmount (sfLimitAmount));
bool const bQualityIn (mTxn.isFieldPresent (sfQualityIn)); bool const bQualityIn (tx().isFieldPresent (sfQualityIn));
bool const bQualityOut (mTxn.isFieldPresent (sfQualityOut)); bool const bQualityOut (tx().isFieldPresent (sfQualityOut));
Currency const currency (saLimitAmount.getCurrency ()); Currency const currency (saLimitAmount.getCurrency ());
AccountID uDstAccountID (saLimitAmount.getIssuer ()); AccountID uDstAccountID (saLimitAmount.getIssuer ());
@@ -115,13 +118,13 @@ SetTrust::doApply ()
? 0 ? 0
: view().fees().accountReserve(uOwnerCount + 1)); : view().fees().accountReserve(uOwnerCount + 1));
std::uint32_t uQualityIn (bQualityIn ? mTxn.getFieldU32 (sfQualityIn) : 0); std::uint32_t uQualityIn (bQualityIn ? tx().getFieldU32 (sfQualityIn) : 0);
std::uint32_t uQualityOut (bQualityOut ? mTxn.getFieldU32 (sfQualityOut) : 0); std::uint32_t uQualityOut (bQualityOut ? tx().getFieldU32 (sfQualityOut) : 0);
if (bQualityOut && QUALITY_ONE == uQualityOut) if (bQualityOut && QUALITY_ONE == uQualityOut)
uQualityOut = 0; uQualityOut = 0;
std::uint32_t const uTxFlags = mTxn.getFlags (); std::uint32_t const uTxFlags = tx().getFlags ();
bool const bSetAuth = (uTxFlags & tfSetfAuth); bool const bSetAuth = (uTxFlags & tfSetfAuth);
bool const bSetNoRipple = (uTxFlags & tfSetNoRipple); bool const bSetNoRipple = (uTxFlags & tfSetNoRipple);

View File

@@ -32,14 +32,15 @@ class SetTrust
: public Transactor : public Transactor
{ {
public: public:
template <class... Args> SetTrust (ApplyContext& ctx)
SetTrust (Args&&... args) : Transactor(ctx)
: Transactor(std::forward<
Args>(args)...)
{ {
} }
TER preCheck () override; static
TER
preflight (PreflightContext const& ctx);
TER doApply () override; TER doApply () override;
}; };

View File

@@ -30,10 +30,56 @@
namespace ripple { namespace ripple {
TER
Transactor::preflight (PreflightContext const& ctx)
{
auto& tx = ctx.tx;
auto& j = ctx.j;
auto const id = tx.getAccountID(sfAccount);
if (id == zero)
{
JLOG(j.warning) << "Transactor::preflight: bad account id";
return temBAD_SRC_ACCOUNT;
}
// Extract signing key
// Transactions contain a signing key. This allows us to trivially verify a
// transaction has at least been properly signed without going to disk.
// Each transaction also notes a source account id. This is used to verify
// that the signing key is associated with the account.
// XXX This could be a lot cleaner to prevent unnecessary copying.
auto const pk =
RippleAddress::createAccountPublic(
tx.getSigningPubKey());
if (!tx.isKnownGood ())
{
if (tx.isKnownBad () ||
(! (ctx.flags & tapNO_CHECK_SIGN) && !tx.checkSign(
(
#if RIPPLE_ENABLE_MULTI_SIGN
true
#else
ctx.flags & tapENABLE_TESTING
#endif
))))
{
tx.setBad();
j.debug << "apply: Invalid transaction (bad signature)";
return temINVALID;
}
tx.setGood();
}
return tesSUCCESS;
}
//------------------------------------------------------------------------------
Transactor::Transactor( Transactor::Transactor(
ApplyContext& ctx) ApplyContext& ctx)
: mTxn (ctx.tx) : ctx_ (ctx)
, ctx_ (ctx)
, j_ (ctx.journal) , j_ (ctx.journal)
, mHasAuthKey (false) , mHasAuthKey (false)
, mSigMaster (false) , mSigMaster (false)
@@ -55,7 +101,7 @@ std::uint64_t Transactor::calculateBaseFee ()
TER Transactor::payFee () TER Transactor::payFee ()
{ {
STAmount saPaid = mTxn.getTransactionFee (); STAmount saPaid = tx().getTransactionFee ();
if (!isLegalNet (saPaid)) if (!isLegalNet (saPaid))
return temBAD_AMOUNT; return temBAD_AMOUNT;
@@ -111,7 +157,7 @@ TER Transactor::checkSeq ()
auto const sle = view().peek( auto const sle = view().peek(
keylet::account(account_)); keylet::account(account_));
std::uint32_t const t_seq = mTxn.getSequence (); std::uint32_t const t_seq = tx().getSequence ();
std::uint32_t const a_seq = sle->getFieldU32 (sfSequence); std::uint32_t const a_seq = sle->getFieldU32 (sfSequence);
if (t_seq != a_seq) if (t_seq != a_seq)
@@ -124,7 +170,7 @@ TER Transactor::checkSeq ()
return terPRE_SEQ; return terPRE_SEQ;
} }
if (view().txExists(mTxn.getTransactionID ())) if (view().txExists(tx().getTransactionID ()))
return tefALREADY; return tefALREADY;
j_.trace << "applyTransaction: has past sequence number " << j_.trace << "applyTransaction: has past sequence number " <<
@@ -132,91 +178,41 @@ TER Transactor::checkSeq ()
return tefPAST_SEQ; return tefPAST_SEQ;
} }
if (mTxn.isFieldPresent (sfAccountTxnID) && if (tx().isFieldPresent (sfAccountTxnID) &&
(sle->getFieldH256 (sfAccountTxnID) != mTxn.getFieldH256 (sfAccountTxnID))) (sle->getFieldH256 (sfAccountTxnID) != tx().getFieldH256 (sfAccountTxnID)))
return tefWRONG_PRIOR; return tefWRONG_PRIOR;
if (mTxn.isFieldPresent (sfLastLedgerSequence) && if (tx().isFieldPresent (sfLastLedgerSequence) &&
(view().seq() > mTxn.getFieldU32 (sfLastLedgerSequence))) (view().seq() > tx().getFieldU32 (sfLastLedgerSequence)))
return tefMAX_LEDGER; return tefMAX_LEDGER;
sle->setFieldU32 (sfSequence, t_seq + 1); sle->setFieldU32 (sfSequence, t_seq + 1);
if (sle->isFieldPresent (sfAccountTxnID)) if (sle->isFieldPresent (sfAccountTxnID))
sle->setFieldH256 (sfAccountTxnID, mTxn.getTransactionID ()); sle->setFieldH256 (sfAccountTxnID, tx().getTransactionID ());
return tesSUCCESS; return tesSUCCESS;
} }
// check stuff before you bother to lock the ledger // check stuff before you bother to lock the ledger
TER Transactor::preCheck () void Transactor::preCompute ()
{ {
TER result = preCheckAccount (); account_ = tx().getAccountID(sfAccount);
if (result != tesSUCCESS) assert(account_ != zero);
return result;
return preCheckSigningKey ();
}
TER Transactor::preCheckAccount ()
{
account_ = mTxn.getAccountID(sfAccount);
if (!account_)
{
j_.warning << "applyTransaction: bad transaction source id";
return temBAD_SRC_ACCOUNT;
}
return tesSUCCESS;
}
TER Transactor::preCheckSigningKey ()
{
// Extract signing key
// Transactions contain a signing key. This allows us to trivially verify a
// transaction has at least been properly signed without going to disk.
// Each transaction also notes a source account id. This is used to verify
// that the signing key is associated with the account.
// XXX This could be a lot cleaner to prevent unnecessary copying.
mSigningPubKey = mSigningPubKey =
RippleAddress::createAccountPublic (mTxn.getSigningPubKey ()); RippleAddress::createAccountPublic(
tx().getSigningPubKey());
// Consistency: really signed.
if (!mTxn.isKnownGood ())
{
if (mTxn.isKnownBad () ||
(!(view().flags() & tapNO_CHECK_SIGN) && !mTxn.checkSign(
(
#if RIPPLE_ENABLE_MULTI_SIGN
true
#else
view().flags() & tapENABLE_TESTING
#endif
))))
{
mTxn.setBad ();
j_.debug << "apply: Invalid transaction (bad signature)";
return temINVALID;
}
mTxn.setGood ();
}
return tesSUCCESS;
} }
TER Transactor::apply () TER Transactor::apply ()
{ {
// No point in going any further if the transaction fee is malformed. // No point in going any further if the transaction fee is malformed.
STAmount const saTxnFee = mTxn.getTransactionFee (); STAmount const saTxnFee = tx().getTransactionFee ();
if (!saTxnFee.native () || saTxnFee.negative () || !isLegalNet (saTxnFee)) if (!saTxnFee.native () || saTxnFee.negative () || !isLegalNet (saTxnFee))
return temBAD_FEE; return temBAD_FEE;
TER terResult = preCheck (); preCompute();
if (terResult != tesSUCCESS)
return terResult;
// Find source account // Find source account
auto const sle = view().peek (keylet::account(account_)); auto const sle = view().peek (keylet::account(account_));
@@ -229,9 +225,9 @@ TER Transactor::apply ()
{ {
if (mustHaveValidAccount ()) if (mustHaveValidAccount ())
{ {
j_.trace << JLOG(j_.trace) <<
"applyTransaction: delay: source account does not exist " << "applyTransaction: delay: source account does not exist " <<
toBase58(mTxn.getAccountID(sfAccount)); toBase58(tx().getAccountID(sfAccount));
return terNO_ACCOUNT; return terNO_ACCOUNT;
} }
} }
@@ -242,17 +238,17 @@ TER Transactor::apply ()
mHasAuthKey = sle->isFieldPresent (sfRegularKey); mHasAuthKey = sle->isFieldPresent (sfRegularKey);
} }
terResult = checkSeq (); auto terResult = checkSeq ();
if (terResult != tesSUCCESS) return (terResult); if (terResult != tesSUCCESS) return terResult;
terResult = payFee (); terResult = payFee ();
if (terResult != tesSUCCESS) return (terResult); if (terResult != tesSUCCESS) return terResult;
terResult = checkSign (); terResult = checkSign ();
if (terResult != tesSUCCESS) return (terResult); if (terResult != tesSUCCESS) return terResult;
if (sle) if (sle)
view().update (sle); view().update (sle);
@@ -514,7 +510,7 @@ TER Transactor::checkMultiSign ()
return outer.ter; return outer.ter;
// Get the actual array of transaction signers. // Get the actual array of transaction signers.
STArray const& multiSigners (mTxn.getFieldArray (sfMultiSigners)); STArray const& multiSigners (tx().getFieldArray (sfMultiSigners));
// Walk the accountSigners performing a variety of checks and see if // Walk the accountSigners performing a variety of checks and see if
// the quorum is met. // the quorum is met.
@@ -650,7 +646,7 @@ Transactor::operator()()
JLOG(j_.trace) << JLOG(j_.trace) <<
"applyTransaction>"; "applyTransaction>";
uint256 const& txID = mTxn.getTransactionID (); uint256 const& txID = tx().getTransactionID ();
if (!txID) if (!txID)
{ {
@@ -665,15 +661,15 @@ Transactor::operator()()
#ifdef BEAST_DEBUG #ifdef BEAST_DEBUG
{ {
Serializer ser; Serializer ser;
mTxn.add (ser); tx().add (ser);
SerialIter sit(ser.slice()); SerialIter sit(ser.slice());
STTx s2 (sit); STTx s2 (sit);
if (! s2.isEquivalent(mTxn)) if (! s2.isEquivalent(tx()))
{ {
JLOG(j_.fatal) << JLOG(j_.fatal) <<
"Transaction serdes mismatch"; "Transaction serdes mismatch";
JLOG(j_.info) << to_string(mTxn.getJson (0)); JLOG(j_.info) << to_string(tx().getJson (0));
JLOG(j_.fatal) << s2.getJson (0); JLOG(j_.fatal) << s2.getJson (0);
assert (false); assert (false);
} }
@@ -716,11 +712,11 @@ Transactor::operator()()
ctx_.discard(); ctx_.discard();
auto const txnAcct = view().peek( auto const txnAcct = view().peek(
keylet::account(mTxn.getAccountID(sfAccount))); keylet::account(tx().getAccountID(sfAccount)));
if (txnAcct) if (txnAcct)
{ {
std::uint32_t t_seq = mTxn.getSequence (); std::uint32_t t_seq = tx().getSequence ();
std::uint32_t a_seq = txnAcct->getFieldU32 (sfSequence); std::uint32_t a_seq = txnAcct->getFieldU32 (sfSequence);
if (a_seq < t_seq) if (a_seq < t_seq)
@@ -729,7 +725,7 @@ Transactor::operator()()
terResult = tefPAST_SEQ; terResult = tefPAST_SEQ;
else else
{ {
STAmount fee = mTxn.getTransactionFee (); STAmount fee = tx().getTransactionFee ();
STAmount balance = txnAcct->getFieldAmount (sfBalance); STAmount balance = txnAcct->getFieldAmount (sfBalance);
// We retry/reject the transaction if the account // We retry/reject the transaction if the account
@@ -774,7 +770,7 @@ Transactor::operator()()
// encapsulation of STAmount here and use "special // encapsulation of STAmount here and use "special
// knowledge" - namely that a native amount is // knowledge" - namely that a native amount is
// stored fully in the mantissa: // stored fully in the mantissa:
auto const fee = mTxn.getTransactionFee (); auto const fee = tx().getTransactionFee ();
// The transactor guarantees these will never trigger // The transactor guarantees these will never trigger
if (!fee.native () || fee.negative ()) if (!fee.native () || fee.negative ())

View File

@@ -25,10 +25,32 @@
namespace ripple { namespace ripple {
/** State information when preflighting a tx. */
struct PreflightContext
{
public:
explicit PreflightContext(STTx const& tx_,
Rules const& rules_, ApplyFlags flags_,
//SigVerify verify_,
beast::Journal j_ = {})
: tx(tx_)
, rules(rules_)
, flags(flags_)
//, verify(verify_)
, j(j_)
{
}
STTx const& tx;
Rules const& rules;
ApplyFlags flags;
//SigVerify verify;
beast::Journal j;
};
class Transactor class Transactor
{ {
protected: protected:
STTx const& mTxn;
ApplyContext& ctx_; ApplyContext& ctx_;
beast::Journal j_; beast::Journal j_;
@@ -57,6 +79,16 @@ public:
return ctx_.view(); return ctx_.view();
} }
STTx const&
tx() const
{
return ctx_.tx;
}
static
TER
preflight (PreflightContext const& ctx);
protected: protected:
TER TER
apply(); apply();
@@ -64,8 +96,6 @@ protected:
explicit explicit
Transactor (ApplyContext& ctx); Transactor (ApplyContext& ctx);
TER preCheckAccount ();
TER preCheckSigningKey ();
void calculateFee (); void calculateFee ();
// VFALCO This is the equivalent of dynamic_cast // VFALCO This is the equivalent of dynamic_cast
@@ -81,7 +111,7 @@ protected:
// Returns the fee, not scaled for load (Should be in fee units. FIXME) // Returns the fee, not scaled for load (Should be in fee units. FIXME)
virtual std::uint64_t calculateBaseFee (); virtual std::uint64_t calculateBaseFee ();
virtual TER preCheck (); virtual void preCompute();
virtual TER checkSeq (); virtual TER checkSeq ();
virtual TER payFee (); virtual TER payFee ();
virtual TER checkSign (); virtual TER checkSign ();

View File

@@ -20,7 +20,6 @@
#include <BeastConfig.h> #include <BeastConfig.h>
#include <ripple/app/tx/apply.h> #include <ripple/app/tx/apply.h>
#include <ripple/app/tx/impl/ApplyContext.h> #include <ripple/app/tx/impl/ApplyContext.h>
#include <ripple/app/tx/impl/CancelOffer.h> #include <ripple/app/tx/impl/CancelOffer.h>
#include <ripple/app/tx/impl/CancelTicket.h> #include <ripple/app/tx/impl/CancelTicket.h>
#include <ripple/app/tx/impl/Change.h> #include <ripple/app/tx/impl/Change.h>
@@ -34,68 +33,112 @@
namespace ripple { namespace ripple {
template <class Processor,
class... Args>
static static
std::pair<TER, bool> TER
do_apply (Args&&... args) invoke_preflight (PreflightContext const& ctx)
{ {
ApplyContext ctx ( switch(ctx.tx.getTxnType())
std::forward<Args>(args)...);
Processor p(ctx);
return p();
}
template <class... Args>
static
std::pair<TER, bool>
invoke (TxType type,
Args&&... args)
{
switch(type)
{ {
case ttACCOUNT_SET: return do_apply< SetAccount >(std::forward<Args>(args)...); case ttACCOUNT_SET: return SetAccount ::preflight(ctx);
case ttOFFER_CANCEL: return do_apply< CancelOffer >(std::forward<Args>(args)...); case ttOFFER_CANCEL: return CancelOffer ::preflight(ctx);
case ttOFFER_CREATE: return do_apply< CreateOffer >(std::forward<Args>(args)...); case ttOFFER_CREATE: return CreateOffer ::preflight(ctx);
case ttPAYMENT: return do_apply< Payment >(std::forward<Args>(args)...); case ttPAYMENT: return Payment ::preflight(ctx);
case ttREGULAR_KEY_SET: return do_apply< SetRegularKey >(std::forward<Args>(args)...); case ttREGULAR_KEY_SET: return SetRegularKey ::preflight(ctx);
case ttSIGNER_LIST_SET: return do_apply< SetSignerList >(std::forward<Args>(args)...); case ttSIGNER_LIST_SET: return SetSignerList ::preflight(ctx);
case ttTICKET_CANCEL: return do_apply< CancelTicket >(std::forward<Args>(args)...); case ttTICKET_CANCEL: return CancelTicket ::preflight(ctx);
case ttTICKET_CREATE: return do_apply< CreateTicket >(std::forward<Args>(args)...); case ttTICKET_CREATE: return CreateTicket ::preflight(ctx);
case ttTRUST_SET: return do_apply< SetTrust >(std::forward<Args>(args)...); case ttTRUST_SET: return SetTrust ::preflight(ctx);
// VFALCO These are both the same?
case ttAMENDMENT: case ttAMENDMENT:
case ttFEE: return do_apply< Change >(std::forward<Args>(args)...); case ttFEE: return Change ::preflight(ctx);
default: default:
break; return temUNKNOWN;
} }
return { temUNKNOWN, false };
} }
static
std::pair<TER, bool> std::pair<TER, bool>
apply (OpenView& view, invoke_apply (ApplyContext& ctx)
STTx const& tx, ApplyFlags flags, {
Config const& config, switch(ctx.tx.getTxnType())
beast::Journal j) {
case ttACCOUNT_SET: { SetAccount p(ctx); return p(); }
case ttOFFER_CANCEL: { CancelOffer p(ctx); return p(); }
case ttOFFER_CREATE: { CreateOffer p(ctx); return p(); }
case ttPAYMENT: { Payment p(ctx); return p(); }
case ttREGULAR_KEY_SET: { SetRegularKey p(ctx); return p(); }
case ttSIGNER_LIST_SET: { SetSignerList p(ctx); return p(); }
case ttTICKET_CANCEL: { CancelTicket p(ctx); return p(); }
case ttTICKET_CREATE: { CreateTicket p(ctx); return p(); }
case ttTRUST_SET: { SetTrust p(ctx); return p(); }
case ttAMENDMENT:
case ttFEE: { Change p(ctx); return p(); }
default:
return { temUNKNOWN, false };
}
}
//------------------------------------------------------------------------------
TER
preflight (Rules const& rules, STTx const& tx,
ApplyFlags flags,
Config const& config, beast::Journal j)
{ {
try try
{ {
return invoke (tx.getTxnType(), PreflightContext pfctx(
view, tx, flags, config, j); tx, rules, flags, j);
return invoke_preflight(pfctx);
} }
catch(std::exception const& e) catch (std::exception const& e)
{ {
JLOG(j.fatal) << JLOG(j.fatal) <<
"Caught exception: " << e.what(); "apply: " << e.what();
return tefEXCEPTION;
}
catch (...)
{
JLOG(j.fatal) <<
"apply: <unknown exception>";
return tefEXCEPTION;
}
}
std::pair<TER, bool>
doapply(OpenView& view, STTx const& tx,
ApplyFlags flags, Config const& config,
beast::Journal j)
{
try
{
ApplyContext ctx(
view, tx, flags, config, j);
return invoke_apply(ctx);
}
catch (std::exception const& e)
{
JLOG(j.fatal) <<
"apply: " << e.what();
return { tefEXCEPTION, false }; return { tefEXCEPTION, false };
} }
catch(...) catch (...)
{ {
JLOG(j.fatal) << JLOG(j.fatal) <<
"Caught unknown exception"; "apply: <unknown exception>";
return { tefEXCEPTION, false }; return { tefEXCEPTION, false };
} }
} }
std::pair<TER, bool>
apply (OpenView& view, STTx const& tx,
ApplyFlags flags,
Config const& config, beast::Journal j)
{
auto pfresult = preflight(view.rules(),
tx, flags, config, j);
if (pfresult != tesSUCCESS)
return { pfresult, false };
return doapply(view, tx, flags, config, j);
}
} // ripple } // ripple