mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-01 16:35:53 +00:00
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:
@@ -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.
|
||||||
|
|||||||
@@ -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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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_));
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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?
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 ();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 ())
|
||||||
|
|||||||
@@ -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 ();
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user