mirror of
https://github.com/XRPLF/rippled.git
synced 2026-03-06 04:42:30 +00:00
Compare commits
6 Commits
Bronek/max
...
copilot/re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad116a35ff | ||
|
|
249fb12e8f | ||
|
|
cbabee1bec | ||
|
|
cf2835e3c1 | ||
|
|
9b0e87a37e | ||
|
|
4a31ee1926 |
@@ -177,7 +177,7 @@ jobs:
|
||||
|
||||
- name: Upload the binary (Linux)
|
||||
if: ${{ github.repository_owner == 'XRPLF' && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: xrpld-${{ inputs.config_name }}
|
||||
path: ${{ env.BUILD_DIR }}/xrpld
|
||||
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
|
||||
- name: Upload clang-tidy output
|
||||
if: steps.run_clang_tidy.outcome != 'success'
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: clang-tidy-results
|
||||
path: clang-tidy-output.txt
|
||||
|
||||
@@ -20,7 +20,7 @@ repos:
|
||||
args: [--assume-in-merge]
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: cd481d7b0bfb5c7b3090c21846317f9a8262e891 # frozen: v22.1.0
|
||||
rev: 75ca4ad908dc4a99f57921f29b7e6c1521e10b26 # frozen: v21.1.8
|
||||
hooks:
|
||||
- id: clang-format
|
||||
args: [--style=file]
|
||||
@@ -33,17 +33,17 @@ repos:
|
||||
additional_dependencies: [PyYAML]
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: c2bc67fe8f8f549cc489e00ba8b45aa18ee713b1 # frozen: v3.8.1
|
||||
rev: 5ba47274f9b181bce26a5150a725577f3c336011 # frozen: v3.6.2
|
||||
hooks:
|
||||
- id: prettier
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: ea488cebbfd88a5f50b8bd95d5c829d0bb76feb8 # frozen: 26.1.0
|
||||
rev: 831207fd435b47aeffdf6af853097e64322b4d44 # frozen: v25.12.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/streetsidesoftware/cspell-cli
|
||||
rev: a42085ade523f591dca134379a595e7859986445 # frozen: v9.7.0
|
||||
rev: 1cfa010f078c354f3ffb8413616280cc28f5ba21 # frozen: v9.4.0
|
||||
hooks:
|
||||
- id: cspell # Spell check changed files
|
||||
exclude: .config/cspell.config.yaml
|
||||
|
||||
@@ -6,6 +6,13 @@ For info about how [API versioning](https://xrpl.org/request-formatting.html#api
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
### Modifications to `tx` and `account_tx`
|
||||
|
||||
In API version 2, the `tx_json` field in `tx` and `account_tx` responses includes server-added lower-case fields (`date`, `ledger_index`, and `ctid`) that are not part of the canonical signed transaction. In API version 3, these fields are removed from `tx_json` and are only present at the top-level result object.
|
||||
|
||||
- **Before (API v2)**: The `tx_json` object in the response contained `date`, `ledger_index`, and `ctid` fields alongside the canonical PascalCase transaction fields.
|
||||
- **After (API v3)**: The `tx_json` object contains only the canonical signed transaction fields. The `date`, `ledger_index`, and `ctid` fields appear exclusively at the top-level result object.
|
||||
|
||||
### Modifications to `amm_info`
|
||||
|
||||
The order of error checks has been changed to provide more specific error messages. ([#4924](https://github.com/XRPLF/rippled/pull/4924))
|
||||
|
||||
@@ -23,13 +23,13 @@ public:
|
||||
static constexpr size_t initialBufferSize = kilobytes(256);
|
||||
|
||||
RawStateTable()
|
||||
: monotonic_resource_{
|
||||
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
|
||||
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
|
||||
initialBufferSize)}
|
||||
, items_{monotonic_resource_.get()} {};
|
||||
|
||||
RawStateTable(RawStateTable const& rhs)
|
||||
: monotonic_resource_{
|
||||
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
|
||||
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
|
||||
initialBufferSize)}
|
||||
, items_{rhs.items_, monotonic_resource_.get()}
|
||||
, dropsDestroyed_{rhs.dropsDestroyed_} {};
|
||||
|
||||
|
||||
@@ -64,12 +64,6 @@
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
// We do not want feature names to exceed this size.
|
||||
static constexpr std::size_t maxFeatureNameSize = 63;
|
||||
// We not want feature names of this length (and + 1), to enable the use of
|
||||
// 32-long byte string for selection of feature as uint256, in WASM
|
||||
static constexpr std::size_t reservedFeatureNameSize = 32;
|
||||
|
||||
enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes };
|
||||
enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported };
|
||||
|
||||
|
||||
@@ -23,9 +23,10 @@ struct JsonOptions
|
||||
none = 0b0000'0000,
|
||||
include_date = 0b0000'0001,
|
||||
disable_API_prior_V2 = 0b0000'0010,
|
||||
disable_API_prior_V3 = 0b0000'0100,
|
||||
|
||||
// IMPORTANT `_all` must be union of all of the above; see also operator~
|
||||
_all = 0b0000'0011
|
||||
_all = 0b0000'0111
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ OpenView::OpenView(
|
||||
ReadView const* base,
|
||||
Rules const& rules,
|
||||
std::shared_ptr<void const> hold)
|
||||
: monotonic_resource_{
|
||||
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
|
||||
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
|
||||
initialBufferSize)}
|
||||
, txs_{monotonic_resource_.get()}
|
||||
, rules_(rules)
|
||||
, header_(base->header())
|
||||
@@ -88,8 +88,8 @@ OpenView::OpenView(
|
||||
}
|
||||
|
||||
OpenView::OpenView(ReadView const* base, std::shared_ptr<void const> hold)
|
||||
: monotonic_resource_{
|
||||
std::make_unique<boost::container::pmr::monotonic_buffer_resource>(initialBufferSize)}
|
||||
: monotonic_resource_{std::make_unique<boost::container::pmr::monotonic_buffer_resource>(
|
||||
initialBufferSize)}
|
||||
, txs_{monotonic_resource_.get()}
|
||||
, rules_(base->rules())
|
||||
, header_(base->header())
|
||||
|
||||
@@ -395,22 +395,10 @@ featureToName(uint256 const& f)
|
||||
#pragma push_macro("XRPL_RETIRE_FIX")
|
||||
#undef XRPL_RETIRE_FIX
|
||||
|
||||
template <std::size_t N>
|
||||
constexpr auto
|
||||
enforceMaxFeatureNameSize(char const (&n)[N]) -> char const*
|
||||
{
|
||||
static_assert(N != reservedFeatureNameSize);
|
||||
static_assert(N != reservedFeatureNameSize + 1);
|
||||
static_assert(N <= maxFeatureNameSize);
|
||||
return n;
|
||||
}
|
||||
|
||||
#define XRPL_FEATURE(name, supported, vote) \
|
||||
uint256 const feature##name = \
|
||||
registerFeature(enforceMaxFeatureNameSize(#name), supported, vote);
|
||||
uint256 const feature##name = registerFeature(#name, supported, vote);
|
||||
#define XRPL_FIX(name, supported, vote) \
|
||||
uint256 const fix##name = \
|
||||
registerFeature(enforceMaxFeatureNameSize("fix" #name), supported, vote);
|
||||
uint256 const fix##name = registerFeature("fix" #name, supported, vote);
|
||||
|
||||
// clang-format off
|
||||
#define XRPL_RETIRE_FEATURE(name) \
|
||||
|
||||
@@ -133,9 +133,9 @@ STVar::constructST(SerializedTypeID id, int depth, Args&&... args)
|
||||
{
|
||||
construct<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
else if constexpr (
|
||||
std::
|
||||
is_same_v<std::tuple<std::remove_cvref_t<Args>...>, std::tuple<SerialIter, SField>>)
|
||||
else if constexpr (std::is_same_v<
|
||||
std::tuple<std::remove_cvref_t<Args>...>,
|
||||
std::tuple<SerialIter, SField>>)
|
||||
{
|
||||
construct<T>(std::forward<Args>(args)..., depth);
|
||||
}
|
||||
|
||||
@@ -180,9 +180,8 @@ ammAccountHolds(ReadView const& view, AccountID const& ammAccountID, Issue const
|
||||
if (auto const sle = view.read(keylet::account(ammAccountID)))
|
||||
return (*sle)[sfBalance];
|
||||
}
|
||||
else if (
|
||||
auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency));
|
||||
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
|
||||
else if (auto const sle = view.read(keylet::line(ammAccountID, issue.account, issue.currency));
|
||||
sle && !isFrozen(view, ammAccountID, issue.currency, issue.account))
|
||||
{
|
||||
auto amount = (*sle)[sfBalance];
|
||||
if (ammAccountID > issue.account)
|
||||
|
||||
@@ -42,9 +42,8 @@ AMMVote::preclaim(PreclaimContext const& ctx)
|
||||
}
|
||||
else if (ammSle->getFieldAmount(sfLPTokenBalance) == beast::zero)
|
||||
return tecAMM_EMPTY;
|
||||
else if (
|
||||
auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
|
||||
lpTokensNew == beast::zero)
|
||||
else if (auto const lpTokensNew = ammLPHolds(ctx.view, *ammSle, ctx.tx[sfAccount], ctx.j);
|
||||
lpTokensNew == beast::zero)
|
||||
{
|
||||
JLOG(ctx.j.debug()) << "AMM Vote: account is not LP.";
|
||||
return tecAMM_INVALID_TOKENS;
|
||||
|
||||
@@ -84,12 +84,11 @@ LoanSet::preflight(PreflightContext const& ctx)
|
||||
!validNumericMinimum(paymentInterval, LoanSet::minPaymentInterval))
|
||||
return temINVALID;
|
||||
// Grace period is between min default value and payment interval
|
||||
else if (
|
||||
auto const gracePeriod = tx[~sfGracePeriod]; //
|
||||
!validNumericRange(
|
||||
gracePeriod,
|
||||
paymentInterval.value_or(LoanSet::defaultPaymentInterval),
|
||||
defaultGracePeriod))
|
||||
else if (auto const gracePeriod = tx[~sfGracePeriod]; //
|
||||
!validNumericRange(
|
||||
gracePeriod,
|
||||
paymentInterval.value_or(LoanSet::defaultPaymentInterval),
|
||||
defaultGracePeriod))
|
||||
return temINVALID;
|
||||
|
||||
// Copied from preflight2
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include <xrpl/protocol/STTx.h>
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -394,48 +393,6 @@ public:
|
||||
return close(std::chrono::seconds(5));
|
||||
}
|
||||
|
||||
/** Close and advance the ledger, then synchronize with the server's
|
||||
io_context to ensure all async operations initiated by the close have
|
||||
been started.
|
||||
|
||||
This function performs the same ledger close as close(), but additionally
|
||||
ensures that all tasks posted to the server's io_context (such as
|
||||
WebSocket subscription message sends) have been initiated before returning.
|
||||
|
||||
What it guarantees:
|
||||
- All async operations posted before syncClose() have been STARTED
|
||||
- For WebSocket sends: async_write_some() has been called
|
||||
- The actual I/O completion may still be pending (async)
|
||||
|
||||
What it does NOT guarantee:
|
||||
- Async operations have COMPLETED
|
||||
- WebSocket messages have been received by clients
|
||||
- However, for localhost connections, the remaining latency is typically
|
||||
microseconds, making tests reliable
|
||||
|
||||
Use this instead of close() when:
|
||||
- Test code immediately checks for subscription messages
|
||||
- Race conditions between test and worker threads must be avoided
|
||||
- Deterministic test behavior is required
|
||||
|
||||
@param timeout Maximum time to wait for the barrier task to execute
|
||||
@return true if close succeeded and barrier executed within timeout,
|
||||
false otherwise
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
syncClose(std::chrono::steady_clock::duration timeout = std::chrono::seconds{1})
|
||||
{
|
||||
XRPL_ASSERT(
|
||||
app().getNumberOfThreads() == 1,
|
||||
"syncClose() is only useful on an application with a single thread");
|
||||
auto const result = close();
|
||||
auto serverBarrier = std::make_shared<std::promise<void>>();
|
||||
auto future = serverBarrier->get_future();
|
||||
boost::asio::post(app().getIOContext(), [serverBarrier]() { serverBarrier->set_value(); });
|
||||
auto const status = future.wait_for(timeout);
|
||||
return result && status == std::future_status::ready;
|
||||
}
|
||||
|
||||
/** Turn on JSON tracing.
|
||||
With no arguments, trace all
|
||||
*/
|
||||
|
||||
@@ -73,8 +73,6 @@ std::unique_ptr<Config> admin_localnet(std::unique_ptr<Config>);
|
||||
|
||||
std::unique_ptr<Config> secure_gateway_localnet(std::unique_ptr<Config>);
|
||||
|
||||
std::unique_ptr<Config> single_thread_io(std::unique_ptr<Config>);
|
||||
|
||||
/// @brief adjust configuration with params needed to be a validator
|
||||
///
|
||||
/// this is intended for use with envconfig, as in
|
||||
|
||||
@@ -87,12 +87,6 @@ secure_gateway_localnet(std::unique_ptr<Config> cfg)
|
||||
(*cfg)[PORT_WS].set("secure_gateway", "127.0.0.0/8");
|
||||
return cfg;
|
||||
}
|
||||
std::unique_ptr<Config>
|
||||
single_thread_io(std::unique_ptr<Config> cfg)
|
||||
{
|
||||
cfg->IO_WORKERS = 1;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
auto constexpr defaultseed = "shUwVw52ofnCUX5m7kPTKzJdr4HEH";
|
||||
|
||||
|
||||
@@ -122,20 +122,52 @@ class AccountTx_test : public beast::unit_test::suite
|
||||
{
|
||||
auto const& payment = j[jss::result][jss::transactions][1u];
|
||||
|
||||
return (payment.isMember(jss::tx_json)) &&
|
||||
(payment[jss::tx_json][jss::TransactionType] == jss::Payment) &&
|
||||
(payment[jss::tx_json][jss::DeliverMax] == "10000000010") &&
|
||||
(!payment[jss::tx_json].isMember(jss::Amount)) &&
|
||||
(!payment[jss::tx_json].isMember(jss::hash)) &&
|
||||
(payment[jss::hash] ==
|
||||
"9F3085D85F472D1CC29627F260DF68EDE59D42D1D0C33E345"
|
||||
"ECF0D4CE981D0A8") &&
|
||||
(payment[jss::validated] == true) &&
|
||||
(payment[jss::ledger_index] == 3) &&
|
||||
(payment[jss::ledger_hash] ==
|
||||
"5476DCD816EA04CBBA57D47BBF1FC58A5217CC93A5ADD79CB"
|
||||
"580A5AFDD727E33") &&
|
||||
(payment[jss::close_time_iso] == "2000-01-01T00:00:10Z");
|
||||
if (apiVersion >= 3)
|
||||
{
|
||||
// In API v3, server-added lower-case fields must
|
||||
// not be in tx_json, but must be at result level
|
||||
return (payment.isMember(jss::tx_json)) &&
|
||||
(payment[jss::tx_json][jss::TransactionType] == jss::Payment) &&
|
||||
(payment[jss::tx_json][jss::DeliverMax] == "10000000010") &&
|
||||
(!payment[jss::tx_json].isMember(jss::Amount)) &&
|
||||
(!payment[jss::tx_json].isMember(jss::hash)) &&
|
||||
(!payment[jss::tx_json].isMember(jss::date)) &&
|
||||
(!payment[jss::tx_json].isMember(jss::ledger_index)) &&
|
||||
(!payment[jss::tx_json].isMember(jss::ctid)) &&
|
||||
// date and ctid must be at the transaction
|
||||
// object level (outside tx_json) in API v3
|
||||
(payment.isMember(jss::date)) && (payment.isMember(jss::ctid)) &&
|
||||
(payment[jss::hash] ==
|
||||
"9F3085D85F472D1CC29627F260DF68EDE59D42D1D0C33E345"
|
||||
"ECF0D4CE981D0A8") &&
|
||||
(payment[jss::validated] == true) &&
|
||||
(payment[jss::ledger_index] == 3) &&
|
||||
(payment[jss::ledger_hash] ==
|
||||
"5476DCD816EA04CBBA57D47BBF1FC58A5217CC93A5ADD79CB"
|
||||
"580A5AFDD727E33") &&
|
||||
(payment[jss::close_time_iso] == "2000-01-01T00:00:10Z");
|
||||
}
|
||||
else
|
||||
{
|
||||
// In API v2, date and ledger_index are still in
|
||||
// tx_json for backwards compatibility
|
||||
return (payment.isMember(jss::tx_json)) &&
|
||||
(payment[jss::tx_json][jss::TransactionType] == jss::Payment) &&
|
||||
(payment[jss::tx_json][jss::DeliverMax] == "10000000010") &&
|
||||
(!payment[jss::tx_json].isMember(jss::Amount)) &&
|
||||
(!payment[jss::tx_json].isMember(jss::hash)) &&
|
||||
(payment[jss::tx_json].isMember(jss::date)) &&
|
||||
(payment[jss::tx_json].isMember(jss::ledger_index)) &&
|
||||
(payment[jss::hash] ==
|
||||
"9F3085D85F472D1CC29627F260DF68EDE59D42D1D0C33E345"
|
||||
"ECF0D4CE981D0A8") &&
|
||||
(payment[jss::validated] == true) &&
|
||||
(payment[jss::ledger_index] == 3) &&
|
||||
(payment[jss::ledger_hash] ==
|
||||
"5476DCD816EA04CBBA57D47BBF1FC58A5217CC93A5ADD79CB"
|
||||
"580A5AFDD727E33") &&
|
||||
(payment[jss::close_time_iso] == "2000-01-01T00:00:10Z");
|
||||
}
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <xrpl/ledger/AmendmentTable.h>
|
||||
#include <xrpl/protocol/Feature.h>
|
||||
#include <xrpl/protocol/digest.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
namespace xrpl {
|
||||
@@ -169,18 +168,16 @@ class Feature_test : public beast::unit_test::suite
|
||||
using namespace test::jtx;
|
||||
Env env{*this};
|
||||
|
||||
std::string const name = "fixAMMOverflowOffer";
|
||||
auto jrr = env.rpc("feature", name)[jss::result];
|
||||
auto jrr = env.rpc("feature", "fixAMMOverflowOffer")[jss::result];
|
||||
BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
|
||||
jrr.removeMember(jss::status);
|
||||
BEAST_EXPECT(jrr.size() == 1);
|
||||
auto const expected = to_string(sha512Half(Slice(name.data(), name.size())));
|
||||
char const sha[] = "12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E2A87F1D8107";
|
||||
BEAST_EXPECT(expected == sha);
|
||||
BEAST_EXPECT(jrr.isMember(expected));
|
||||
BEAST_EXPECT(jrr.isMember(
|
||||
"12523DF04B553A0B1AD74F42DDB741DE8DC06A03FC089A0EF197E"
|
||||
"2A87F1D8107"));
|
||||
auto feature = *(jrr.begin());
|
||||
|
||||
BEAST_EXPECTS(feature[jss::name] == name, "name");
|
||||
BEAST_EXPECTS(feature[jss::name] == "fixAMMOverflowOffer", "name");
|
||||
BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled");
|
||||
BEAST_EXPECTS(feature[jss::vetoed].isBool() && !feature[jss::vetoed].asBool(), "vetoed");
|
||||
BEAST_EXPECTS(feature[jss::supported].asBool(), "supported");
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env{*this, single_thread_io(envconfig())};
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
@@ -92,7 +92,7 @@ public:
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env{*this, single_thread_io(envconfig())};
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
|
||||
{
|
||||
// Accept a ledger
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
|
||||
{
|
||||
// Accept another ledger
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// Check stream update
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace jtx;
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
auto baseFee = env.current()->fees().base.drops();
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
@@ -171,7 +171,7 @@ public:
|
||||
|
||||
{
|
||||
env.fund(XRP(10000), "alice");
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// Check stream update for payment transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
@@ -195,7 +195,7 @@ public:
|
||||
}));
|
||||
|
||||
env.fund(XRP(10000), "bob");
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// Check stream update for payment transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
@@ -249,12 +249,12 @@ public:
|
||||
{
|
||||
// Transaction that does not affect stream
|
||||
env.fund(XRP(10000), "carol");
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
BEAST_EXPECT(!wsc->getMsg(10ms));
|
||||
|
||||
// Transactions concerning alice
|
||||
env.trust(Account("bob")["USD"](100), "alice");
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// Check stream updates
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
@@ -288,7 +288,6 @@ public:
|
||||
using namespace jtx;
|
||||
Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
|
||||
cfg->FEES.reference_fee = 10;
|
||||
cfg = single_thread_io(std::move(cfg));
|
||||
return cfg;
|
||||
}));
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
@@ -311,7 +310,7 @@ public:
|
||||
|
||||
{
|
||||
env.fund(XRP(10000), "alice");
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// Check stream update for payment transaction
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
@@ -361,7 +360,7 @@ public:
|
||||
testManifests()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
|
||||
@@ -395,7 +394,7 @@ public:
|
||||
{
|
||||
using namespace jtx;
|
||||
|
||||
Env env{*this, single_thread_io(envconfig(validator, "")), features};
|
||||
Env env{*this, envconfig(validator, ""), features};
|
||||
auto& cfg = env.app().config();
|
||||
if (!BEAST_EXPECT(cfg.section(SECTION_VALIDATION_SEED).empty()))
|
||||
return;
|
||||
@@ -484,7 +483,7 @@ public:
|
||||
// at least one flag ledger.
|
||||
while (env.closed()->header().seq < 300)
|
||||
{
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
using namespace std::chrono_literals;
|
||||
BEAST_EXPECT(wsc->findMsg(5s, validValidationFields));
|
||||
}
|
||||
@@ -506,7 +505,7 @@ public:
|
||||
{
|
||||
using namespace jtx;
|
||||
testcase("Subscribe by url");
|
||||
Env env{*this, single_thread_io(envconfig())};
|
||||
Env env{*this};
|
||||
|
||||
Json::Value jv;
|
||||
jv[jss::url] = "http://localhost/events";
|
||||
@@ -537,7 +536,7 @@ public:
|
||||
auto const method = subscribe ? "subscribe" : "unsubscribe";
|
||||
testcase << "Error cases for " << method;
|
||||
|
||||
Env env{*this, single_thread_io(envconfig())};
|
||||
Env env{*this};
|
||||
auto wsc = makeWSClient(env.app().config());
|
||||
|
||||
{
|
||||
@@ -573,7 +572,7 @@ public:
|
||||
}
|
||||
|
||||
{
|
||||
Env env_nonadmin{*this, single_thread_io(no_admin(envconfig()))};
|
||||
Env env_nonadmin{*this, no_admin(envconfig())};
|
||||
Json::Value jv;
|
||||
jv[jss::url] = "no-url";
|
||||
auto jr = env_nonadmin.rpc("json", method, to_string(jv))[jss::result];
|
||||
@@ -835,13 +834,12 @@ public:
|
||||
* send payments between the two accounts a and b,
|
||||
* and close ledgersToClose ledgers
|
||||
*/
|
||||
auto sendPayments = [this](
|
||||
Env& env,
|
||||
Account const& a,
|
||||
Account const& b,
|
||||
int newTxns,
|
||||
std::uint32_t ledgersToClose,
|
||||
int numXRP = 10) {
|
||||
auto sendPayments = [](Env& env,
|
||||
Account const& a,
|
||||
Account const& b,
|
||||
int newTxns,
|
||||
std::uint32_t ledgersToClose,
|
||||
int numXRP = 10) {
|
||||
env.memoize(a);
|
||||
env.memoize(b);
|
||||
for (int i = 0; i < newTxns; ++i)
|
||||
@@ -854,7 +852,7 @@ public:
|
||||
jtx::sig(jtx::autofill));
|
||||
}
|
||||
for (int i = 0; i < ledgersToClose; ++i)
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
return newTxns;
|
||||
};
|
||||
|
||||
@@ -947,7 +945,7 @@ public:
|
||||
*
|
||||
* also test subscribe to the account before it is created
|
||||
*/
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
auto wscTxHistory = makeWSClient(env.app().config());
|
||||
Json::Value request;
|
||||
request[jss::account_history_tx_stream] = Json::objectValue;
|
||||
@@ -990,7 +988,7 @@ public:
|
||||
* subscribe genesis account tx history without txns
|
||||
* subscribe to bob's account after it is created
|
||||
*/
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
auto wscTxHistory = makeWSClient(env.app().config());
|
||||
Json::Value request;
|
||||
request[jss::account_history_tx_stream] = Json::objectValue;
|
||||
@@ -1000,7 +998,6 @@ public:
|
||||
if (!BEAST_EXPECT(goodSubRPC(jv)))
|
||||
return;
|
||||
IdxHashVec genesisFullHistoryVec;
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
if (!BEAST_EXPECT(!getTxHash(*wscTxHistory, genesisFullHistoryVec, 1).first))
|
||||
return;
|
||||
|
||||
@@ -1019,7 +1016,6 @@ public:
|
||||
if (!BEAST_EXPECT(goodSubRPC(jv)))
|
||||
return;
|
||||
IdxHashVec bobFullHistoryVec;
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
r = getTxHash(*wscTxHistory, bobFullHistoryVec, 1);
|
||||
if (!BEAST_EXPECT(r.first && r.second))
|
||||
return;
|
||||
@@ -1054,7 +1050,6 @@ public:
|
||||
"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh";
|
||||
jv = wscTxHistory->invoke("subscribe", request);
|
||||
genesisFullHistoryVec.clear();
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
BEAST_EXPECT(getTxHash(*wscTxHistory, genesisFullHistoryVec, 31).second);
|
||||
jv = wscTxHistory->invoke("unsubscribe", request);
|
||||
|
||||
@@ -1067,13 +1062,13 @@ public:
|
||||
* subscribe account and subscribe account tx history
|
||||
* and compare txns streamed
|
||||
*/
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
auto wscAccount = makeWSClient(env.app().config());
|
||||
auto wscTxHistory = makeWSClient(env.app().config());
|
||||
|
||||
std::array<Account, 2> accounts = {alice, bob};
|
||||
env.fund(XRP(222222), accounts);
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// subscribe account
|
||||
Json::Value stream = Json::objectValue;
|
||||
@@ -1136,18 +1131,18 @@ public:
|
||||
* alice issues USD to carol
|
||||
* mix USD and XRP payments
|
||||
*/
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
auto const USD_a = alice["USD"];
|
||||
|
||||
std::array<Account, 2> accounts = {alice, carol};
|
||||
env.fund(XRP(333333), accounts);
|
||||
env.trust(USD_a(20000), carol);
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
auto mixedPayments = [&]() -> int {
|
||||
sendPayments(env, alice, carol, 1, 0);
|
||||
env(pay(alice, carol, USD_a(100)));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
return 2;
|
||||
};
|
||||
|
||||
@@ -1157,7 +1152,6 @@ public:
|
||||
request[jss::account_history_tx_stream][jss::account] = carol.human();
|
||||
auto ws = makeWSClient(env.app().config());
|
||||
auto jv = ws->invoke("subscribe", request);
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
{
|
||||
// take out existing txns from the stream
|
||||
IdxHashVec tempVec;
|
||||
@@ -1175,10 +1169,10 @@ public:
|
||||
/*
|
||||
* long transaction history
|
||||
*/
|
||||
Env env(*this, single_thread_io(envconfig()));
|
||||
Env env(*this);
|
||||
std::array<Account, 2> accounts = {alice, carol};
|
||||
env.fund(XRP(444444), accounts);
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
// many payments, and close lots of ledgers
|
||||
auto oneRound = [&](int numPayments) {
|
||||
@@ -1191,7 +1185,6 @@ public:
|
||||
request[jss::account_history_tx_stream][jss::account] = carol.human();
|
||||
auto wscLong = makeWSClient(env.app().config());
|
||||
auto jv = wscLong->invoke("subscribe", request);
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
{
|
||||
// take out existing txns from the stream
|
||||
IdxHashVec tempVec;
|
||||
@@ -1229,7 +1222,7 @@ public:
|
||||
jtx::testable_amendments() | featurePermissionedDomains | featureCredentials |
|
||||
featurePermissionedDEX};
|
||||
|
||||
Env env(*this, single_thread_io(envconfig()), all);
|
||||
Env env(*this, all);
|
||||
PermissionedDEX permDex(env);
|
||||
auto const alice = permDex.alice;
|
||||
auto const bob = permDex.bob;
|
||||
@@ -1248,10 +1241,10 @@ public:
|
||||
if (!BEAST_EXPECT(jv[jss::status] == "success"))
|
||||
return;
|
||||
env(offer(alice, XRP(10), USD(10)), domain(domainID), txflags(tfHybrid));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
env(pay(bob, carol, USD(5)), path(~USD), sendmax(XRP(5)), domain(domainID));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(wsc->findMsg(5s, [&](auto const& jv) {
|
||||
if (jv[jss::changes].size() != 1)
|
||||
@@ -1291,9 +1284,9 @@ public:
|
||||
Account const bob{"bob"};
|
||||
Account const broker{"broker"};
|
||||
|
||||
Env env{*this, single_thread_io(envconfig()), features};
|
||||
Env env{*this, features};
|
||||
env.fund(XRP(10000), alice, bob, broker);
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
|
||||
auto wsc = test::makeWSClient(env.app().config());
|
||||
Json::Value stream;
|
||||
@@ -1357,12 +1350,12 @@ public:
|
||||
// Verify the NFTokenIDs are correct in the NFTokenMint tx meta
|
||||
uint256 const nftId1{token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenID(nftId1);
|
||||
|
||||
uint256 const nftId2{token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenID(nftId2);
|
||||
|
||||
// Alice creates one sell offer for each NFT
|
||||
@@ -1370,32 +1363,32 @@ public:
|
||||
// meta
|
||||
uint256 const aliceOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId1, drops(1)), txflags(tfSellNFToken));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex1);
|
||||
|
||||
uint256 const aliceOfferIndex2 = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId2, drops(1)), txflags(tfSellNFToken));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex2);
|
||||
|
||||
// Alice cancels two offers she created
|
||||
// Verify the NFTokenIDs are correct in the NFTokenCancelOffer tx
|
||||
// meta
|
||||
env(token::cancelOffer(alice, {aliceOfferIndex1, aliceOfferIndex2}));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId1, nftId2});
|
||||
|
||||
// Bobs creates a buy offer for nftId1
|
||||
// Verify the offer id is correct in the NFTokenCreateOffer tx meta
|
||||
auto const bobBuyOfferIndex = keylet::nftoffer(bob, env.seq(bob)).key;
|
||||
env(token::createOffer(bob, nftId1, drops(1)), token::owner(alice));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(bobBuyOfferIndex);
|
||||
|
||||
// Alice accepts bob's buy offer
|
||||
// Verify the NFTokenID is correct in the NFTokenAcceptOffer tx meta
|
||||
env(token::acceptBuyOffer(alice, bobBuyOfferIndex));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenID(nftId1);
|
||||
}
|
||||
|
||||
@@ -1404,7 +1397,7 @@ public:
|
||||
// Alice mints a NFT
|
||||
uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
|
||||
// Alice creates sell offer and set broker as destination
|
||||
@@ -1412,18 +1405,18 @@ public:
|
||||
env(token::createOffer(alice, nftId, drops(1)),
|
||||
token::destination(broker),
|
||||
txflags(tfSellNFToken));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(offerAliceToBroker);
|
||||
|
||||
// Bob creates buy offer
|
||||
uint256 const offerBobToBroker = keylet::nftoffer(bob, env.seq(bob)).key;
|
||||
env(token::createOffer(bob, nftId, drops(1)), token::owner(alice));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(offerBobToBroker);
|
||||
|
||||
// Check NFTokenID meta for NFTokenAcceptOffer in brokered mode
|
||||
env(token::brokerOffers(broker, offerBobToBroker, offerAliceToBroker));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
}
|
||||
|
||||
@@ -1433,24 +1426,24 @@ public:
|
||||
// Alice mints a NFT
|
||||
uint256 const nftId{token::getNextID(env, alice, 0u, tfTransferable)};
|
||||
env(token::mint(alice, 0u), txflags(tfTransferable));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenID(nftId);
|
||||
|
||||
// Alice creates 2 sell offers for the same NFT
|
||||
uint256 const aliceOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)), txflags(tfSellNFToken));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex1);
|
||||
|
||||
uint256 const aliceOfferIndex2 = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::createOffer(alice, nftId, drops(1)), txflags(tfSellNFToken));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceOfferIndex2);
|
||||
|
||||
// Make sure the metadata only has 1 nft id, since both offers are
|
||||
// for the same nft
|
||||
env(token::cancelOffer(alice, {aliceOfferIndex1, aliceOfferIndex2}));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenIDsInCancelOffer({nftId});
|
||||
}
|
||||
|
||||
@@ -1458,7 +1451,7 @@ public:
|
||||
{
|
||||
uint256 const aliceMintWithOfferIndex1 = keylet::nftoffer(alice, env.seq(alice)).key;
|
||||
env(token::mint(alice), token::amount(XRP(0)));
|
||||
BEAST_EXPECT(env.syncClose());
|
||||
env.close();
|
||||
verifyNFTokenOfferID(aliceMintWithOfferIndex1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -760,6 +760,25 @@ class Transaction_test : public beast::unit_test::suite
|
||||
result[jss::result][jss::ledger_hash] ==
|
||||
"B41882E20F0EC6228417D28B9AE0F33833645D35F6799DFB782AC97FC4BB51"
|
||||
"D2");
|
||||
|
||||
auto const& tx_json = result[jss::result][jss::tx_json];
|
||||
if (apiVersion >= 3)
|
||||
{
|
||||
// In API v3, server-added lower-case fields must not appear
|
||||
// inside tx_json; they are at the result level.
|
||||
BEAST_EXPECT(!tx_json.isMember(jss::date));
|
||||
BEAST_EXPECT(!tx_json.isMember(jss::ledger_index));
|
||||
BEAST_EXPECT(!tx_json.isMember(jss::ctid));
|
||||
// date must be at result level in API v3
|
||||
BEAST_EXPECT(result[jss::result].isMember(jss::date));
|
||||
}
|
||||
else
|
||||
{
|
||||
// In API v2, date and ledger_index are still included in
|
||||
// tx_json for backwards compatibility.
|
||||
BEAST_EXPECT(tx_json.isMember(jss::date));
|
||||
BEAST_EXPECT(tx_json.isMember(jss::ledger_index));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto memberIt = expected.begin(); memberIt != expected.end(); memberIt++)
|
||||
|
||||
@@ -1072,12 +1072,6 @@ public:
|
||||
return trapTxID_;
|
||||
}
|
||||
|
||||
size_t
|
||||
getNumberOfThreads() const override
|
||||
{
|
||||
return get_number_of_threads();
|
||||
}
|
||||
|
||||
private:
|
||||
// For a newly-started validator, this is the greatest persisted ledger
|
||||
// and new validations must be greater than this.
|
||||
|
||||
@@ -157,10 +157,6 @@ public:
|
||||
* than the last ledger it persisted. */
|
||||
virtual LedgerIndex
|
||||
getMaxDisallowedLedger() = 0;
|
||||
|
||||
/** Returns the number of io_context (I/O worker) threads used by the application. */
|
||||
virtual size_t
|
||||
getNumberOfThreads() const = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Application>
|
||||
|
||||
@@ -23,10 +23,4 @@ public:
|
||||
{
|
||||
return io_context_;
|
||||
}
|
||||
|
||||
size_t
|
||||
get_number_of_threads() const
|
||||
{
|
||||
return threads_.size();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -141,28 +141,30 @@ Transaction::getJson(JsonOptions options, bool binary) const
|
||||
ret[jss::inLedger] = mLedgerIndex;
|
||||
}
|
||||
|
||||
// TODO: disable_API_prior_V3 to disable output of both `date` and
|
||||
// `ledger_index` elements (taking precedence over include_date)
|
||||
ret[jss::ledger_index] = mLedgerIndex;
|
||||
|
||||
if (options & JsonOptions::include_date)
|
||||
if (!(options & JsonOptions::disable_API_prior_V3))
|
||||
{
|
||||
auto ct = mApp.getLedgerMaster().getCloseTimeBySeq(mLedgerIndex);
|
||||
if (ct)
|
||||
ret[jss::date] = ct->time_since_epoch().count();
|
||||
}
|
||||
ret[jss::ledger_index] = mLedgerIndex;
|
||||
|
||||
// compute outgoing CTID
|
||||
// override local network id if it's explicitly in the txn
|
||||
std::optional netID = mNetworkID;
|
||||
if (mTransaction->isFieldPresent(sfNetworkID))
|
||||
netID = mTransaction->getFieldU32(sfNetworkID);
|
||||
if (options & JsonOptions::include_date)
|
||||
{
|
||||
auto ct = mApp.getLedgerMaster().getCloseTimeBySeq(mLedgerIndex);
|
||||
if (ct)
|
||||
ret[jss::date] = ct->time_since_epoch().count();
|
||||
}
|
||||
|
||||
if (mTxnSeq && netID)
|
||||
{
|
||||
std::optional<std::string> const ctid = RPC::encodeCTID(mLedgerIndex, *mTxnSeq, *netID);
|
||||
if (ctid)
|
||||
ret[jss::ctid] = *ctid;
|
||||
// compute outgoing CTID
|
||||
// override local network id if it's explicitly in the txn
|
||||
std::optional netID = mNetworkID;
|
||||
if (mTransaction->isFieldPresent(sfNetworkID))
|
||||
netID = mTransaction->getFieldU32(sfNetworkID);
|
||||
|
||||
if (mTxnSeq && netID)
|
||||
{
|
||||
std::optional<std::string> const ctid =
|
||||
RPC::encodeCTID(mLedgerIndex, *mTxnSeq, *netID);
|
||||
if (ctid)
|
||||
ret[jss::ctid] = *ctid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <xrpld/app/misc/DeliverMax.h>
|
||||
#include <xrpld/app/misc/Transaction.h>
|
||||
#include <xrpld/app/rdb/backend/SQLiteDatabase.h>
|
||||
#include <xrpld/rpc/CTID.h>
|
||||
#include <xrpld/rpc/Context.h>
|
||||
#include <xrpld/rpc/DeliveredAmount.h>
|
||||
#include <xrpld/rpc/MPTokenIssuanceID.h>
|
||||
@@ -11,6 +12,7 @@
|
||||
#include <xrpld/rpc/detail/RPCLedgerHelpers.h>
|
||||
#include <xrpld/rpc/detail/Tuning.h>
|
||||
|
||||
#include <xrpl/core/NetworkIDService.h>
|
||||
#include <xrpl/json/json_value.h>
|
||||
#include <xrpl/ledger/ReadView.h>
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
@@ -286,8 +288,10 @@ populateJsonResponse(
|
||||
auto const json_tx = (context.apiVersion > 1 ? jss::tx_json : jss::tx);
|
||||
if (context.apiVersion > 1)
|
||||
{
|
||||
jvObj[json_tx] = txn->getJson(
|
||||
JsonOptions::include_date | JsonOptions::disable_API_prior_V2, false);
|
||||
auto const opts = context.apiVersion >= 3
|
||||
? JsonOptions::disable_API_prior_V2 | JsonOptions::disable_API_prior_V3
|
||||
: JsonOptions::include_date | JsonOptions::disable_API_prior_V2;
|
||||
jvObj[json_tx] = txn->getJson(opts, false);
|
||||
jvObj[jss::hash] = to_string(txn->getID());
|
||||
jvObj[jss::ledger_index] = txn->getLedger();
|
||||
jvObj[jss::ledger_hash] =
|
||||
@@ -295,7 +299,20 @@ populateJsonResponse(
|
||||
|
||||
if (auto closeTime =
|
||||
context.ledgerMaster.getCloseTimeBySeq(txn->getLedger()))
|
||||
{
|
||||
jvObj[jss::close_time_iso] = to_string_iso(*closeTime);
|
||||
if (context.apiVersion >= 3)
|
||||
jvObj[jss::date] = closeTime->time_since_epoch().count();
|
||||
}
|
||||
|
||||
if (context.apiVersion >= 3 && txnMeta)
|
||||
{
|
||||
uint32_t const lgrSeq = txn->getLedger();
|
||||
uint32_t const txnIdx = txnMeta->getIndex();
|
||||
uint32_t const netID = context.app.getNetworkIDService().getNetworkID();
|
||||
if (auto const ctid = RPC::encodeCTID(lgrSeq, txnIdx, netID))
|
||||
jvObj[jss::ctid] = *ctid;
|
||||
}
|
||||
}
|
||||
else
|
||||
jvObj[json_tx] = txn->getJson(JsonOptions::include_date);
|
||||
|
||||
@@ -189,8 +189,14 @@ populateJsonResponse(
|
||||
auto const& sttx = result.txn->getSTransaction();
|
||||
if (context.apiVersion > 1)
|
||||
{
|
||||
constexpr auto optionsJson =
|
||||
// In API v2, include_date and disable_API_prior_V2 are used to
|
||||
// include date/ledger_index/ctid in tx_json. In API v3+, those
|
||||
// fields are excluded from tx_json and are only at result level.
|
||||
constexpr auto optionsV2 =
|
||||
JsonOptions::include_date | JsonOptions::disable_API_prior_V2;
|
||||
constexpr auto optionsV3 =
|
||||
JsonOptions::disable_API_prior_V2 | JsonOptions::disable_API_prior_V3;
|
||||
auto const optionsJson = context.apiVersion >= 3 ? optionsV3 : optionsV2;
|
||||
if (args.binary)
|
||||
response[jss::tx_blob] = result.txn->getJson(optionsJson, true);
|
||||
else
|
||||
@@ -210,7 +216,11 @@ populateJsonResponse(
|
||||
{
|
||||
response[jss::ledger_index] = result.txn->getLedger();
|
||||
if (result.closeTime)
|
||||
{
|
||||
response[jss::close_time_iso] = to_string_iso(*result.closeTime);
|
||||
if (context.apiVersion >= 3)
|
||||
response[jss::date] = result.closeTime->time_since_epoch().count();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user