mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 03:35:55 +00:00
delivered amount
This commit is contained in:
@@ -7,7 +7,6 @@ template <class T, class F>
|
||||
void
|
||||
processAsyncWriteResponse(T& requestParams, CassFuture* fut, F func)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " Processing async write response";
|
||||
CassandraBackend const& backend = *requestParams.backend;
|
||||
auto rc = cass_future_error_code(fut);
|
||||
if (rc != CASS_OK)
|
||||
|
||||
@@ -1,6 +1,56 @@
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <handlers/RPCHelpers.h>
|
||||
#include <handlers/Status.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
|
||||
std::optional<ripple::STAmount>
|
||||
getDeliveredAmount(
|
||||
std::shared_ptr<ripple::STTx const> const& txn,
|
||||
std::shared_ptr<ripple::STObject const> const& meta,
|
||||
uint32_t ledgerSequence)
|
||||
{
|
||||
if (meta->isFieldPresent(ripple::sfDeliveredAmount))
|
||||
return meta->getFieldAmount(ripple::sfDeliveredAmount);
|
||||
if (txn->isFieldPresent(ripple::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 (ledgerSequence >= 4594095)
|
||||
{
|
||||
return txn->getFieldAmount(ripple::sfAmount);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool
|
||||
canHaveDeliveredAmount(
|
||||
std::shared_ptr<ripple::STTx const> const& txn,
|
||||
std::shared_ptr<ripple::STObject const> const& meta)
|
||||
{
|
||||
ripple::TxType const tt{txn->getTxnType()};
|
||||
if (tt != ripple::ttPAYMENT && tt != ripple::ttCHECK_CASH &&
|
||||
tt != ripple::ttACCOUNT_DELETE)
|
||||
return false;
|
||||
|
||||
/*
|
||||
if (tt == ttCHECK_CASH && !getFix1623Enabled())
|
||||
return false;
|
||||
*/
|
||||
|
||||
if (ripple::TER::fromInt(meta->getFieldU8(ripple::sfTransactionResult)) !=
|
||||
ripple::tesSUCCESS)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<ripple::AccountID>
|
||||
accountFromStringStrict(std::string const& account)
|
||||
@@ -76,6 +126,23 @@ toJson(ripple::STBase const& obj)
|
||||
return value.as_object();
|
||||
}
|
||||
|
||||
std::pair<boost::json::object, boost::json::object>
|
||||
toExpandedJson(Backend::TransactionAndMetadata const& blobs)
|
||||
{
|
||||
auto [txn, meta] = deserializeTxPlusMeta(blobs);
|
||||
auto txnJson = toJson(*txn);
|
||||
auto metaJson = toJson(*meta);
|
||||
if (canHaveDeliveredAmount(txn, meta))
|
||||
{
|
||||
if (auto amt = getDeliveredAmount(txn, meta, blobs.ledgerSequence))
|
||||
metaJson["delivered_amount"] =
|
||||
toBoostJson(amt->getJson(ripple::JsonOptions::include_date));
|
||||
else
|
||||
metaJson["delivered_amount"] = "unavailable";
|
||||
}
|
||||
return {txnJson, metaJson};
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
toJson(ripple::TxMeta const& meta)
|
||||
{
|
||||
@@ -88,8 +155,7 @@ toJson(ripple::TxMeta const& meta)
|
||||
boost::json::value
|
||||
toBoostJson(Json::Value const& value)
|
||||
{
|
||||
boost::json::value boostValue =
|
||||
boost::json::parse(value.toStyledString());
|
||||
boost::json::value boostValue = boost::json::parse(value.toStyledString());
|
||||
|
||||
return boostValue;
|
||||
}
|
||||
@@ -138,14 +204,15 @@ ledgerInfoFromRequest(RPC::Context const& ctx)
|
||||
if (!hashValue.is_null())
|
||||
{
|
||||
if (!hashValue.is_string())
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS, "ledgerHashNotString"};
|
||||
|
||||
ripple::uint256 ledgerHash;
|
||||
if (!ledgerHash.parseHex(hashValue.as_string().c_str()))
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS, "ledgerHashMalformed"};
|
||||
|
||||
lgrInfo = ctx.backend->fetchLedgerByHash(ledgerHash);
|
||||
|
||||
}
|
||||
else if (!indexValue.is_null())
|
||||
{
|
||||
@@ -155,15 +222,14 @@ ledgerInfoFromRequest(RPC::Context const& ctx)
|
||||
else if (!indexValue.is_string() && indexValue.is_int64())
|
||||
ledgerSequence = indexValue.as_int64();
|
||||
else
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS, "ledgerIndexMalformed"};
|
||||
|
||||
lgrInfo =
|
||||
ctx.backend->fetchLedgerBySequence(ledgerSequence);
|
||||
lgrInfo = ctx.backend->fetchLedgerBySequence(ledgerSequence);
|
||||
}
|
||||
else
|
||||
{
|
||||
lgrInfo =
|
||||
ctx.backend->fetchLedgerBySequence(ctx.range.maxSequence);
|
||||
lgrInfo = ctx.backend->fetchLedgerBySequence(ctx.range.maxSequence);
|
||||
}
|
||||
|
||||
if (!lgrInfo)
|
||||
@@ -297,13 +363,15 @@ keypairFromRequst(boost::json::object const& request)
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS, "missing field secret"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS, "missing field secret"};
|
||||
|
||||
if (count > 1)
|
||||
{
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS,
|
||||
"Exactly one of the following must be specified: "
|
||||
" passphrase, secret, seed, or seed_hex"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS,
|
||||
"Exactly one of the following must be specified: "
|
||||
" passphrase, secret, seed, or seed_hex"};
|
||||
}
|
||||
|
||||
boost::optional<ripple::KeyType> keyType;
|
||||
@@ -312,18 +380,20 @@ keypairFromRequst(boost::json::object const& request)
|
||||
if (has_key_type)
|
||||
{
|
||||
if (!request.at("key_type").is_string())
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS, "keyTypeNotString"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS, "keyTypeNotString"};
|
||||
|
||||
std::string key_type = request.at("key_type").as_string().c_str();
|
||||
keyType = ripple::keyTypeFromString(key_type);
|
||||
|
||||
if (!keyType)
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS, "invalidFieldKeyType"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS, "invalidFieldKeyType"};
|
||||
|
||||
if (secretType == "secret")
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS,
|
||||
"The secret field is not allowed if key_type is used."};
|
||||
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS,
|
||||
"The secret field is not allowed if key_type is used."};
|
||||
}
|
||||
|
||||
// ripple-lib encodes seed used to generate an Ed25519 wallet in a
|
||||
@@ -337,9 +407,11 @@ keypairFromRequst(boost::json::object const& request)
|
||||
{
|
||||
// If the user passed in an Ed25519 seed but *explicitly*
|
||||
// requested another key type, return an error.
|
||||
if (keyType.value_or(ripple::KeyType::ed25519) != ripple::KeyType::ed25519)
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS,
|
||||
"Specified seed is for an Ed25519 wallet."};
|
||||
if (keyType.value_or(ripple::KeyType::ed25519) !=
|
||||
ripple::KeyType::ed25519)
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS,
|
||||
"Specified seed is for an Ed25519 wallet."};
|
||||
|
||||
keyType = ripple::KeyType::ed25519;
|
||||
}
|
||||
@@ -353,8 +425,9 @@ keypairFromRequst(boost::json::object const& request)
|
||||
if (has_key_type)
|
||||
{
|
||||
if (!request.at(secretType).is_string())
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS,
|
||||
"secret value must be string"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS,
|
||||
"secret value must be string"};
|
||||
|
||||
std::string key = request.at(secretType).as_string().c_str();
|
||||
|
||||
@@ -372,8 +445,9 @@ keypairFromRequst(boost::json::object const& request)
|
||||
else
|
||||
{
|
||||
if (!request.at("secret").is_string())
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS,
|
||||
"field secret should be a string"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS,
|
||||
"field secret should be a string"};
|
||||
|
||||
std::string secret = request.at("secret").as_string().c_str();
|
||||
seed = ripple::parseGenericSeed(secret);
|
||||
@@ -381,13 +455,15 @@ keypairFromRequst(boost::json::object const& request)
|
||||
}
|
||||
|
||||
if (!seed)
|
||||
return RPC::Status{RPC::Error::rpcBAD_SEED,
|
||||
"Bad Seed: invalid field message secretType"};
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcBAD_SEED,
|
||||
"Bad Seed: invalid field message secretType"};
|
||||
|
||||
if (keyType != ripple::KeyType::secp256k1
|
||||
&& keyType != ripple::KeyType::ed25519)
|
||||
return RPC::Status{RPC::Error::rpcINVALID_PARAMS,
|
||||
"keypairForSignature: invalid key type"};
|
||||
if (keyType != ripple::KeyType::secp256k1 &&
|
||||
keyType != ripple::KeyType::ed25519)
|
||||
return RPC::Status{
|
||||
RPC::Error::rpcINVALID_PARAMS,
|
||||
"keypairForSignature: invalid key type"};
|
||||
|
||||
return generateKeyPair(*keyType, *seed);
|
||||
}
|
||||
@@ -498,7 +574,8 @@ xrpLiquid(
|
||||
|
||||
std::uint32_t const ownerCount = sle.getFieldU32(ripple::sfOwnerCount);
|
||||
|
||||
auto const reserve = backend.fetchFees(sequence)->accountReserve(ownerCount);
|
||||
auto const reserve =
|
||||
backend.fetchFees(sequence)->accountReserve(ownerCount);
|
||||
|
||||
auto const balance = sle.getFieldAmount(ripple::sfBalance);
|
||||
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
#ifndef XRPL_REPORTING_RPCHELPERS_H_INCLUDED
|
||||
#define XRPL_REPORTING_RPCHELPERS_H_INCLUDED
|
||||
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <boost/json.hpp>
|
||||
#include <handlers/Status.h>
|
||||
#include <handlers/Context.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <handlers/Context.h>
|
||||
#include <handlers/Status.h>
|
||||
|
||||
std::optional<ripple::AccountID>
|
||||
accountFromStringStrict(std::string const& account);
|
||||
@@ -27,6 +27,9 @@ deserializeTxPlusMeta(
|
||||
Backend::TransactionAndMetadata const& blobs,
|
||||
std::uint32_t seq);
|
||||
|
||||
std::pair<boost::json::object, boost::json::object>
|
||||
toExpandedJson(Backend::TransactionAndMetadata const& blobs);
|
||||
|
||||
boost::json::object
|
||||
toJson(ripple::STBase const& obj);
|
||||
|
||||
|
||||
@@ -194,11 +194,10 @@ doAccountTx(Context const& context)
|
||||
|
||||
if (!binary)
|
||||
{
|
||||
auto [txn, meta] = deserializeTxPlusMeta(txnPlusMeta);
|
||||
obj["meta"] = toJson(*meta);
|
||||
obj["tx"] = toJson(*txn);
|
||||
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
||||
obj["meta"] = meta;
|
||||
obj["tx"] = txn;
|
||||
obj["tx"].as_object()["ledger_index"] = txnPlusMeta.ledgerSequence;
|
||||
obj["tx"].as_object()["inLedger"] = txnPlusMeta.ledgerSequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
#include <handlers/methods/Ledger.h>
|
||||
#include <handlers/RPCHelpers.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <handlers/RPCHelpers.h>
|
||||
#include <handlers/methods/Ledger.h>
|
||||
|
||||
namespace RPC
|
||||
{
|
||||
namespace RPC {
|
||||
|
||||
Result
|
||||
doLedger(Context const& context)
|
||||
@@ -12,27 +11,27 @@ doLedger(Context const& context)
|
||||
boost::json::object response = {};
|
||||
|
||||
bool binary = false;
|
||||
if(params.contains("binary"))
|
||||
if (params.contains("binary"))
|
||||
{
|
||||
if(!params.at("binary").is_bool())
|
||||
if (!params.at("binary").is_bool())
|
||||
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
||||
|
||||
binary = params.at("binary").as_bool();
|
||||
}
|
||||
|
||||
bool transactions = false;
|
||||
if(params.contains("transactions"))
|
||||
if (params.contains("transactions"))
|
||||
{
|
||||
if(!params.at("transactions").is_bool())
|
||||
if (!params.at("transactions").is_bool())
|
||||
return Status{Error::rpcINVALID_PARAMS, "transactionsFlagNotBool"};
|
||||
|
||||
transactions = params.at("transactions").as_bool();
|
||||
}
|
||||
|
||||
bool expand = false;
|
||||
if(params.contains("expand"))
|
||||
if (params.contains("expand"))
|
||||
{
|
||||
if(!params.at("expand").is_bool())
|
||||
if (!params.at("expand").is_bool())
|
||||
return Status{Error::rpcINVALID_PARAMS, "expandFlagNotBool"};
|
||||
|
||||
expand = params.at("expand").as_bool();
|
||||
@@ -55,7 +54,8 @@ doLedger(Context const& context)
|
||||
header["account_hash"] = ripple::strHex(lgrInfo.accountHash);
|
||||
header["close_flags"] = lgrInfo.closeFlags;
|
||||
header["close_time"] = lgrInfo.closeTime.time_since_epoch().count();
|
||||
header["close_time_human"] = ripple::to_string(lgrInfo.closeTime);;
|
||||
header["close_time_human"] = ripple::to_string(lgrInfo.closeTime);
|
||||
;
|
||||
header["close_time_resolution"] = lgrInfo.closeTimeResolution.count();
|
||||
header["closed"] = true;
|
||||
header["hash"] = ripple::strHex(lgrInfo.hash);
|
||||
@@ -69,6 +69,7 @@ doLedger(Context const& context)
|
||||
header["total_coins"] = ripple::to_string(lgrInfo.drops);
|
||||
header["transaction_hash"] = ripple::strHex(lgrInfo.txHash);
|
||||
}
|
||||
header["closed"] = true;
|
||||
|
||||
if (transactions)
|
||||
{
|
||||
@@ -76,7 +77,8 @@ doLedger(Context const& context)
|
||||
boost::json::array& jsonTxs = header.at("transactions").as_array();
|
||||
if (expand)
|
||||
{
|
||||
auto txns = context.backend->fetchAllTransactionsInLedger(lgrInfo.seq);
|
||||
auto txns =
|
||||
context.backend->fetchAllTransactionsInLedger(lgrInfo.seq);
|
||||
|
||||
std::transform(
|
||||
std::move_iterator(txns.begin()),
|
||||
@@ -86,16 +88,16 @@ doLedger(Context const& context)
|
||||
boost::json::object entry;
|
||||
if (!binary)
|
||||
{
|
||||
auto [sttx, meta] = deserializeTxPlusMeta(obj);
|
||||
entry = toJson(*sttx);
|
||||
entry["metaData"] = toJson(*meta);
|
||||
auto [txn, meta] = toExpandedJson(obj);
|
||||
entry = txn;
|
||||
entry["metaData"] = meta;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry["tx_blob"] = ripple::strHex(obj.transaction);
|
||||
entry["meta"] = ripple::strHex(obj.metadata);
|
||||
}
|
||||
entry["ledger_sequence"] = obj.ledgerSequence;
|
||||
entry["ledger_index"] = obj.ledgerSequence;
|
||||
return entry;
|
||||
});
|
||||
}
|
||||
@@ -120,4 +122,4 @@ doLedger(Context const& context)
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace RPC
|
||||
|
||||
@@ -17,13 +17,12 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <handlers/RPCHelpers.h>
|
||||
#include <handlers/methods/Transaction.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <backend/Pg.h>
|
||||
#include <handlers/RPCHelpers.h>
|
||||
#include <handlers/methods/Transaction.h>
|
||||
|
||||
namespace RPC
|
||||
{
|
||||
namespace RPC {
|
||||
|
||||
// {
|
||||
// transaction: <hex>
|
||||
@@ -46,9 +45,9 @@ doTx(Context const& context)
|
||||
return Status{Error::rpcINVALID_PARAMS, "malformedTransaction"};
|
||||
|
||||
bool binary = false;
|
||||
if(request.contains("binary"))
|
||||
if (request.contains("binary"))
|
||||
{
|
||||
if(!request.at("binary").is_bool())
|
||||
if (!request.at("binary").is_bool())
|
||||
return Status{Error::rpcINVALID_PARAMS, "binaryFlagNotBool"};
|
||||
|
||||
binary = request.at("binary").as_bool();
|
||||
@@ -64,9 +63,10 @@ doTx(Context const& context)
|
||||
|
||||
if (!binary)
|
||||
{
|
||||
auto [sttx, meta] = deserializeTxPlusMeta(dbResponse.value());
|
||||
response = toJson(*sttx);
|
||||
response["meta"] = toJson(*meta);
|
||||
auto [txn, meta] = toExpandedJson(*dbResponse);
|
||||
response = txn;
|
||||
response["meta"] = meta;
|
||||
response["ledger_index"] = dbResponse->ledgerSequence;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -77,4 +77,4 @@ doTx(Context const& context)
|
||||
return response;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
} // namespace RPC
|
||||
|
||||
Reference in New Issue
Block a user