mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Refactor View, MetaView, and tx processing:
This tidies up the View interface and makes transaction application a free function, with the removal of the TransactionEngine class. A new class ApplyContext provides all the state information needed to apply a Transactor. The Transactor is refactored to perform all the processing activities previously part of TransactionEngine. The calculation of metadata from a MetaView is improved. A new apply function performs all the steps for calculating and inserting metadata into the tx map. Transaction processing code path is passed a Config instead of retrieving the global, and uses the Journal supplied in the call to apply() consistently. To support transaction processing and RPC operations, a new POD type ViewInfo is added which consolidates static information about open and closed ledgers, such as the ledger sequence number or the closing times. Ledger and MetaView are refactored to use this info. The ViewInfo now contains the "open ledger" setting. The tapOPEN_LEDGER ViewFlag is removed. The view property of being an open ledger is obtained from the base or by using the MetaView constructor which presents a closed ledger as an open one. View, MetaView: * Fix missing includes * Add apply free function * Use Journal in TransactionEngine * Use BasicView in TransactionEngine * inline NetworkOPs::batchApply * Add shallow_copy, open_ledger MetaView ctor tags * Add ViewInfo with open flag, seq, close times * Make parent_ a reference * Tidy up ctor arguments and base_ name * Remove tapOPEN_LEDGER * add assert to MetaView::apply * ViewInfo comment * Throw, pass Journal in txInsert * Add BasicView::txCount TransactionEngine: * Add apply * Make TransactionEngine private * Refactor MetaView::apply and apply() * Rename to TxMeta * Refactor treatment of metadata in MetaView, TransactionEngine * Rename to ApplyContext * Use ApplyContext& in Transactor * Pass Config in ApplyContext * Declare Transactor classes in headers * Use view flags in Transactor
This commit is contained in:
@@ -18,8 +18,8 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/tx/impl/Payment.h>
|
||||
#include <ripple/app/paths/RippleCalc.h>
|
||||
#include <ripple/app/tx/impl/Transactor.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
|
||||
@@ -27,379 +27,349 @@ namespace ripple {
|
||||
|
||||
// See https://ripple.com/wiki/Transaction_Format#Payment_.280.29
|
||||
|
||||
class Payment
|
||||
: public Transactor
|
||||
TER
|
||||
Payment::preCheck ()
|
||||
{
|
||||
/* The largest number of paths we allow */
|
||||
static std::size_t const MaxPathSize = 6;
|
||||
std::uint32_t const uTxFlags = mTxn.getFlags ();
|
||||
|
||||
/* The longest path we allow */
|
||||
static std::size_t const MaxPathLength = 8;
|
||||
|
||||
public:
|
||||
Payment (
|
||||
STTx const& txn,
|
||||
ViewFlags params,
|
||||
TransactionEngine* engine)
|
||||
: Transactor (
|
||||
txn,
|
||||
params,
|
||||
engine,
|
||||
deprecatedLogs().journal("Payment"))
|
||||
if (uTxFlags & tfPaymentMask)
|
||||
{
|
||||
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
TER preCheck () override
|
||||
bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
|
||||
bool const limitQuality = uTxFlags & tfLimitQuality;
|
||||
bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
|
||||
bool const bPaths = mTxn.isFieldPresent (sfPaths);
|
||||
bool const bMax = mTxn.isFieldPresent (sfSendMax);
|
||||
|
||||
STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount));
|
||||
|
||||
STAmount maxSourceAmount;
|
||||
|
||||
if (bMax)
|
||||
maxSourceAmount = mTxn.getFieldAmount (sfSendMax);
|
||||
else if (saDstAmount.native ())
|
||||
maxSourceAmount = saDstAmount;
|
||||
else
|
||||
maxSourceAmount = STAmount (
|
||||
{ saDstAmount.getCurrency (), mTxnAccountID },
|
||||
saDstAmount.mantissa(), saDstAmount.exponent (),
|
||||
saDstAmount < zero);
|
||||
|
||||
auto const& uSrcCurrency = maxSourceAmount.getCurrency ();
|
||||
auto const& uDstCurrency = saDstAmount.getCurrency ();
|
||||
|
||||
// isZero() is XRP. FIX!
|
||||
bool const bXRPDirect = uSrcCurrency.isZero () && uDstCurrency.isZero ();
|
||||
|
||||
if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
AccountID const uDstAccountID (mTxn.getAccountID (sfDestination));
|
||||
|
||||
if (!uDstAccountID)
|
||||
{
|
||||
std::uint32_t const uTxFlags = mTxn.getFlags ();
|
||||
|
||||
if (uTxFlags & tfPaymentMask)
|
||||
{
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"Invalid flags set.";
|
||||
return temINVALID_FLAG;
|
||||
}
|
||||
|
||||
bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
|
||||
bool const limitQuality = uTxFlags & tfLimitQuality;
|
||||
bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
|
||||
bool const bPaths = mTxn.isFieldPresent (sfPaths);
|
||||
bool const bMax = mTxn.isFieldPresent (sfSendMax);
|
||||
|
||||
STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount));
|
||||
|
||||
STAmount maxSourceAmount;
|
||||
|
||||
if (bMax)
|
||||
maxSourceAmount = mTxn.getFieldAmount (sfSendMax);
|
||||
else if (saDstAmount.native ())
|
||||
maxSourceAmount = saDstAmount;
|
||||
else
|
||||
maxSourceAmount = STAmount (
|
||||
{ saDstAmount.getCurrency (), mTxnAccountID },
|
||||
saDstAmount.mantissa(), saDstAmount.exponent (),
|
||||
saDstAmount < zero);
|
||||
|
||||
auto const& uSrcCurrency = maxSourceAmount.getCurrency ();
|
||||
auto const& uDstCurrency = saDstAmount.getCurrency ();
|
||||
|
||||
// isZero() is XRP. FIX!
|
||||
bool const bXRPDirect = uSrcCurrency.isZero () && uDstCurrency.isZero ();
|
||||
|
||||
if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount))
|
||||
return temBAD_AMOUNT;
|
||||
|
||||
AccountID const uDstAccountID (mTxn.getAccountID (sfDestination));
|
||||
|
||||
if (!uDstAccountID)
|
||||
{
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"Payment destination account not specified.";
|
||||
return temDST_NEEDED;
|
||||
}
|
||||
if (bMax && maxSourceAmount <= zero)
|
||||
{
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"bad max amount: " << maxSourceAmount.getFullText ();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (saDstAmount <= zero)
|
||||
{
|
||||
m_journal.trace << "Malformed transaction: "<<
|
||||
"bad dst amount: " << saDstAmount.getFullText ();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency)
|
||||
{
|
||||
m_journal.trace <<"Malformed transaction: " <<
|
||||
"Bad currency.";
|
||||
return temBAD_CURRENCY;
|
||||
}
|
||||
if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths)
|
||||
{
|
||||
// You're signing yourself a payment.
|
||||
// If bPaths is true, you might be trying some arbitrage.
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"Redundant payment from " << to_string (mTxnAccountID) <<
|
||||
" to self without path for " << to_string (uDstCurrency);
|
||||
return temREDUNDANT;
|
||||
}
|
||||
if (bXRPDirect && bMax)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"SendMax specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_MAX;
|
||||
}
|
||||
if (bXRPDirect && bPaths)
|
||||
{
|
||||
// XRP is sent without paths.
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"Paths specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_PATHS;
|
||||
}
|
||||
if (bXRPDirect && partialPaymentAllowed)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"Partial payment specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_PARTIAL;
|
||||
}
|
||||
if (bXRPDirect && limitQuality)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"Limit quality specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_LIMIT;
|
||||
}
|
||||
if (bXRPDirect && !defaultPathsAllowed)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
m_journal.trace << "Malformed transaction: " <<
|
||||
"No ripple direct specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_NO_DIRECT;
|
||||
}
|
||||
|
||||
return Transactor::preCheck ();
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"Payment destination account not specified.";
|
||||
return temDST_NEEDED;
|
||||
}
|
||||
if (bMax && maxSourceAmount <= zero)
|
||||
{
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"bad max amount: " << maxSourceAmount.getFullText ();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (saDstAmount <= zero)
|
||||
{
|
||||
j_.trace << "Malformed transaction: "<<
|
||||
"bad dst amount: " << saDstAmount.getFullText ();
|
||||
return temBAD_AMOUNT;
|
||||
}
|
||||
if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency)
|
||||
{
|
||||
j_.trace <<"Malformed transaction: " <<
|
||||
"Bad currency.";
|
||||
return temBAD_CURRENCY;
|
||||
}
|
||||
if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths)
|
||||
{
|
||||
// You're signing yourself a payment.
|
||||
// If bPaths is true, you might be trying some arbitrage.
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"Redundant payment from " << to_string (mTxnAccountID) <<
|
||||
" to self without path for " << to_string (uDstCurrency);
|
||||
return temREDUNDANT;
|
||||
}
|
||||
if (bXRPDirect && bMax)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"SendMax specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_MAX;
|
||||
}
|
||||
if (bXRPDirect && bPaths)
|
||||
{
|
||||
// XRP is sent without paths.
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"Paths specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_PATHS;
|
||||
}
|
||||
if (bXRPDirect && partialPaymentAllowed)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"Partial payment specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_PARTIAL;
|
||||
}
|
||||
if (bXRPDirect && limitQuality)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"Limit quality specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_LIMIT;
|
||||
}
|
||||
if (bXRPDirect && !defaultPathsAllowed)
|
||||
{
|
||||
// Consistent but redundant transaction.
|
||||
j_.trace << "Malformed transaction: " <<
|
||||
"No ripple direct specified for XRP to XRP.";
|
||||
return temBAD_SEND_XRP_NO_DIRECT;
|
||||
}
|
||||
|
||||
TER doApply () override
|
||||
return Transactor::preCheck ();
|
||||
}
|
||||
|
||||
TER
|
||||
Payment::doApply ()
|
||||
{
|
||||
// Ripple if source or destination is non-native or if there are paths.
|
||||
std::uint32_t const uTxFlags = mTxn.getFlags ();
|
||||
bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
|
||||
bool const limitQuality = uTxFlags & tfLimitQuality;
|
||||
bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
|
||||
bool const bPaths = mTxn.isFieldPresent (sfPaths);
|
||||
bool const bMax = mTxn.isFieldPresent (sfSendMax);
|
||||
AccountID const uDstAccountID (mTxn.getAccountID (sfDestination));
|
||||
STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount));
|
||||
STAmount maxSourceAmount;
|
||||
if (bMax)
|
||||
maxSourceAmount = mTxn.getFieldAmount (sfSendMax);
|
||||
else if (saDstAmount.native ())
|
||||
maxSourceAmount = saDstAmount;
|
||||
else
|
||||
maxSourceAmount = STAmount (
|
||||
{saDstAmount.getCurrency (), mTxnAccountID},
|
||||
saDstAmount.mantissa(), saDstAmount.exponent (),
|
||||
saDstAmount < zero);
|
||||
|
||||
j_.trace <<
|
||||
"maxSourceAmount=" << maxSourceAmount.getFullText () <<
|
||||
" saDstAmount=" << saDstAmount.getFullText ();
|
||||
|
||||
// Open a ledger for editing.
|
||||
auto const k = keylet::account(uDstAccountID);
|
||||
SLE::pointer sleDst = view().peek (k);
|
||||
|
||||
if (!sleDst)
|
||||
{
|
||||
// Ripple if source or destination is non-native or if there are paths.
|
||||
std::uint32_t const uTxFlags = mTxn.getFlags ();
|
||||
bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
|
||||
bool const limitQuality = uTxFlags & tfLimitQuality;
|
||||
bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
|
||||
bool const bPaths = mTxn.isFieldPresent (sfPaths);
|
||||
bool const bMax = mTxn.isFieldPresent (sfSendMax);
|
||||
AccountID const uDstAccountID (mTxn.getAccountID (sfDestination));
|
||||
STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount));
|
||||
STAmount maxSourceAmount;
|
||||
if (bMax)
|
||||
maxSourceAmount = mTxn.getFieldAmount (sfSendMax);
|
||||
else if (saDstAmount.native ())
|
||||
maxSourceAmount = saDstAmount;
|
||||
else
|
||||
maxSourceAmount = STAmount (
|
||||
{saDstAmount.getCurrency (), mTxnAccountID},
|
||||
saDstAmount.mantissa(), saDstAmount.exponent (),
|
||||
saDstAmount < zero);
|
||||
|
||||
m_journal.trace <<
|
||||
"maxSourceAmount=" << maxSourceAmount.getFullText () <<
|
||||
" saDstAmount=" << saDstAmount.getFullText ();
|
||||
|
||||
// Open a ledger for editing.
|
||||
auto const k = keylet::account(uDstAccountID);
|
||||
SLE::pointer sleDst = mEngine->view().peek (k);
|
||||
|
||||
if (!sleDst)
|
||||
// Destination account does not exist.
|
||||
if (!saDstAmount.native ())
|
||||
{
|
||||
// Destination account does not exist.
|
||||
if (!saDstAmount.native ())
|
||||
{
|
||||
m_journal.trace <<
|
||||
"Delay transaction: Destination account does not exist.";
|
||||
j_.trace <<
|
||||
"Delay transaction: Destination account does not exist.";
|
||||
|
||||
// Another transaction could create the account and then this
|
||||
// transaction would succeed.
|
||||
return tecNO_DST;
|
||||
}
|
||||
else if (mParams & tapOPEN_LEDGER && partialPaymentAllowed)
|
||||
{
|
||||
// You cannot fund an account with a partial payment.
|
||||
// Make retry work smaller, by rejecting this.
|
||||
m_journal.trace <<
|
||||
"Delay transaction: Partial payment not allowed to create account.";
|
||||
|
||||
|
||||
// Another transaction could create the account and then this
|
||||
// transaction would succeed.
|
||||
return telNO_DST_PARTIAL;
|
||||
}
|
||||
else if (saDstAmount < STAmount (mEngine->view().fees().accountReserve(0)))
|
||||
{
|
||||
// getReserve() is the minimum amount that an account can have.
|
||||
// Reserve is not scaled by load.
|
||||
m_journal.trace <<
|
||||
"Delay transaction: Destination account does not exist. " <<
|
||||
"Insufficent payment to create account.";
|
||||
|
||||
// TODO: dedupe
|
||||
// Another transaction could create the account and then this
|
||||
// transaction would succeed.
|
||||
return tecNO_DST_INSUF_XRP;
|
||||
}
|
||||
|
||||
// Create the account.
|
||||
sleDst = std::make_shared<SLE>(k);
|
||||
sleDst->setAccountID (sfAccount, uDstAccountID);
|
||||
sleDst->setFieldU32 (sfSequence, 1);
|
||||
mEngine->view().insert(sleDst);
|
||||
// Another transaction could create the account and then this
|
||||
// transaction would succeed.
|
||||
return tecNO_DST;
|
||||
}
|
||||
else if ((sleDst->getFlags () & lsfRequireDestTag) &&
|
||||
!mTxn.isFieldPresent (sfDestinationTag))
|
||||
else if (view().open()
|
||||
&& partialPaymentAllowed)
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
// You cannot fund an account with a partial payment.
|
||||
// Make retry work smaller, by rejecting this.
|
||||
j_.trace <<
|
||||
"Delay transaction: Partial payment not allowed to create account.";
|
||||
|
||||
// We didn't make this test for a newly-formed account because there's
|
||||
// no way for this field to be set.
|
||||
m_journal.trace << "Malformed transaction: DestinationTag required.";
|
||||
|
||||
return tecDST_TAG_NEEDED;
|
||||
// Another transaction could create the account and then this
|
||||
// transaction would succeed.
|
||||
return telNO_DST_PARTIAL;
|
||||
}
|
||||
else
|
||||
else if (saDstAmount < STAmount (view().fees().accountReserve(0)))
|
||||
{
|
||||
// Tell the engine that we are intending to change the the destination
|
||||
// account. The source account gets always charged a fee so it's always
|
||||
// marked as modified.
|
||||
mEngine->view().update (sleDst);
|
||||
// getReserve() is the minimum amount that an account can have.
|
||||
// Reserve is not scaled by load.
|
||||
j_.trace <<
|
||||
"Delay transaction: Destination account does not exist. " <<
|
||||
"Insufficent payment to create account.";
|
||||
|
||||
// TODO: dedupe
|
||||
// Another transaction could create the account and then this
|
||||
// transaction would succeed.
|
||||
return tecNO_DST_INSUF_XRP;
|
||||
}
|
||||
|
||||
TER terResult;
|
||||
// Create the account.
|
||||
sleDst = std::make_shared<SLE>(k);
|
||||
sleDst->setAccountID (sfAccount, uDstAccountID);
|
||||
sleDst->setFieldU32 (sfSequence, 1);
|
||||
view().insert(sleDst);
|
||||
}
|
||||
else if ((sleDst->getFlags () & lsfRequireDestTag) &&
|
||||
!mTxn.isFieldPresent (sfDestinationTag))
|
||||
{
|
||||
// The tag is basically account-specific information we don't
|
||||
// understand, but we can require someone to fill it in.
|
||||
|
||||
bool const bRipple = bPaths || bMax || !saDstAmount.native ();
|
||||
// XXX Should bMax be sufficient to imply ripple?
|
||||
// We didn't make this test for a newly-formed account because there's
|
||||
// no way for this field to be set.
|
||||
j_.trace << "Malformed transaction: DestinationTag required.";
|
||||
|
||||
if (bRipple)
|
||||
return tecDST_TAG_NEEDED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tell the engine that we are intending to change the the destination
|
||||
// account. The source account gets always charged a fee so it's always
|
||||
// marked as modified.
|
||||
view().update (sleDst);
|
||||
}
|
||||
|
||||
TER terResult;
|
||||
|
||||
bool const bRipple = bPaths || bMax || !saDstAmount.native ();
|
||||
// XXX Should bMax be sufficient to imply ripple?
|
||||
|
||||
if (bRipple)
|
||||
{
|
||||
// Ripple payment with at least one intermediate step and uses
|
||||
// transitive balances.
|
||||
|
||||
// Copy paths into an editable class.
|
||||
STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);
|
||||
|
||||
try
|
||||
{
|
||||
// Ripple payment with at least one intermediate step and uses
|
||||
// transitive balances.
|
||||
path::RippleCalc::Input rcInput;
|
||||
rcInput.partialPaymentAllowed = partialPaymentAllowed;
|
||||
rcInput.defaultPathsAllowed = defaultPathsAllowed;
|
||||
rcInput.limitQuality = limitQuality;
|
||||
rcInput.deleteUnfundedOffers = true;
|
||||
rcInput.isLedgerOpen = view().open();
|
||||
|
||||
// Copy paths into an editable class.
|
||||
STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);
|
||||
bool pathTooBig = spsPaths.size () > MaxPathSize;
|
||||
|
||||
try
|
||||
for (auto const& path : spsPaths)
|
||||
if (path.size () > MaxPathLength)
|
||||
pathTooBig = true;
|
||||
|
||||
if (rcInput.isLedgerOpen && pathTooBig)
|
||||
{
|
||||
path::RippleCalc::Input rcInput;
|
||||
rcInput.partialPaymentAllowed = partialPaymentAllowed;
|
||||
rcInput.defaultPathsAllowed = defaultPathsAllowed;
|
||||
rcInput.limitQuality = limitQuality;
|
||||
rcInput.deleteUnfundedOffers = true;
|
||||
rcInput.isLedgerOpen = static_cast<bool>(mParams & tapOPEN_LEDGER);
|
||||
|
||||
bool pathTooBig = spsPaths.size () > MaxPathSize;
|
||||
|
||||
for (auto const& path : spsPaths)
|
||||
if (path.size () > MaxPathLength)
|
||||
pathTooBig = true;
|
||||
|
||||
if (rcInput.isLedgerOpen && pathTooBig)
|
||||
{
|
||||
terResult = telBAD_PATH_COUNT; // Too many paths for proposed ledger.
|
||||
}
|
||||
else
|
||||
{
|
||||
path::RippleCalc::Output rc;
|
||||
{
|
||||
PaymentView view (&mEngine->view());
|
||||
rc = path::RippleCalc::rippleCalculate (
|
||||
view,
|
||||
maxSourceAmount,
|
||||
saDstAmount,
|
||||
uDstAccountID,
|
||||
mTxnAccountID,
|
||||
spsPaths,
|
||||
&rcInput);
|
||||
// VFALCO NOTE We might not need to apply, depending
|
||||
// on the TER. But always applying *should*
|
||||
// be safe.
|
||||
view.apply(mEngine->view());
|
||||
}
|
||||
|
||||
// TODO: is this right? If the amount is the correct amount, was
|
||||
// the delivered amount previously set?
|
||||
if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount)
|
||||
mEngine->deliverAmount (rc.actualAmountOut);
|
||||
|
||||
terResult = rc.result ();
|
||||
}
|
||||
|
||||
// TODO(tom): what's going on here?
|
||||
if (isTerRetry (terResult))
|
||||
terResult = tecPATH_DRY;
|
||||
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
m_journal.trace <<
|
||||
"Caught throw: " << e.what ();
|
||||
|
||||
terResult = tefEXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Direct XRP payment.
|
||||
|
||||
// uOwnerCount is the number of entries in this legder for this
|
||||
// account that require a reserve.
|
||||
auto const uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);
|
||||
|
||||
// This is the total reserve in drops.
|
||||
auto const uReserve =
|
||||
mEngine->view().fees().accountReserve(uOwnerCount);
|
||||
|
||||
// mPriorBalance is the balance on the sending account BEFORE the
|
||||
// fees were charged. We want to make sure we have enough reserve
|
||||
// to send. Allow final spend to use reserve for fee.
|
||||
auto const mmm = std::max(mTxn.getTransactionFee (),
|
||||
STAmount (uReserve));
|
||||
|
||||
if (mPriorBalance < saDstAmount + mmm)
|
||||
{
|
||||
// Vote no. However the transaction might succeed, if applied in
|
||||
// a different order.
|
||||
m_journal.trace << "Delay transaction: Insufficient funds: " <<
|
||||
" " << mPriorBalance.getText () <<
|
||||
" / " << (saDstAmount + mmm).getText () <<
|
||||
" (" << uReserve << ")";
|
||||
|
||||
terResult = tecUNFUNDED_PAYMENT;
|
||||
terResult = telBAD_PATH_COUNT; // Too many paths for proposed ledger.
|
||||
}
|
||||
else
|
||||
{
|
||||
// The source account does have enough money, so do the
|
||||
// arithmetic for the transfer and make the ledger change.
|
||||
mTxnAccount->setFieldAmount (sfBalance,
|
||||
mSourceBalance - saDstAmount);
|
||||
sleDst->setFieldAmount (sfBalance,
|
||||
sleDst->getFieldAmount (sfBalance) + saDstAmount);
|
||||
path::RippleCalc::Output rc;
|
||||
{
|
||||
PaymentView pv (view(), view().flags());
|
||||
rc = path::RippleCalc::rippleCalculate (
|
||||
pv,
|
||||
maxSourceAmount,
|
||||
saDstAmount,
|
||||
uDstAccountID,
|
||||
mTxnAccountID,
|
||||
spsPaths,
|
||||
&rcInput);
|
||||
// VFALCO NOTE We might not need to apply, depending
|
||||
// on the TER. But always applying *should*
|
||||
// be safe.
|
||||
pv.apply(view());
|
||||
}
|
||||
|
||||
// Re-arm the password change fee if we can and need to.
|
||||
if ((sleDst->getFlags () & lsfPasswordSpent))
|
||||
sleDst->clearFlag (lsfPasswordSpent);
|
||||
// TODO: is this right? If the amount is the correct amount, was
|
||||
// the delivered amount previously set?
|
||||
if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount)
|
||||
ctx_.deliverAmount (rc.actualAmountOut);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
terResult = rc.result ();
|
||||
}
|
||||
|
||||
// TODO(tom): what's going on here?
|
||||
if (isTerRetry (terResult))
|
||||
terResult = tecPATH_DRY;
|
||||
|
||||
}
|
||||
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
if (transResultInfo (terResult, strToken, strHuman))
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
m_journal.trace <<
|
||||
strToken << ": " << strHuman;
|
||||
j_.trace <<
|
||||
"Caught throw: " << e.what ();
|
||||
|
||||
terResult = tefEXCEPTION;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Direct XRP payment.
|
||||
|
||||
// uOwnerCount is the number of entries in this legder for this
|
||||
// account that require a reserve.
|
||||
auto const uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);
|
||||
|
||||
// This is the total reserve in drops.
|
||||
auto const uReserve =
|
||||
view().fees().accountReserve(uOwnerCount);
|
||||
|
||||
// mPriorBalance is the balance on the sending account BEFORE the
|
||||
// fees were charged. We want to make sure we have enough reserve
|
||||
// to send. Allow final spend to use reserve for fee.
|
||||
auto const mmm = std::max(mTxn.getTransactionFee (),
|
||||
STAmount (uReserve));
|
||||
|
||||
if (mPriorBalance < saDstAmount + mmm)
|
||||
{
|
||||
// Vote no. However the transaction might succeed, if applied in
|
||||
// a different order.
|
||||
j_.trace << "Delay transaction: Insufficient funds: " <<
|
||||
" " << mPriorBalance.getText () <<
|
||||
" / " << (saDstAmount + mmm).getText () <<
|
||||
" (" << uReserve << ")";
|
||||
|
||||
terResult = tecUNFUNDED_PAYMENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (false);
|
||||
// The source account does have enough money, so do the
|
||||
// arithmetic for the transfer and make the ledger change.
|
||||
mTxnAccount->setFieldAmount (sfBalance,
|
||||
mSourceBalance - saDstAmount);
|
||||
sleDst->setFieldAmount (sfBalance,
|
||||
sleDst->getFieldAmount (sfBalance) + saDstAmount);
|
||||
|
||||
// Re-arm the password change fee if we can and need to.
|
||||
if ((sleDst->getFlags () & lsfPasswordSpent))
|
||||
sleDst->clearFlag (lsfPasswordSpent);
|
||||
|
||||
terResult = tesSUCCESS;
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
};
|
||||
|
||||
TER
|
||||
transact_Payment (
|
||||
STTx const& txn,
|
||||
ViewFlags params,
|
||||
TransactionEngine* engine)
|
||||
{
|
||||
return Payment(txn, params, engine).apply ();
|
||||
std::string strToken;
|
||||
std::string strHuman;
|
||||
|
||||
if (transResultInfo (terResult, strToken, strHuman))
|
||||
{
|
||||
j_.trace <<
|
||||
strToken << ": " << strHuman;
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (false);
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
Reference in New Issue
Block a user