mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-04 18:55:49 +00:00
Add delivered amount to the ledger RPC command
This commit is contained in:
@@ -1998,6 +1998,7 @@ else ()
|
||||
src/ripple/rpc/handlers/ValidatorListSites.cpp
|
||||
src/ripple/rpc/handlers/Validators.cpp
|
||||
src/ripple/rpc/handlers/WalletPropose.cpp
|
||||
src/ripple/rpc/impl/DeliveredAmount.cpp
|
||||
src/ripple/rpc/impl/Handler.cpp
|
||||
src/ripple/rpc/impl/LegacyPathFind.cpp
|
||||
src/ripple/rpc/impl/RPCHandler.cpp
|
||||
@@ -2268,6 +2269,7 @@ else ()
|
||||
src/test/rpc/AmendmentBlocked_test.cpp
|
||||
src/test/rpc/Book_test.cpp
|
||||
src/test/rpc/DepositAuthorized_test.cpp
|
||||
src/test/rpc/DeliveredAmount_test.cpp
|
||||
src/test/rpc/Feature_test.cpp
|
||||
src/test/rpc/GatewayBalances_test.cpp
|
||||
src/test/rpc/GetCounts_test.cpp
|
||||
|
||||
@@ -35,8 +35,8 @@ AcceptedLedgerTx::AcceptedLedgerTx (
|
||||
: mLedger (ledger)
|
||||
, mTxn (txn)
|
||||
, mMeta (std::make_shared<TxMeta> (
|
||||
txn->getTransactionID(), ledger->seq(), *met, logs.journal ("View")))
|
||||
, mAffected (mMeta->getAffectedAccounts ())
|
||||
txn->getTransactionID(), ledger->seq(), *met))
|
||||
, mAffected (mMeta->getAffectedAccounts (logs.journal("View")))
|
||||
, accountCache_ (accountCache)
|
||||
, logs_ (logs)
|
||||
{
|
||||
|
||||
@@ -170,13 +170,13 @@ log_metadata_difference(
|
||||
uint256 const& tx,
|
||||
beast::Journal j)
|
||||
{
|
||||
auto getMeta = [j](ReadView const& ledger,
|
||||
auto getMeta = [](ReadView const& ledger,
|
||||
uint256 const& txID) -> std::shared_ptr<TxMeta>
|
||||
{
|
||||
auto meta = ledger.txRead(txID).second;
|
||||
if (!meta)
|
||||
return {};
|
||||
return std::make_shared<TxMeta> (txID, ledger.seq(), *meta, j);
|
||||
return std::make_shared<TxMeta> (txID, ledger.seq(), *meta);
|
||||
};
|
||||
|
||||
auto validMetaData = getMeta (validLedger, tx);
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/basics/date.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -97,49 +99,63 @@ void fillJsonBinary(Object& json, bool closed, LedgerInfo const& info)
|
||||
}
|
||||
}
|
||||
|
||||
Json::Value fillJsonTx (LedgerFill const& fill,
|
||||
bool bBinary, bool bExpanded,
|
||||
std::pair<std::shared_ptr<STTx const>,
|
||||
std::shared_ptr<STObject const>> const i)
|
||||
Json::Value
|
||||
fillJsonTx(
|
||||
LedgerFill const& fill,
|
||||
bool bBinary,
|
||||
bool bExpanded,
|
||||
std::shared_ptr<STTx const> const& txn,
|
||||
std::shared_ptr<STObject const> const& stMeta)
|
||||
{
|
||||
if (! bExpanded)
|
||||
if (!bExpanded)
|
||||
return to_string(txn->getTransactionID());
|
||||
|
||||
Json::Value txJson{Json::objectValue};
|
||||
auto const txnType = txn->getTxnType();
|
||||
if (bBinary)
|
||||
{
|
||||
return to_string(i.first->getTransactionID());
|
||||
txJson[jss::tx_blob] = serializeHex(*txn);
|
||||
if (stMeta)
|
||||
txJson[jss::meta] = serializeHex(*stMeta);
|
||||
}
|
||||
else
|
||||
{
|
||||
Json::Value txJson{ Json::objectValue };
|
||||
if (bBinary)
|
||||
copyFrom(txJson, txn->getJson(0));
|
||||
if (stMeta)
|
||||
{
|
||||
txJson[jss::tx_blob] = serializeHex(*i.first);
|
||||
if (i.second)
|
||||
txJson[jss::meta] = serializeHex(*i.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyFrom(txJson, i.first->getJson(0));
|
||||
if (i.second)
|
||||
txJson[jss::metaData] = i.second->getJson(0);
|
||||
}
|
||||
|
||||
if ((fill.options & LedgerFill::ownerFunds) &&
|
||||
i.first->getTxnType() == ttOFFER_CREATE)
|
||||
{
|
||||
auto const account = i.first->getAccountID(sfAccount);
|
||||
auto const amount = i.first->getFieldAmount(sfTakerGets);
|
||||
|
||||
// If the offer create is not self funded then add the
|
||||
// owner balance
|
||||
if (account != amount.getIssuer())
|
||||
txJson[jss::metaData] = stMeta->getJson(0);
|
||||
if (txnType == ttPAYMENT || txnType == ttCHECK_CASH)
|
||||
{
|
||||
auto const ownerFunds = accountFunds(fill.ledger,
|
||||
account, amount, fhIGNORE_FREEZE,
|
||||
beast::Journal {beast::Journal::getNullSink()});
|
||||
txJson[jss::owner_funds] = ownerFunds.getText ();
|
||||
// Insert delivered amount
|
||||
auto txMeta = std::make_shared<TxMeta>(
|
||||
txn->getTransactionID(), fill.ledger.seq(), *stMeta);
|
||||
RPC::insertDeliveredAmount(
|
||||
txJson[jss::metaData], fill.ledger, txn, *txMeta);
|
||||
}
|
||||
}
|
||||
return txJson;
|
||||
}
|
||||
|
||||
if ((fill.options & LedgerFill::ownerFunds) &&
|
||||
txn->getTxnType() == ttOFFER_CREATE)
|
||||
{
|
||||
auto const account = txn->getAccountID(sfAccount);
|
||||
auto const amount = txn->getFieldAmount(sfTakerGets);
|
||||
|
||||
// If the offer create is not self funded then add the
|
||||
// owner balance
|
||||
if (account != amount.getIssuer())
|
||||
{
|
||||
auto const ownerFunds = accountFunds(
|
||||
fill.ledger,
|
||||
account,
|
||||
amount,
|
||||
fhIGNORE_FREEZE,
|
||||
beast::Journal{beast::Journal::getNullSink()});
|
||||
txJson[jss::owner_funds] = ownerFunds.getText();
|
||||
}
|
||||
}
|
||||
|
||||
return txJson;
|
||||
}
|
||||
|
||||
template <class Object>
|
||||
@@ -153,7 +169,7 @@ void fillJsonTx (Object& json, LedgerFill const& fill)
|
||||
{
|
||||
for (auto& i: fill.ledger.txs)
|
||||
{
|
||||
txns.append(fillJsonTx(fill, bBinary, bExpanded, i));
|
||||
txns.append(fillJsonTx(fill, bBinary, bExpanded, i.first, i.second));
|
||||
}
|
||||
}
|
||||
catch (std::exception const&)
|
||||
@@ -219,8 +235,7 @@ void fillJsonQueue(Object& json, LedgerFill const& fill)
|
||||
if (tx.lastResult)
|
||||
txJson["last_result"] = transToken(*tx.lastResult);
|
||||
|
||||
txJson[jss::tx] = fillJsonTx(fill, bBinary, bExpanded,
|
||||
std::make_pair(tx.txn, nullptr));
|
||||
txJson[jss::tx] = fillJsonTx(fill, bBinary, bExpanded, tx.txn, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2063,7 +2063,7 @@ void ApplicationImp::addTxnSeqField ()
|
||||
}
|
||||
else
|
||||
{
|
||||
TxMeta m (transID, 0, txnMeta, journal ("TxMeta"));
|
||||
TxMeta m (transID, 0, txnMeta);
|
||||
txIDs.push_back (std::make_pair (transID, m.getIndex ()));
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <ripple/overlay/predicates.h>
|
||||
#include <ripple/protocol/BuildInfo.h>
|
||||
#include <ripple/resource/ResourceManager.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/beast/rfc2616.h>
|
||||
#include <ripple/beast/core/LexicalCast.h>
|
||||
#include <ripple/beast/utility/rngfill.h>
|
||||
@@ -1963,9 +1964,10 @@ NetworkOPs::AccountTxs NetworkOPsImp::getAccountTxs (
|
||||
}
|
||||
|
||||
if (txn)
|
||||
ret.emplace_back (txn, std::make_shared<TxMeta> (
|
||||
txn->getID (), txn->getLedger (), txnMeta,
|
||||
app_.journal("TxMeta")));
|
||||
ret.emplace_back(
|
||||
txn,
|
||||
std::make_shared<TxMeta>(
|
||||
txn->getID(), txn->getLedger(), txnMeta));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2556,9 +2558,16 @@ void NetworkOPsImp::pubValidatedTransaction (
|
||||
std::shared_ptr<ReadView const> const& alAccepted,
|
||||
const AcceptedLedgerTx& alTx)
|
||||
{
|
||||
std::shared_ptr<STTx const> stTxn = alTx.getTxn();
|
||||
Json::Value jvObj = transJson (
|
||||
*alTx.getTxn (), alTx.getResult (), true, alAccepted);
|
||||
jvObj[jss::meta] = alTx.getMeta ()->getJson (0);
|
||||
*stTxn, alTx.getResult (), true, alAccepted);
|
||||
|
||||
if (auto const txMeta = alTx.getMeta())
|
||||
{
|
||||
jvObj[jss::meta] = txMeta->getJson(0);
|
||||
RPC::insertDeliveredAmount(
|
||||
jvObj[jss::meta], *alAccepted, stTxn, *txMeta);
|
||||
}
|
||||
|
||||
{
|
||||
ScopedLockType sl (mSubLock);
|
||||
@@ -2666,11 +2675,19 @@ void NetworkOPsImp::pubAccountTransaction (
|
||||
|
||||
if (!notify.empty ())
|
||||
{
|
||||
std::shared_ptr<STTx const> stTxn = alTx.getTxn();
|
||||
Json::Value jvObj = transJson (
|
||||
*alTx.getTxn (), alTx.getResult (), bAccepted, lpCurrent);
|
||||
*stTxn, alTx.getResult (), bAccepted, lpCurrent);
|
||||
|
||||
if (alTx.isApplied ())
|
||||
jvObj[jss::meta] = alTx.getMeta ()->getJson (0);
|
||||
{
|
||||
if (auto const txMeta = alTx.getMeta())
|
||||
{
|
||||
jvObj[jss::meta] = txMeta->getJson(0);
|
||||
RPC::insertDeliveredAmount(
|
||||
jvObj[jss::meta], *lpCurrent, stTxn, *txMeta);
|
||||
}
|
||||
}
|
||||
|
||||
for (InfoSub::ref isrListener : notify)
|
||||
isrListener->send (jvObj, true);
|
||||
|
||||
@@ -48,7 +48,7 @@ convertBlobsToTxResult (
|
||||
tr->setLedger (ledger_index);
|
||||
|
||||
auto metaset = std::make_shared<TxMeta> (
|
||||
tr->getID (), tr->getLedger (), rawMeta, app.journal ("TxMeta"));
|
||||
tr->getID (), tr->getLedger (), rawMeta);
|
||||
|
||||
to.emplace_back(std::move(tr), metaset);
|
||||
};
|
||||
|
||||
@@ -42,29 +42,26 @@ private:
|
||||
explicit CtorHelper() = default;
|
||||
};
|
||||
template<class T>
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, T const& data, beast::Journal j,
|
||||
CtorHelper);
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, T const& data, CtorHelper);
|
||||
public:
|
||||
TxMeta (beast::Journal j)
|
||||
TxMeta ()
|
||||
: mLedger (0)
|
||||
, mIndex (static_cast<std::uint32_t> (-1))
|
||||
, mResult (255)
|
||||
, j_ (j)
|
||||
{
|
||||
}
|
||||
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, std::uint32_t index, beast::Journal j)
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, std::uint32_t index)
|
||||
: mTransactionID (txID)
|
||||
, mLedger (ledger)
|
||||
, mIndex (static_cast<std::uint32_t> (-1))
|
||||
, mResult (255)
|
||||
, j_(j)
|
||||
{
|
||||
}
|
||||
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, Blob const&, beast::Journal j);
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, std::string const&, beast::Journal j);
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, STObject const&, beast::Journal j);
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, Blob const&);
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, std::string const&);
|
||||
TxMeta (uint256 const& txID, std::uint32_t ledger, STObject const&);
|
||||
|
||||
void init (uint256 const& transactionID, std::uint32_t ledger);
|
||||
void clear ()
|
||||
@@ -103,7 +100,7 @@ public:
|
||||
|
||||
/** Return a list of accounts affected by this transaction */
|
||||
boost::container::flat_set<AccountID>
|
||||
getAffectedAccounts() const;
|
||||
getAffectedAccounts(beast::Journal j) const;
|
||||
|
||||
Json::Value getJson (int p) const
|
||||
{
|
||||
@@ -144,8 +141,6 @@ private:
|
||||
boost::optional <STAmount> mDelivered;
|
||||
|
||||
STArray mNodes;
|
||||
|
||||
beast::Journal j_;
|
||||
};
|
||||
|
||||
} // ripple
|
||||
|
||||
@@ -116,7 +116,7 @@ ApplyStateTable::apply (OpenView& to,
|
||||
std::shared_ptr<Serializer> sMeta;
|
||||
if (!to.open())
|
||||
{
|
||||
TxMeta meta(j);
|
||||
TxMeta meta;
|
||||
// VFALCO Shouldn't TxMeta ctor do this?
|
||||
meta.init (tx.getTransactionID(), to.seq());
|
||||
if (deliver)
|
||||
|
||||
@@ -30,11 +30,10 @@ namespace ripple {
|
||||
|
||||
template<class T>
|
||||
TxMeta::TxMeta (uint256 const& txid,
|
||||
std::uint32_t ledger, T const& data, beast::Journal j, CtorHelper)
|
||||
std::uint32_t ledger, T const& data, CtorHelper)
|
||||
: mTransactionID (txid)
|
||||
, mLedger (ledger)
|
||||
, mNodes (sfAffectedNodes, 32)
|
||||
, j_ (j)
|
||||
{
|
||||
SerialIter sit (makeSlice(data));
|
||||
|
||||
@@ -47,12 +46,10 @@ TxMeta::TxMeta (uint256 const& txid,
|
||||
setDeliveredAmount (obj.getFieldAmount (sfDeliveredAmount));
|
||||
}
|
||||
|
||||
TxMeta::TxMeta (uint256 const& txid, std::uint32_t ledger, STObject const& obj,
|
||||
beast::Journal j)
|
||||
TxMeta::TxMeta (uint256 const& txid, std::uint32_t ledger, STObject const& obj)
|
||||
: mTransactionID (txid)
|
||||
, mLedger (ledger)
|
||||
, mNodes (obj.getFieldArray (sfAffectedNodes))
|
||||
, j_ (j)
|
||||
{
|
||||
mResult = obj.getFieldU8 (sfTransactionResult);
|
||||
mIndex = obj.getFieldU32 (sfTransactionIndex);
|
||||
@@ -69,17 +66,15 @@ TxMeta::TxMeta (uint256 const& txid, std::uint32_t ledger, STObject const& obj,
|
||||
|
||||
TxMeta::TxMeta (uint256 const& txid,
|
||||
std::uint32_t ledger,
|
||||
Blob const& vec,
|
||||
beast::Journal j)
|
||||
: TxMeta (txid, ledger, vec, j, CtorHelper ())
|
||||
Blob const& vec)
|
||||
: TxMeta (txid, ledger, vec, CtorHelper ())
|
||||
{
|
||||
}
|
||||
|
||||
TxMeta::TxMeta (uint256 const& txid,
|
||||
std::uint32_t ledger,
|
||||
std::string const& data,
|
||||
beast::Journal j)
|
||||
: TxMeta (txid, ledger, data, j, CtorHelper ())
|
||||
std::string const& data)
|
||||
: TxMeta (txid, ledger, data, CtorHelper ())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -117,7 +112,7 @@ void TxMeta::setAffectedNode (uint256 const& node, SField const& type,
|
||||
}
|
||||
|
||||
boost::container::flat_set<AccountID>
|
||||
TxMeta::getAffectedAccounts() const
|
||||
TxMeta::getAffectedAccounts(beast::Journal j) const
|
||||
{
|
||||
boost::container::flat_set<AccountID> list;
|
||||
list.reserve (10);
|
||||
@@ -156,7 +151,7 @@ TxMeta::getAffectedAccounts() const
|
||||
}
|
||||
else
|
||||
{
|
||||
JLOG (j_.fatal()) << "limit is not amount " << field.getJson (0);
|
||||
JLOG (j.fatal()) << "limit is not amount " << field.getJson (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ JSS ( dbKBLedger ); // out: getCounts
|
||||
JSS ( dbKBTotal ); // out: getCounts
|
||||
JSS ( dbKBTransaction ); // out: getCounts
|
||||
JSS ( debug_signing ); // in: TransactionSign
|
||||
JSS ( delivered_amount ); // out: addPaymentDeliveredAmount
|
||||
JSS ( delivered_amount ); // out: insertDeliveredAmount
|
||||
JSS ( deposit_authorized ); // out: deposit_authorized
|
||||
JSS ( deposit_preauth ); // in: AccountObjects, LedgerData
|
||||
JSS ( deprecated ); // out
|
||||
|
||||
68
src/ripple/rpc/DeliveredAmount.h
Normal file
68
src/ripple/rpc/DeliveredAmount.h
Normal file
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2019 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef RIPPLE_RPC_DELIVEREDAMOUNT_H_INCLUDED
|
||||
#define RIPPLE_RPC_DELIVEREDAMOUNT_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Json {
|
||||
class Value;
|
||||
}
|
||||
|
||||
namespace ripple {
|
||||
|
||||
class ReadView;
|
||||
class Transaction;
|
||||
class TxMeta;
|
||||
class STTx;
|
||||
|
||||
namespace RPC {
|
||||
|
||||
struct Context;
|
||||
|
||||
/**
|
||||
Add a `delivered_amount` field to the `meta` input/output parameter.
|
||||
The field is only added to successful payment and check cash transactions.
|
||||
If a delivered amount field is available in the TxMeta parameter, that value
|
||||
is used. Otherwise, the transaction's `Amount` field is used. If neither is available,
|
||||
then the delivered amount is set to "unavailable".
|
||||
|
||||
@{
|
||||
*/
|
||||
void
|
||||
insertDeliveredAmount(
|
||||
Json::Value& meta,
|
||||
ReadView const&,
|
||||
std::shared_ptr<STTx const> serializedTx,
|
||||
TxMeta const&);
|
||||
|
||||
void
|
||||
insertDeliveredAmount(
|
||||
Json::Value& meta,
|
||||
Context&,
|
||||
std::shared_ptr<Transaction>,
|
||||
TxMeta const&);
|
||||
|
||||
/** @} */
|
||||
|
||||
} // RPC
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <ripple/protocol/UintTypes.h>
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
|
||||
@@ -166,7 +167,7 @@ Json::Value doAccountTx (RPC::Context& context)
|
||||
if (it.second)
|
||||
{
|
||||
auto meta = it.second->getJson (1);
|
||||
addPaymentDeliveredAmount (meta, context, it.first, it.second);
|
||||
insertDeliveredAmount (meta, context, it.first, *it.second);
|
||||
jvObj[jss::meta] = std::move(meta);
|
||||
|
||||
std::uint32_t uLedgerIndex = it.second->getLgrSeq ();
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/rpc/Role.h>
|
||||
|
||||
@@ -183,7 +184,7 @@ Json::Value doAccountTxOld (RPC::Context& context)
|
||||
std::uint32_t uLedgerIndex = it->second->getLgrSeq ();
|
||||
|
||||
auto meta = it->second->getJson(0);
|
||||
addPaymentDeliveredAmount(meta, context, it->first, it->second);
|
||||
insertDeliveredAmount(meta, context, it->first, *it->second);
|
||||
jvObj[jss::meta] = std::move(meta);
|
||||
|
||||
jvObj[jss::validated]
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -126,12 +127,12 @@ Json::Value doTx (RPC::Context& context)
|
||||
auto rawMeta = lgr->txRead (txn->getID()).second;
|
||||
if (rawMeta)
|
||||
{
|
||||
auto txMeta = std::make_shared<TxMeta> (txn->getID (),
|
||||
lgr->seq (), *rawMeta, context.app.journal ("TxMeta"));
|
||||
auto txMeta = std::make_shared<TxMeta>(
|
||||
txn->getID(), lgr->seq(), *rawMeta);
|
||||
okay = true;
|
||||
auto meta = txMeta->getJson (0);
|
||||
addPaymentDeliveredAmount (meta, context, txn, txMeta);
|
||||
ret[jss::meta] = meta;
|
||||
insertDeliveredAmount (meta, context, txn, *txMeta);
|
||||
ret[jss::meta] = std::move(meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
173
src/ripple/rpc/impl/DeliveredAmount.cpp
Normal file
173
src/ripple/rpc/impl/DeliveredAmount.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2019 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/ledger/OpenLedger.h>
|
||||
#include <ripple/app/misc/Transaction.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
#include <ripple/net/RPCErr.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
namespace ripple {
|
||||
namespace RPC {
|
||||
|
||||
/*
|
||||
GetLedgerIndex and GetCloseTime are lambdas that allow the close time and
|
||||
ledger index to be lazily calculated. Without these lambdas, these values
|
||||
would be calculated even when not needed, and in some circumstances they are
|
||||
not trivial to compute.
|
||||
|
||||
GetFix1623Enabled is a callable that returns a bool
|
||||
GetLedgerIndex is a callable that returns a LedgerIndex
|
||||
GetCloseTime is a callable that returns a
|
||||
boost::optional<NetClock::time_point>
|
||||
*/
|
||||
template<class GetFix1623Enabled, class GetLedgerIndex, class GetCloseTime>
|
||||
void
|
||||
insertDeliveredAmount(
|
||||
Json::Value& meta,
|
||||
GetFix1623Enabled const& getFix1623Enabled,
|
||||
GetLedgerIndex const& getLedgerIndex,
|
||||
GetCloseTime const& getCloseTime,
|
||||
std::shared_ptr<STTx const> serializedTx,
|
||||
TxMeta const& transactionMeta)
|
||||
{
|
||||
{
|
||||
TxType const tt{serializedTx->getTxnType()};
|
||||
if (tt != ttPAYMENT &&
|
||||
tt != ttCHECK_CASH)
|
||||
return;
|
||||
|
||||
if (tt == ttCHECK_CASH &&
|
||||
!getFix1623Enabled())
|
||||
return;
|
||||
}
|
||||
|
||||
// if the transaction failed nothing could have been delivered.
|
||||
if (transactionMeta.getResultTER() != tesSUCCESS)
|
||||
return;
|
||||
|
||||
if (transactionMeta.hasDeliveredAmount())
|
||||
{
|
||||
meta[jss::delivered_amount] =
|
||||
transactionMeta.getDeliveredAmount().getJson(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (serializedTx->isFieldPresent(sfAmount))
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// Ledger 4594095 is the first ledger in which the DeliveredAmount field
|
||||
// was present when a partial payment was made and its absence indicates
|
||||
// that the amount delivered is listed in the Amount field.
|
||||
//
|
||||
// If the ledger closed long after the DeliveredAmount code was deployed
|
||||
// then its absence indicates that the amount delivered is listed in the
|
||||
// Amount field. DeliveredAmount went live January 24, 2014.
|
||||
// 446000000 is in Feb 2014, well after DeliveredAmount went live
|
||||
if (getLedgerIndex() >= 4594095 ||
|
||||
getCloseTime() > NetClock::time_point{446000000s})
|
||||
{
|
||||
meta[jss::delivered_amount] =
|
||||
serializedTx->getFieldAmount(sfAmount).getJson(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// report "unavailable" which cannot be parsed into a sensible amount.
|
||||
meta[jss::delivered_amount] = Json::Value("unavailable");
|
||||
}
|
||||
|
||||
void
|
||||
insertDeliveredAmount(
|
||||
Json::Value& meta,
|
||||
ReadView const& ledger,
|
||||
std::shared_ptr<STTx const> serializedTx,
|
||||
TxMeta const& transactionMeta)
|
||||
{
|
||||
if (!serializedTx)
|
||||
return;
|
||||
|
||||
auto const info = ledger.info();
|
||||
auto const getFix1623Enabled = [&ledger] {
|
||||
return ledger.rules().enabled(fix1623);
|
||||
};
|
||||
auto const getLedgerIndex = [&info] {
|
||||
return info.seq;
|
||||
};
|
||||
auto const getCloseTime = [&info] {
|
||||
return info.closeTime;
|
||||
};
|
||||
|
||||
insertDeliveredAmount(
|
||||
meta,
|
||||
getFix1623Enabled,
|
||||
getLedgerIndex,
|
||||
getCloseTime,
|
||||
std::move(serializedTx),
|
||||
transactionMeta);
|
||||
}
|
||||
|
||||
void
|
||||
insertDeliveredAmount(
|
||||
Json::Value& meta,
|
||||
RPC::Context& context,
|
||||
std::shared_ptr<Transaction> transaction,
|
||||
TxMeta const& transactionMeta)
|
||||
{
|
||||
if (!transaction)
|
||||
return;
|
||||
|
||||
auto const serializedTx = transaction->getSTransaction ();
|
||||
if (! serializedTx)
|
||||
return;
|
||||
|
||||
|
||||
// These lambdas are used to compute the values lazily
|
||||
auto const getFix1623Enabled = [&context]() -> bool {
|
||||
auto const view = context.app.openLedger().current();
|
||||
if (!view)
|
||||
return false;
|
||||
return view->rules().enabled(fix1623);
|
||||
};
|
||||
auto const getLedgerIndex = [&transaction]() -> LedgerIndex {
|
||||
return transaction->getLedger();
|
||||
};
|
||||
auto const getCloseTime =
|
||||
[&context, &transaction]() -> boost::optional<NetClock::time_point> {
|
||||
return context.ledgerMaster.getCloseTimeBySeq(transaction->getLedger());
|
||||
};
|
||||
|
||||
insertDeliveredAmount(
|
||||
meta,
|
||||
getFix1623Enabled,
|
||||
getLedgerIndex,
|
||||
getCloseTime,
|
||||
std::move(serializedTx),
|
||||
transactionMeta);
|
||||
}
|
||||
} // RPC
|
||||
} // ripple
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/DeliveredAmount.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
@@ -393,86 +394,6 @@ parseAccountIds(Json::Value const& jvArray)
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
addPaymentDeliveredAmount(Json::Value& meta, RPC::Context& context,
|
||||
std::shared_ptr<Transaction> transaction, TxMeta::pointer transactionMeta)
|
||||
{
|
||||
// We only want to add a "delivered_amount" field if the transaction
|
||||
// succeeded - otherwise nothing could have been delivered.
|
||||
if (! transaction)
|
||||
return;
|
||||
|
||||
auto const serializedTx = transaction->getSTransaction ();
|
||||
if (! serializedTx)
|
||||
return;
|
||||
|
||||
{
|
||||
// Only include this field for Payment and CheckCash transactions.
|
||||
TxType const tt {serializedTx->getTxnType()};
|
||||
if ((tt != ttPAYMENT) && (tt != ttCHECK_CASH))
|
||||
return;
|
||||
|
||||
// Only include this field for CheckCash transactions if the fix
|
||||
// is enabled.
|
||||
if (tt == ttCHECK_CASH)
|
||||
{
|
||||
auto const view = context.app.openLedger().current();
|
||||
if (!view || !view->rules().enabled (fix1623))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (transactionMeta)
|
||||
{
|
||||
if (transactionMeta->getResultTER() != tesSUCCESS)
|
||||
return;
|
||||
|
||||
// If the transaction explicitly specifies a DeliveredAmount in the
|
||||
// metadata then we use it.
|
||||
if (transactionMeta->hasDeliveredAmount ())
|
||||
{
|
||||
meta[jss::delivered_amount] =
|
||||
transactionMeta->getDeliveredAmount ().getJson (1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (transaction->getResult() != tesSUCCESS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (serializedTx->isFieldPresent (sfAmount))
|
||||
{
|
||||
// Ledger 4594095 is the first ledger in which the DeliveredAmount field
|
||||
// was present when a partial payment was made and its absence indicates
|
||||
// that the amount delivered is listed in the Amount field.
|
||||
if (transaction->getLedger () >= 4594095)
|
||||
{
|
||||
meta[jss::delivered_amount] =
|
||||
serializedTx->getFieldAmount (sfAmount).getJson (1);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the ledger closed long after the DeliveredAmount code was deployed
|
||||
// then its absence indicates that the amount delivered is listed in the
|
||||
// Amount field. DeliveredAmount went live January 24, 2014.
|
||||
using namespace std::chrono_literals;
|
||||
auto const ct =
|
||||
context.ledgerMaster.getCloseTimeBySeq (transaction->getLedger ());
|
||||
if (ct && (*ct > NetClock::time_point{446000000s}))
|
||||
{
|
||||
// 446000000 is in Feb 2014, well after DeliveredAmount went live
|
||||
meta[jss::delivered_amount] =
|
||||
serializedTx->getFieldAmount (sfAmount).getJson (1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise we report "unavailable" which cannot be parsed into a
|
||||
// sensible amount.
|
||||
meta[jss::delivered_amount] = Json::Value ("unavailable");
|
||||
}
|
||||
|
||||
void
|
||||
injectSLE(Json::Value& jv, SLE const& sle)
|
||||
{
|
||||
|
||||
@@ -86,10 +86,6 @@ lookupLedger (std::shared_ptr<ReadView const>&, Context&, Json::Value& result);
|
||||
hash_set <AccountID>
|
||||
parseAccountIds(Json::Value const& jvArray);
|
||||
|
||||
void
|
||||
addPaymentDeliveredAmount(Json::Value&, Context&,
|
||||
std::shared_ptr<Transaction>, TxMeta::pointer);
|
||||
|
||||
/** Inject JSON describing ledger entry
|
||||
|
||||
Effects:
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <ripple/rpc/handlers/ValidatorListSites.cpp>
|
||||
#include <ripple/rpc/handlers/WalletPropose.cpp>
|
||||
|
||||
#include <ripple/rpc/impl/DeliveredAmount.cpp>
|
||||
#include <ripple/rpc/impl/Handler.cpp>
|
||||
#include <ripple/rpc/impl/LegacyPathFind.cpp>
|
||||
#include <ripple/rpc/impl/Role.cpp>
|
||||
|
||||
341
src/test/rpc/DeliveredAmount_test.cpp
Normal file
341
src/test/rpc/DeliveredAmount_test.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2019 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/beast/unit_test.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <test/jtx.h>
|
||||
#include <test/jtx/WSClient.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
|
||||
// Helper class to track the expected number `delivered_amount` results.
|
||||
class CheckDeliveredAmount
|
||||
{
|
||||
// If the test occurs before or after the switch time
|
||||
bool afterSwitchTime_;
|
||||
// number of payments expected 'delivered_amount' available
|
||||
int numExpectedAvailable_ = 0;
|
||||
// Number of payments with field with `delivered_amount` set to the
|
||||
// string "unavailable"
|
||||
int numExpectedSetUnavailable_ = 0;
|
||||
// Number of payments with no `delivered_amount` field
|
||||
int numExpectedNotSet_ = 0;
|
||||
|
||||
// Increment one of the expected numExpected{Available_, Unavailable_, NotSet_} values.
|
||||
// Which value to increment depends on:
|
||||
// 1) If the ledger is before or after the switch time
|
||||
// 2) If the tx is a partial payment
|
||||
// 3) If the payment is successful or not
|
||||
void
|
||||
adjCounters(bool success, bool partial)
|
||||
{
|
||||
if (!success)
|
||||
{
|
||||
++numExpectedNotSet_;
|
||||
return;
|
||||
}
|
||||
if (!afterSwitchTime_)
|
||||
{
|
||||
if (partial)
|
||||
++numExpectedAvailable_;
|
||||
else
|
||||
++numExpectedSetUnavailable_;
|
||||
return;
|
||||
}
|
||||
// normal case: after switch time & successful transaction
|
||||
++numExpectedAvailable_;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CheckDeliveredAmount(bool afterSwitchTime)
|
||||
: afterSwitchTime_(afterSwitchTime)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
adjCountersSuccess()
|
||||
{
|
||||
adjCounters(true, false);
|
||||
}
|
||||
|
||||
void
|
||||
adjCountersFail()
|
||||
{
|
||||
adjCounters(false, false);
|
||||
}
|
||||
void
|
||||
adjCountersPartialPayment()
|
||||
{
|
||||
adjCounters(true, true);
|
||||
}
|
||||
|
||||
// After all the txns are checked, all the `numExpected` variables should be
|
||||
// zero. The `checkTxn` function decrements these variables.
|
||||
bool
|
||||
checkExpectedCounters() const
|
||||
{
|
||||
return !numExpectedAvailable_ && !numExpectedNotSet_ &&
|
||||
!numExpectedSetUnavailable_;
|
||||
}
|
||||
|
||||
// Check if the transaction has `delivered_amount` in the metaData as
|
||||
// expected from our rules. Decrements the appropriate `numExpected`
|
||||
// variable. After all the txns are checked, all the `numExpected` variables
|
||||
// should be zero.
|
||||
bool
|
||||
checkTxn(Json::Value const& t, Json::Value const& metaData)
|
||||
{
|
||||
if (t[jss::TransactionType].asString() != "Payment")
|
||||
return true;
|
||||
|
||||
bool isSet = metaData.isMember(jss::delivered_amount);
|
||||
bool isSetUnavailable = false;
|
||||
bool isSetAvailable = false;
|
||||
if (isSet)
|
||||
{
|
||||
if (metaData[jss::delivered_amount] != "unavailable")
|
||||
isSetAvailable = true;
|
||||
else
|
||||
isSetUnavailable = true;
|
||||
}
|
||||
if (isSetAvailable)
|
||||
--numExpectedAvailable_;
|
||||
else if (isSetUnavailable)
|
||||
--numExpectedSetUnavailable_;
|
||||
else if (!isSet)
|
||||
--numExpectedNotSet_;
|
||||
|
||||
if (isSet)
|
||||
{
|
||||
if (metaData.isMember(sfDeliveredAmount.jsonName))
|
||||
{
|
||||
if (metaData[jss::delivered_amount] !=
|
||||
metaData[sfDeliveredAmount.jsonName])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (afterSwitchTime_)
|
||||
{
|
||||
if (metaData[jss::delivered_amount] != t[jss::Amount])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (metaData[jss::delivered_amount] != "unavailable")
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (metaData[sfTransactionResult.jsonName] != "tesSUCCESS")
|
||||
{
|
||||
if (isSet)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (afterSwitchTime_)
|
||||
{
|
||||
if (!isSetAvailable)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (metaData.isMember(sfDeliveredAmount.jsonName))
|
||||
{
|
||||
if (!isSetAvailable)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isSetUnavailable)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class DeliveredAmount_test : public beast::unit_test::suite
|
||||
{
|
||||
void
|
||||
testAccountDeliveredAmountSubscribe()
|
||||
{
|
||||
testcase("Ledger Request Subscribe DeliveredAmount");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
for (bool const afterSwitchTime : {true, false})
|
||||
{
|
||||
Env env{*this};
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
if (afterSwitchTime)
|
||||
env.close(NetClock::time_point{446000000s});
|
||||
else
|
||||
env.close();
|
||||
|
||||
CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
|
||||
{
|
||||
// add payments, but do no close until subscribed
|
||||
|
||||
// normal payments
|
||||
env(pay(gw, alice, USD(50)));
|
||||
checkDeliveredAmount.adjCountersSuccess();
|
||||
env(pay(gw, alice, XRP(50)));
|
||||
checkDeliveredAmount.adjCountersSuccess();
|
||||
|
||||
// partial payment
|
||||
env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
|
||||
checkDeliveredAmount.adjCountersPartialPayment();
|
||||
env.require(balance(bob, USD(1000)));
|
||||
|
||||
// failed payment
|
||||
env(pay(bob, carol, USD(9999999)), ter(tecPATH_PARTIAL));
|
||||
checkDeliveredAmount.adjCountersFail();
|
||||
env.require(balance(carol, USD(0)));
|
||||
}
|
||||
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
Json::Value stream;
|
||||
// RPC subscribe to ledger stream
|
||||
stream[jss::streams] = Json::arrayValue;
|
||||
stream[jss::streams].append("ledger");
|
||||
stream[jss::accounts] = Json::arrayValue;
|
||||
stream[jss::accounts].append(toBase58(alice.id()));
|
||||
stream[jss::accounts].append(toBase58(bob.id()));
|
||||
stream[jss::accounts].append(toBase58(carol.id()));
|
||||
auto jv = wsc->invoke("subscribe", stream);
|
||||
if (wsc->version() == 2)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
jv.isMember(jss::jsonrpc) && jv[jss::jsonrpc] == "2.0");
|
||||
BEAST_EXPECT(
|
||||
jv.isMember(jss::ripplerpc) &&
|
||||
jv[jss::ripplerpc] == "2.0");
|
||||
BEAST_EXPECT(jv.isMember(jss::id) && jv[jss::id] == 5);
|
||||
}
|
||||
BEAST_EXPECT(jv[jss::result][jss::ledger_index] == 3);
|
||||
}
|
||||
{
|
||||
env.close();
|
||||
// Check stream update
|
||||
while (true)
|
||||
{
|
||||
auto const r = wsc->findMsg(1s, [&](auto const& jv) {
|
||||
return jv[jss::ledger_index] == 4;
|
||||
});
|
||||
if (!r)
|
||||
break;
|
||||
|
||||
if (!r->isMember(jss::transaction))
|
||||
continue;
|
||||
|
||||
BEAST_EXPECT(checkDeliveredAmount.checkTxn(
|
||||
(*r)[jss::transaction], (*r)[jss::meta]));
|
||||
}
|
||||
}
|
||||
BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
|
||||
}
|
||||
}
|
||||
void
|
||||
testTxDeliveredAmountRPC()
|
||||
{
|
||||
testcase("Ledger Request RPC DeliveredAmount");
|
||||
|
||||
using namespace test::jtx;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
Account const alice("alice");
|
||||
Account const bob("bob");
|
||||
Account const carol("carol");
|
||||
auto const gw = Account("gateway");
|
||||
auto const USD = gw["USD"];
|
||||
|
||||
for (bool const afterSwitchTime : {true, false})
|
||||
{
|
||||
Env env{*this};
|
||||
env.fund(XRP(10000), alice, bob, carol, gw);
|
||||
env.trust(USD(1000), alice, bob, carol);
|
||||
if (afterSwitchTime)
|
||||
env.close(NetClock::time_point{446000000s});
|
||||
else
|
||||
env.close();
|
||||
|
||||
CheckDeliveredAmount checkDeliveredAmount{afterSwitchTime};
|
||||
// normal payments
|
||||
env(pay(gw, alice, USD(50)));
|
||||
checkDeliveredAmount.adjCountersSuccess();
|
||||
env(pay(gw, alice, XRP(50)));
|
||||
checkDeliveredAmount.adjCountersSuccess();
|
||||
|
||||
// partial payment
|
||||
env(pay(gw, bob, USD(9999999)), txflags(tfPartialPayment));
|
||||
checkDeliveredAmount.adjCountersPartialPayment();
|
||||
env.require(balance(bob, USD(1000)));
|
||||
|
||||
// failed payment
|
||||
env(pay(gw, carol, USD(9999999)), ter(tecPATH_PARTIAL));
|
||||
checkDeliveredAmount.adjCountersFail();
|
||||
env.require(balance(carol, USD(0)));
|
||||
|
||||
env.close();
|
||||
std::string index;
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = 4u;
|
||||
jvParams[jss::transactions] = true;
|
||||
jvParams[jss::expand] = true;
|
||||
auto const jtxn = env.rpc(
|
||||
"json",
|
||||
"ledger",
|
||||
to_string(
|
||||
jvParams))[jss::result][jss::ledger][jss::transactions];
|
||||
for (auto const& t : jtxn)
|
||||
BEAST_EXPECT(
|
||||
checkDeliveredAmount.checkTxn(t, t[jss::metaData]));
|
||||
BEAST_EXPECT(checkDeliveredAmount.checkExpectedCounters());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testTxDeliveredAmountRPC();
|
||||
testAccountDeliveredAmountSubscribe();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(DeliveredAmount, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
@@ -651,6 +651,7 @@ public:
|
||||
|
||||
}
|
||||
|
||||
|
||||
void run() override
|
||||
{
|
||||
testServer();
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <test/rpc/AmendmentBlocked_test.cpp>
|
||||
#include <test/rpc/Book_test.cpp>
|
||||
#include <test/rpc/DepositAuthorized_test.cpp>
|
||||
#include <test/rpc/DeliveredAmount_test.cpp>
|
||||
#include <test/rpc/Feature_test.cpp>
|
||||
#include <test/rpc/GatewayBalances_test.cpp>
|
||||
#include <test/rpc/GetCounts_test.cpp>
|
||||
|
||||
Reference in New Issue
Block a user