mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-30 07:55:51 +00:00
Tx queue enhancements and RPC info (RIPD-1205, RIPD-1206):
* Account-related queue stats (RIPD-1205). Boolean "queue" parameter to account_info only if requesting the open ledger. * Account for the TxQ when autofilling sequence in sign-and-submit (RIPD-1206) * Tweak TxQ::accept edge case when choosing which tx to try next. * Labels for experimental "x_" submit parameters use correct separator. === Release Notes === ==== New features ==== When requesting `account_info` for the open ledger, include the `queue : true` to get extra information about any queued transactions for this account. (RIPD-1205). ==== Bug fixes ==== When using sign-and-submit mode to autofill a transaction's sequence number, the logic will not reuse a sequence number that is in the queue for this account. (RIPD-1206). Labels for experimental "x_queue_okay" and "x_assume_tx" parameters to `sign` and `submit` updated to use correct separator.
This commit is contained in:
@@ -865,9 +865,8 @@ void LedgerConsensusImp::accept (std::shared_ptr<SHAMap> set)
|
||||
});
|
||||
}
|
||||
// Update fee computations.
|
||||
app_.getTxQ().processValidatedLedger(app_, accum,
|
||||
app_.getTxQ().processClosedLedger(app_, accum,
|
||||
roundTime_ > 5s);
|
||||
|
||||
accum.apply(*buildLCL);
|
||||
}
|
||||
|
||||
|
||||
@@ -1409,7 +1409,7 @@ void NetworkOPsImp::switchLastClosedLedger (
|
||||
|
||||
// Update fee computations.
|
||||
// TODO: Needs an open ledger
|
||||
//app_.getTxQ().processValidatedLedger(app_, *newLCL, true);
|
||||
//app_.getTxQ().processClosedLedger(app_, *newLCL, true);
|
||||
|
||||
// Caller must own master lock
|
||||
{
|
||||
|
||||
@@ -90,6 +90,13 @@ public:
|
||||
std::uint64_t expFeeLevel; // Estimated fee level to get in next ledger
|
||||
};
|
||||
|
||||
struct AccountTxDetails
|
||||
{
|
||||
uint64_t feeLevel;
|
||||
boost::optional<LedgerIndex const> lastValid;
|
||||
boost::optional<TxConsequences const> consequences;
|
||||
};
|
||||
|
||||
TxQ(Setup const& setup,
|
||||
beast::Journal j);
|
||||
|
||||
@@ -120,19 +127,33 @@ public:
|
||||
accept(Application& app, OpenView& view);
|
||||
|
||||
/**
|
||||
A new ledger has been validated. Update and clean up the
|
||||
queue.
|
||||
Update stats and clean up the queue in preparation for
|
||||
the next ledger.
|
||||
*/
|
||||
void
|
||||
processValidatedLedger(Application& app,
|
||||
processClosedLedger(Application& app,
|
||||
OpenView const& view, bool timeLeap);
|
||||
|
||||
/** Returns fee metrics in reference fee level units.
|
||||
|
||||
@returns Uninitialized @ref optional if the FeeEscalation
|
||||
amendment is not enabled.
|
||||
*/
|
||||
boost::optional<Metrics>
|
||||
getMetrics(Config const& config, OpenView const& view,
|
||||
std::uint32_t txCountPadding = 0) const;
|
||||
|
||||
/** Returns information about the transactions currently
|
||||
in the queue for the account.
|
||||
|
||||
@returns Uninitialized @ref optional if the FeeEscalation
|
||||
amendment is not enabled, OR if the account has no transactions
|
||||
in the queue.
|
||||
*/
|
||||
boost::optional<std::map<TxSeq, AccountTxDetails>>
|
||||
getAccountTxs(AccountID const& account, Config const& config,
|
||||
ReadView const& view) const;
|
||||
|
||||
/** Packages up fee metrics for the `fee` RPC command.
|
||||
*/
|
||||
Json::Value
|
||||
@@ -215,7 +236,6 @@ private:
|
||||
scaleFeeLevel(OpenView const& view, std::uint32_t txCountPadding = 0) const;
|
||||
};
|
||||
|
||||
// Alternate name: MaybeTx
|
||||
class MaybeTx
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -356,9 +356,11 @@ TxQ::eraseAndAdvance(TxQ::FeeMultiSet::const_iterator_type candidateIter)
|
||||
the latter case, continue through the fee queue anyway
|
||||
to head off potential ordering manipulation problems.
|
||||
*/
|
||||
auto feeNextIter = std::next(candidateIter);
|
||||
bool useAccountNext = accountNextIter != txQAccount.transactions.end() &&
|
||||
accountNextIter->first == candidateIter->sequence + 1 &&
|
||||
accountNextIter->second.feeLevel > candidateIter->feeLevel;
|
||||
(feeNextIter == byFee_.end() ||
|
||||
accountNextIter->second.feeLevel > feeNextIter->feeLevel);
|
||||
auto candidateNextIter = byFee_.erase(candidateIter);
|
||||
txQAccount.transactions.erase(accountIter);
|
||||
return useAccountNext ?
|
||||
@@ -917,7 +919,7 @@ TxQ::apply(Application& app, OpenView& view,
|
||||
|
||||
*/
|
||||
void
|
||||
TxQ::processValidatedLedger(Application& app,
|
||||
TxQ::processClosedLedger(Application& app,
|
||||
OpenView const& view, bool timeLeap)
|
||||
{
|
||||
auto const allowEscalation =
|
||||
@@ -1145,6 +1147,38 @@ TxQ::getMetrics(Config const& config, OpenView const& view,
|
||||
return result;
|
||||
}
|
||||
|
||||
auto
|
||||
TxQ::getAccountTxs(AccountID const& account, Config const& config,
|
||||
ReadView const& view) const
|
||||
-> boost::optional<std::map<TxSeq, AccountTxDetails>>
|
||||
{
|
||||
auto const allowEscalation =
|
||||
(view.rules().enabled(featureFeeEscalation,
|
||||
config.features));
|
||||
if (!allowEscalation)
|
||||
return boost::none;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
auto accountIter = byAccount_.find(account);
|
||||
if (accountIter == byAccount_.end() ||
|
||||
accountIter->second.transactions.empty())
|
||||
return boost::none;
|
||||
|
||||
std::map<TxSeq, AccountTxDetails> result;
|
||||
|
||||
for (auto const& tx : accountIter->second.transactions)
|
||||
{
|
||||
auto& resultTx = result[tx.first];
|
||||
resultTx.feeLevel = tx.second.feeLevel;
|
||||
if(tx.second.lastValid)
|
||||
resultTx.lastValid.emplace(*tx.second.lastValid);
|
||||
if(tx.second.consequences)
|
||||
resultTx.consequences.emplace(*tx.second.consequences);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Json::Value
|
||||
TxQ::doRPC(Application& app) const
|
||||
{
|
||||
|
||||
@@ -403,6 +403,33 @@ public:
|
||||
env(noop(daria), fee(7000), queued);
|
||||
env(noop(edgar), fee(7000), queued);
|
||||
checkMetrics(env, 5, boost::none, 3, 2, 256);
|
||||
{
|
||||
auto& txQ = env.app().getTxQ();
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(),
|
||||
env.app().config(), *env.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
{
|
||||
BEAST_EXPECT(aliceStat->size() == 1);
|
||||
BEAST_EXPECT(aliceStat->begin()->second.feeLevel == 256);
|
||||
BEAST_EXPECT(aliceStat->begin()->second.lastValid &&
|
||||
*aliceStat->begin()->second.lastValid == 8);
|
||||
BEAST_EXPECT(!aliceStat->begin()->second.consequences);
|
||||
}
|
||||
|
||||
auto bobStat = txQ.getAccountTxs(bob.id(),
|
||||
env.app().config(), *env.current());
|
||||
if (BEAST_EXPECT(bobStat))
|
||||
{
|
||||
BEAST_EXPECT(bobStat->size() == 1);
|
||||
BEAST_EXPECT(bobStat->begin()->second.feeLevel = 7000 * 256 / 10);
|
||||
BEAST_EXPECT(!bobStat->begin()->second.lastValid);
|
||||
BEAST_EXPECT(!bobStat->begin()->second.consequences);
|
||||
}
|
||||
|
||||
auto noStat = txQ.getAccountTxs(Account::master.id(),
|
||||
env.app().config(), *env.current());
|
||||
BEAST_EXPECT(!noStat);
|
||||
}
|
||||
|
||||
env.close();
|
||||
checkMetrics(env, 1, 6, 4, 3, 256);
|
||||
@@ -719,6 +746,30 @@ public:
|
||||
++aliceSeq;
|
||||
}
|
||||
checkMetrics(env, 8, 8, 5, 4, 257);
|
||||
{
|
||||
auto& txQ = env.app().getTxQ();
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(),
|
||||
env.app().config(), *env.current());
|
||||
std::int64_t fee = 10;
|
||||
auto seq = env.seq(alice);
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
{
|
||||
BEAST_EXPECT(aliceStat->size() == 7);
|
||||
for (auto const& tx : *aliceStat)
|
||||
{
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == mulDiv(fee, 256, 10).second);
|
||||
BEAST_EXPECT(tx.second.lastValid);
|
||||
BEAST_EXPECT((tx.second.consequences &&
|
||||
tx.second.consequences->fee == drops(fee) &&
|
||||
tx.second.consequences->potentialSpend == drops(0) &&
|
||||
tx.second.consequences->category == TxConsequences::normal) ||
|
||||
tx.first == env.seq(alice) + 6);
|
||||
++seq;
|
||||
fee = (fee + 1) * 125 / 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alice attempts to add another item to the queue,
|
||||
// but you can't force your own earlier txn off the
|
||||
@@ -1557,11 +1608,47 @@ public:
|
||||
!RPC::contains_error(fee[jss::result])))
|
||||
{
|
||||
auto const& result = fee[jss::result];
|
||||
BEAST_EXPECT(result.isMember(jss::drops));
|
||||
BEAST_EXPECT(result.isMember(jss::levels));
|
||||
BEAST_EXPECT(result.isMember(jss::current_ledger_size));
|
||||
BEAST_EXPECT(result.isMember(jss::current_queue_size));
|
||||
BEAST_EXPECT(result.isMember(jss::expected_ledger_size));
|
||||
BEAST_EXPECT(!result.isMember(jss::max_queue_size));
|
||||
BEAST_EXPECT(result.isMember(jss::drops));
|
||||
auto const& drops = result[jss::drops];
|
||||
BEAST_EXPECT(drops.isMember(jss::base_fee));
|
||||
BEAST_EXPECT(drops.isMember(jss::median_fee));
|
||||
BEAST_EXPECT(drops.isMember(jss::minimum_fee));
|
||||
BEAST_EXPECT(drops.isMember(jss::open_ledger_fee));
|
||||
BEAST_EXPECT(result.isMember(jss::levels));
|
||||
auto const& levels = result[jss::levels];
|
||||
BEAST_EXPECT(levels.isMember(jss::median_level));
|
||||
BEAST_EXPECT(levels.isMember(jss::minimum_level));
|
||||
BEAST_EXPECT(levels.isMember(jss::open_ledger_level));
|
||||
BEAST_EXPECT(levels.isMember(jss::reference_level));
|
||||
}
|
||||
|
||||
env.close();
|
||||
|
||||
fee = env.rpc("fee");
|
||||
|
||||
if (BEAST_EXPECT(fee.isMember(jss::result) &&
|
||||
!RPC::contains_error(fee[jss::result])))
|
||||
{
|
||||
auto const& result = fee[jss::result];
|
||||
BEAST_EXPECT(result.isMember(jss::current_ledger_size));
|
||||
BEAST_EXPECT(result.isMember(jss::current_queue_size));
|
||||
BEAST_EXPECT(result.isMember(jss::expected_ledger_size));
|
||||
BEAST_EXPECT(result.isMember(jss::max_queue_size));
|
||||
auto const& drops = result[jss::drops];
|
||||
BEAST_EXPECT(drops.isMember(jss::base_fee));
|
||||
BEAST_EXPECT(drops.isMember(jss::median_fee));
|
||||
BEAST_EXPECT(drops.isMember(jss::minimum_fee));
|
||||
BEAST_EXPECT(drops.isMember(jss::open_ledger_fee));
|
||||
BEAST_EXPECT(result.isMember(jss::levels));
|
||||
auto const& levels = result[jss::levels];
|
||||
BEAST_EXPECT(levels.isMember(jss::median_level));
|
||||
BEAST_EXPECT(levels.isMember(jss::minimum_level));
|
||||
BEAST_EXPECT(levels.isMember(jss::open_ledger_level));
|
||||
BEAST_EXPECT(levels.isMember(jss::reference_level));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1668,6 +1755,422 @@ public:
|
||||
BEAST_EXPECT(env.seq(alice) == aliceSeq + 4);
|
||||
}
|
||||
|
||||
void testSignAndSubmitSequence()
|
||||
{
|
||||
testcase("Autofilled sequence should account for TxQ");
|
||||
using namespace jtx;
|
||||
Env env(*this, makeConfig({ {"minimum_txn_in_ledger_standalone", "6"} }),
|
||||
features(featureFeeEscalation));
|
||||
Env_ss envs(env);
|
||||
auto const& txQ = env.app().getTxQ();
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const bob = Account("bob");
|
||||
env.fund(XRP(100000), alice, bob);
|
||||
|
||||
auto params = Json::Value(Json::objectValue);
|
||||
// Max fee = 50k drops
|
||||
params[jss::fee_mult_max] = 100;
|
||||
params["x_queue_okay"] = true;
|
||||
|
||||
fillQueue(env, alice);
|
||||
checkMetrics(env, 0, boost::none, 7, 6, 256);
|
||||
|
||||
// Queue up several transactions for alice sign-and-submit
|
||||
auto const aliceSeq = env.seq(alice);
|
||||
auto const lastLedgerSeq = env.current()->info().seq + 2;
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
if (i == 2)
|
||||
envs(noop(alice), fee(none), seq(none),
|
||||
json(jss::LastLedgerSequence, lastLedgerSeq),
|
||||
ter(terQUEUED))(params);
|
||||
else
|
||||
envs(noop(alice), fee(none), seq(none),
|
||||
ter(terQUEUED))(params);
|
||||
}
|
||||
checkMetrics(env, 5, boost::none, 7, 6, 256);
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(),
|
||||
env.app().config(), *env.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
{
|
||||
auto seq = aliceSeq;
|
||||
BEAST_EXPECT(aliceStat->size() == 5);
|
||||
for (auto const& tx : *aliceStat)
|
||||
{
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == 25600);
|
||||
if(seq == aliceSeq + 2)
|
||||
{
|
||||
BEAST_EXPECT(tx.second.lastValid &&
|
||||
*tx.second.lastValid == lastLedgerSeq);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!tx.second.lastValid);
|
||||
}
|
||||
++seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Put some txs in the queue for bob.
|
||||
// Give them a higher fee so they'll beat alice's.
|
||||
for (int i = 0; i < 8; ++i)
|
||||
envs(noop(bob), fee(2000), seq(none), ter(terQUEUED))();
|
||||
checkMetrics(env, 13, boost::none, 7, 6, 256);
|
||||
|
||||
env.close();
|
||||
checkMetrics(env, 5, 14, 8, 7, 256);
|
||||
// Put some more txs in the queue for bob.
|
||||
// Give them a higher fee so they'll beat alice's.
|
||||
fillQueue(env, bob);
|
||||
for(int i = 0; i < 9; ++i)
|
||||
envs(noop(bob), fee(2000), seq(none), ter(terQUEUED))();
|
||||
checkMetrics(env, 14, 14, 8, 7, 25601);
|
||||
env.close();
|
||||
// Put some more txs in the queue for bob.
|
||||
// Give them a higher fee so they'll beat alice's.
|
||||
fillQueue(env, bob);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
envs(noop(bob), fee(2000), seq(none), ter(terQUEUED))();
|
||||
checkMetrics(env, 15, 16, 9, 8, 256);
|
||||
env.close();
|
||||
checkMetrics(env, 4, 18, 10, 9, 256);
|
||||
{
|
||||
// Bob has nothing left in the queue.
|
||||
auto bobStat = txQ.getAccountTxs(bob.id(),
|
||||
env.app().config(), *env.current());
|
||||
BEAST_EXPECT(!bobStat);
|
||||
}
|
||||
// Verify alice's tx got dropped as we BEAST_EXPECT, and that there's
|
||||
// a gap in her queued txs.
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(),
|
||||
env.app().config(), *env.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
{
|
||||
auto seq = aliceSeq;
|
||||
BEAST_EXPECT(aliceStat->size() == 4);
|
||||
for (auto const& tx : *aliceStat)
|
||||
{
|
||||
// Skip over the missing one.
|
||||
if (seq == aliceSeq + 2)
|
||||
++seq;
|
||||
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == 25600);
|
||||
BEAST_EXPECT(!tx.second.lastValid);
|
||||
++seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now, fill the gap.
|
||||
envs(noop(alice), fee(none), seq(none), ter(terQUEUED))(params);
|
||||
checkMetrics(env, 5, 18, 10, 9, 256);
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(),
|
||||
env.app().config(), *env.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
{
|
||||
auto seq = aliceSeq;
|
||||
BEAST_EXPECT(aliceStat->size() == 5);
|
||||
for (auto const& tx : *aliceStat)
|
||||
{
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == 25600);
|
||||
BEAST_EXPECT(!tx.second.lastValid);
|
||||
++seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env.close();
|
||||
checkMetrics(env, 0, 20, 5, 10, 256);
|
||||
{
|
||||
// Bob's data has been cleaned up.
|
||||
auto bobStat = txQ.getAccountTxs(bob.id(),
|
||||
env.app().config(), *env.current());
|
||||
BEAST_EXPECT(!bobStat);
|
||||
}
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(),
|
||||
env.app().config(), *env.current());
|
||||
BEAST_EXPECT(!aliceStat);
|
||||
}
|
||||
}
|
||||
|
||||
void testAccountInfo()
|
||||
{
|
||||
using namespace jtx;
|
||||
Env env(*this, makeConfig({ { "minimum_txn_in_ledger_standalone", "3" } }),
|
||||
features(featureFeeEscalation));
|
||||
Env_ss envs(env);
|
||||
|
||||
Account const alice{ "alice" };
|
||||
env.fund(XRP(1000000), alice);
|
||||
env.close();
|
||||
|
||||
auto const withQueue =
|
||||
R"({ "account": ")" + alice.human() +
|
||||
R"(", "queue": true })";
|
||||
auto const withoutQueue =
|
||||
R"({ "account": ")" + alice.human() +
|
||||
R"("})";
|
||||
auto const prevLedgerWithQueue =
|
||||
R"({ "account": ")" + alice.human() +
|
||||
R"(", "queue": true, "ledger_index": 3 })";
|
||||
BEAST_EXPECT(env.current()->info().seq > 3);
|
||||
|
||||
auto submitParams = Json::Value(Json::objectValue);
|
||||
// Max fee = 100 drops
|
||||
submitParams[jss::fee_mult_max] = 10;
|
||||
submitParams["x_queue_okay"] = true;
|
||||
|
||||
{
|
||||
// account_info without the "queue" argument.
|
||||
auto const info = env.rpc("json", "account_info", withoutQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
BEAST_EXPECT(!info[jss::result].isMember(jss::queue_data));
|
||||
}
|
||||
{
|
||||
// account_info with the "queue" argument.
|
||||
auto const info = env.rpc("json", "account_info", withQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& result = info[jss::result];
|
||||
BEAST_EXPECT(result.isMember(jss::queue_data));
|
||||
auto const& queue_data = result[jss::queue_data];
|
||||
BEAST_EXPECT(queue_data.isObject());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::txn_count));
|
||||
BEAST_EXPECT(queue_data[jss::txn_count] == 0);
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::transactions));
|
||||
}
|
||||
checkMetrics(env, 0, 6, 0, 3, 256);
|
||||
|
||||
fillQueue(env, alice);
|
||||
checkMetrics(env, 0, 6, 4, 3, 256);
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", withQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& result = info[jss::result];
|
||||
BEAST_EXPECT(result.isMember(jss::queue_data));
|
||||
auto const& queue_data = result[jss::queue_data];
|
||||
BEAST_EXPECT(queue_data.isObject());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::txn_count));
|
||||
BEAST_EXPECT(queue_data[jss::txn_count] == 0);
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::transactions));
|
||||
}
|
||||
|
||||
envs(noop(alice), fee(none), seq(none), ter(terQUEUED))(submitParams);
|
||||
envs(noop(alice), fee(none), seq(none), ter(terQUEUED))(submitParams);
|
||||
envs(noop(alice), fee(none), seq(none), ter(terQUEUED))(submitParams);
|
||||
envs(noop(alice), fee(none), seq(none), ter(terQUEUED))(submitParams);
|
||||
checkMetrics(env, 4, 6, 4, 3, 256);
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", withQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& result = info[jss::result];
|
||||
auto const& data = result[jss::account_data];
|
||||
BEAST_EXPECT(result.isMember(jss::queue_data));
|
||||
auto const& queue_data = result[jss::queue_data];
|
||||
BEAST_EXPECT(queue_data.isObject());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::txn_count));
|
||||
BEAST_EXPECT(queue_data[jss::txn_count] == 4);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence));
|
||||
BEAST_EXPECT(queue_data[jss::lowest_sequence] == data[jss::Sequence]);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::highest_sequence));
|
||||
BEAST_EXPECT(queue_data[jss::highest_sequence] ==
|
||||
data[jss::Sequence].asUInt() +
|
||||
queue_data[jss::txn_count].asUInt() - 1);
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
|
||||
BEAST_EXPECT(queue_data.isMember(jss::transactions));
|
||||
auto const& queued = queue_data[jss::transactions];
|
||||
BEAST_EXPECT(queued.size() == queue_data[jss::txn_count]);
|
||||
for (unsigned i = 0; i < queued.size(); ++i)
|
||||
{
|
||||
auto const& item = queued[i];
|
||||
BEAST_EXPECT(item[jss::seq] == data[jss::Sequence].asInt() + i);
|
||||
BEAST_EXPECT(item[jss::fee_level] == "2560");
|
||||
BEAST_EXPECT(!item.isMember(jss::LastLedgerSequence));
|
||||
|
||||
if (i == queued.size() - 1)
|
||||
{
|
||||
BEAST_EXPECT(!item.isMember(jss::fee));
|
||||
BEAST_EXPECT(!item.isMember(jss::max_spend_drops));
|
||||
BEAST_EXPECT(!item.isMember(jss::auth_change));
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(item.isMember(jss::fee));
|
||||
BEAST_EXPECT(item[jss::fee] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::max_spend_drops));
|
||||
BEAST_EXPECT(item[jss::max_spend_drops] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::auth_change));
|
||||
BEAST_EXPECT(!item[jss::auth_change].asBool());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Queue up a blocker
|
||||
envs(fset(alice, asfAccountTxnID), fee(none), seq(none),
|
||||
json(jss::LastLedgerSequence, 10),
|
||||
ter(terQUEUED))(submitParams);
|
||||
checkMetrics(env, 5, 6, 4, 3, 256);
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", withQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& result = info[jss::result];
|
||||
auto const& data = result[jss::account_data];
|
||||
BEAST_EXPECT(result.isMember(jss::queue_data));
|
||||
auto const& queue_data = result[jss::queue_data];
|
||||
BEAST_EXPECT(queue_data.isObject());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::txn_count));
|
||||
BEAST_EXPECT(queue_data[jss::txn_count] == 5);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence));
|
||||
BEAST_EXPECT(queue_data[jss::lowest_sequence] == data[jss::Sequence]);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::highest_sequence));
|
||||
BEAST_EXPECT(queue_data[jss::highest_sequence] ==
|
||||
data[jss::Sequence].asUInt() +
|
||||
queue_data[jss::txn_count].asUInt() - 1);
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
|
||||
BEAST_EXPECT(queue_data.isMember(jss::transactions));
|
||||
auto const& queued = queue_data[jss::transactions];
|
||||
BEAST_EXPECT(queued.size() == queue_data[jss::txn_count]);
|
||||
for (unsigned i = 0; i < queued.size(); ++i)
|
||||
{
|
||||
auto const& item = queued[i];
|
||||
BEAST_EXPECT(item[jss::seq] == data[jss::Sequence].asInt() + i);
|
||||
BEAST_EXPECT(item[jss::fee_level] == "2560");
|
||||
|
||||
if (i == queued.size() - 1)
|
||||
{
|
||||
BEAST_EXPECT(!item.isMember(jss::fee));
|
||||
BEAST_EXPECT(!item.isMember(jss::max_spend_drops));
|
||||
BEAST_EXPECT(!item.isMember(jss::auth_change));
|
||||
BEAST_EXPECT(item.isMember(jss::LastLedgerSequence));
|
||||
BEAST_EXPECT(item[jss::LastLedgerSequence] == 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(item.isMember(jss::fee));
|
||||
BEAST_EXPECT(item[jss::fee] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::max_spend_drops));
|
||||
BEAST_EXPECT(item[jss::max_spend_drops] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::auth_change));
|
||||
BEAST_EXPECT(!item[jss::auth_change].asBool());
|
||||
BEAST_EXPECT(!item.isMember(jss::LastLedgerSequence));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
envs(noop(alice), fee(none), seq(none), ter(telCAN_NOT_QUEUE))(submitParams);
|
||||
checkMetrics(env, 5, 6, 4, 3, 256);
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", withQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& result = info[jss::result];
|
||||
auto const& data = result[jss::account_data];
|
||||
BEAST_EXPECT(result.isMember(jss::queue_data));
|
||||
auto const& queue_data = result[jss::queue_data];
|
||||
BEAST_EXPECT(queue_data.isObject());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::txn_count));
|
||||
BEAST_EXPECT(queue_data[jss::txn_count] == 5);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::lowest_sequence));
|
||||
BEAST_EXPECT(queue_data[jss::lowest_sequence] == data[jss::Sequence]);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::highest_sequence));
|
||||
BEAST_EXPECT(queue_data[jss::highest_sequence] ==
|
||||
data[jss::Sequence].asUInt() +
|
||||
queue_data[jss::txn_count].asUInt() - 1);
|
||||
BEAST_EXPECT(queue_data.isMember(jss::auth_change_queued));
|
||||
BEAST_EXPECT(queue_data[jss::auth_change_queued].asBool());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::max_spend_drops_total));
|
||||
BEAST_EXPECT(queue_data[jss::max_spend_drops_total] == "500");
|
||||
BEAST_EXPECT(queue_data.isMember(jss::transactions));
|
||||
auto const& queued = queue_data[jss::transactions];
|
||||
BEAST_EXPECT(queued.size() == queue_data[jss::txn_count]);
|
||||
for (unsigned i = 0; i < queued.size(); ++i)
|
||||
{
|
||||
auto const& item = queued[i];
|
||||
BEAST_EXPECT(item[jss::seq] == data[jss::Sequence].asInt() + i);
|
||||
BEAST_EXPECT(item[jss::fee_level] == "2560");
|
||||
|
||||
if (i == queued.size() - 1)
|
||||
{
|
||||
BEAST_EXPECT(item.isMember(jss::fee));
|
||||
BEAST_EXPECT(item[jss::fee] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::max_spend_drops));
|
||||
BEAST_EXPECT(item[jss::max_spend_drops] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::auth_change));
|
||||
BEAST_EXPECT(item[jss::auth_change].asBool());
|
||||
BEAST_EXPECT(item.isMember(jss::LastLedgerSequence));
|
||||
BEAST_EXPECT(item[jss::LastLedgerSequence] == 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(item.isMember(jss::fee));
|
||||
BEAST_EXPECT(item[jss::fee] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::max_spend_drops));
|
||||
BEAST_EXPECT(item[jss::max_spend_drops] == "100");
|
||||
BEAST_EXPECT(item.isMember(jss::auth_change));
|
||||
BEAST_EXPECT(!item[jss::auth_change].asBool());
|
||||
BEAST_EXPECT(!item.isMember(jss::LastLedgerSequence));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", prevLedgerWithQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
RPC::contains_error(info[jss::result]));
|
||||
}
|
||||
|
||||
env.close();
|
||||
checkMetrics(env, 1, 8, 5, 4, 256);
|
||||
env.close();
|
||||
checkMetrics(env, 0, 10, 1, 5, 256);
|
||||
|
||||
{
|
||||
auto const info = env.rpc("json", "account_info", withQueue);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& result = info[jss::result];
|
||||
BEAST_EXPECT(result.isMember(jss::queue_data));
|
||||
auto const& queue_data = result[jss::queue_data];
|
||||
BEAST_EXPECT(queue_data.isObject());
|
||||
BEAST_EXPECT(queue_data.isMember(jss::txn_count));
|
||||
BEAST_EXPECT(queue_data[jss::txn_count] == 0);
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::lowest_sequence));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::highest_sequence));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::auth_change_queued));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::max_spend_drops_total));
|
||||
BEAST_EXPECT(!queue_data.isMember(jss::transactions));
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
testQueue();
|
||||
@@ -1687,6 +2190,8 @@ public:
|
||||
testConsequences();
|
||||
testRPC();
|
||||
testExpirationReplacement();
|
||||
testSignAndSubmitSequence();
|
||||
testAccountInfo();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ JSS ( amendments ); // in: AccountObjects, out: NetworkOPs
|
||||
JSS ( asks ); // out: Subscribe
|
||||
JSS ( assets ); // out: GatewayBalances
|
||||
JSS ( authorized ); // out: AccountLines
|
||||
JSS ( auth_change ); // out: AccountInfo
|
||||
JSS ( auth_change_queued ); // out: AccountInfo
|
||||
JSS ( balance ); // out: AccountLines
|
||||
JSS ( balances ); // out: GatewayBalances
|
||||
JSS ( base ); // out: LogLevel
|
||||
@@ -162,6 +164,7 @@ JSS ( features ); // out: Feature
|
||||
JSS ( fee ); // out: NetworkOPs, Peers
|
||||
JSS ( fee_base ); // out: NetworkOPs
|
||||
JSS ( fee_div_max ); // in: TransactionSign
|
||||
JSS ( fee_level ); // out: AccountInfo
|
||||
JSS ( fee_mult_max ); // in: TransactionSign
|
||||
JSS ( fee_ref ); // out: NetworkOPs
|
||||
JSS ( fetch_pack ); // out: NetworkOPs
|
||||
@@ -184,6 +187,7 @@ JSS ( hashes ); // in: AccountObjects
|
||||
JSS ( have_header ); // out: InboundLedger
|
||||
JSS ( have_state ); // out: InboundLedger
|
||||
JSS ( have_transactions ); // out: InboundLedger
|
||||
JSS ( highest_sequence ); // out: AccountInfo
|
||||
JSS ( hostid ); // out: NetworkOPs
|
||||
JSS ( hotwallet ); // in: GatewayBalances
|
||||
JSS ( id ); // websocket.
|
||||
@@ -243,6 +247,7 @@ JSS ( load_factor_net ); // out: NetworkOPs
|
||||
JSS ( load_fee ); // out: LoadFeeTrackImp, NetworkOPs
|
||||
JSS ( local ); // out: resource/Logic.h
|
||||
JSS ( local_txs ); // out: GetCounts
|
||||
JSS ( lowest_sequence ); // out: AccountInfo
|
||||
JSS ( majority ); // out: RPC feature
|
||||
JSS ( marker ); // in/out: AccountTx, AccountOffers,
|
||||
// AccountLines, AccountObjects,
|
||||
@@ -253,6 +258,8 @@ JSS ( master_seed ); // out: WalletPropose
|
||||
JSS ( master_seed_hex ); // out: WalletPropose
|
||||
JSS ( max_ledger ); // in/out: LedgerCleaner
|
||||
JSS ( max_queue_size ); // out: TxQ
|
||||
JSS ( max_spend_drops ); // out: AccountInfo
|
||||
JSS ( max_spend_drops_total ); // out: AccountInfo
|
||||
JSS ( median_fee ); // out: TxQ
|
||||
JSS ( median_level ); // out: TxQ
|
||||
JSS ( message ); // error.
|
||||
@@ -317,6 +324,8 @@ JSS ( published_ledger ); // out: NetworkOPs
|
||||
JSS ( quality ); // out: NetworkOPs
|
||||
JSS ( quality_in ); // out: AccountLines
|
||||
JSS ( quality_out ); // out: AccountLines
|
||||
JSS ( queue ); // in: AccountInfo
|
||||
JSS ( queue_data ); // out: AccountInfo
|
||||
JSS ( random ); // out: Random
|
||||
JSS ( raw_meta ); // out: AcceptedLedgerTx
|
||||
JSS ( receive_currencies ); // out: AccountCurrencies
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <BeastConfig.h>
|
||||
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/json/json_value.h>
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
@@ -39,6 +40,11 @@ namespace ripple {
|
||||
// ledger_index : <ledger_index>
|
||||
// signer_lists : <bool> // optional (default false)
|
||||
// // if true return SignerList(s).
|
||||
// queue : <bool> // optional (default false)
|
||||
// // if true return information about transactions
|
||||
// // in the current TxQ, only if the requested
|
||||
// // ledger is open. Otherwise if true, returns an
|
||||
// // error.
|
||||
// }
|
||||
|
||||
// TODO(tom): what is that "default"?
|
||||
@@ -46,17 +52,20 @@ Json::Value doAccountInfo (RPC::Context& context)
|
||||
{
|
||||
auto& params = context.params;
|
||||
|
||||
std::string strIdent;
|
||||
if (params.isMember(jss::account))
|
||||
strIdent = params[jss::account].asString();
|
||||
else if (params.isMember(jss::ident))
|
||||
strIdent = params[jss::ident].asString();
|
||||
else
|
||||
return RPC::missing_field_error (jss::account);
|
||||
|
||||
std::shared_ptr<ReadView const> ledger;
|
||||
auto result = RPC::lookupLedger (ledger, context);
|
||||
|
||||
if (!ledger)
|
||||
return result;
|
||||
|
||||
if (!params.isMember (jss::account) && !params.isMember (jss::ident))
|
||||
return RPC::missing_field_error (jss::account);
|
||||
|
||||
std::string strIdent = params.isMember (jss::account)
|
||||
? params[jss::account].asString () : params[jss::ident].asString ();
|
||||
bool bStrict = params.isMember (jss::strict) && params[jss::strict].asBool ();
|
||||
AccountID accountID;
|
||||
|
||||
@@ -70,6 +79,17 @@ Json::Value doAccountInfo (RPC::Context& context)
|
||||
auto const sleAccepted = ledger->read(keylet::account(accountID));
|
||||
if (sleAccepted)
|
||||
{
|
||||
auto const queue = params.isMember(jss::queue) &&
|
||||
params[jss::queue].asBool();
|
||||
|
||||
if (queue && !ledger->open())
|
||||
{
|
||||
// It doesn't make sense to request the queue
|
||||
// with any closed or validated ledger.
|
||||
RPC::inject_error(rpcINVALID_PARAMS, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
RPC::injectSLE(jvAccepted, *sleAccepted);
|
||||
result[jss::account_data] = jvAccepted;
|
||||
|
||||
@@ -79,14 +99,80 @@ Json::Value doAccountInfo (RPC::Context& context)
|
||||
{
|
||||
// We put the SignerList in an array because of an anticipated
|
||||
// future when we support multiple signer lists on one account.
|
||||
auto& jvSignerList = result[jss::account_data][jss::signer_lists];
|
||||
jvSignerList = Json::arrayValue;
|
||||
Json::Value jvSignerList = Json::arrayValue;
|
||||
|
||||
// This code will need to be revisited if in the future we support
|
||||
// multiple SignerLists on one account.
|
||||
auto const sleSigners = ledger->read (keylet::signers (accountID));
|
||||
if (sleSigners)
|
||||
jvSignerList.append (sleSigners->getJson (0));
|
||||
|
||||
result[jss::account_data][jss::signer_lists] =
|
||||
std::move(jvSignerList);
|
||||
}
|
||||
// Return queue info if that is requested
|
||||
if (queue)
|
||||
{
|
||||
Json::Value jvQueueData = Json::objectValue;
|
||||
|
||||
auto const txs = context.app.getTxQ().getAccountTxs(
|
||||
accountID, context.app.config(), *ledger);
|
||||
if (txs && !txs->empty())
|
||||
{
|
||||
jvQueueData[jss::txn_count] = static_cast<Json::UInt>(txs->size());
|
||||
jvQueueData[jss::lowest_sequence] = txs->begin()->first;
|
||||
jvQueueData[jss::highest_sequence] = txs->rbegin()->first;
|
||||
|
||||
auto& jvQueueTx = jvQueueData[jss::transactions];
|
||||
jvQueueTx = Json::arrayValue;
|
||||
|
||||
boost::optional<bool> anyAuthChanged(false);
|
||||
boost::optional<XRPAmount> totalSpend(0);
|
||||
|
||||
for (auto const& tx : *txs)
|
||||
{
|
||||
Json::Value jvTx = Json::objectValue;
|
||||
|
||||
jvTx[jss::seq] = tx.first;
|
||||
jvTx[jss::fee_level] = to_string(tx.second.feeLevel);
|
||||
if (tx.second.lastValid)
|
||||
jvTx[jss::LastLedgerSequence] = *tx.second.lastValid;
|
||||
if (tx.second.consequences)
|
||||
{
|
||||
jvTx[jss::fee] = to_string(
|
||||
tx.second.consequences->fee);
|
||||
auto spend = tx.second.consequences->potentialSpend +
|
||||
tx.second.consequences->fee;
|
||||
jvTx[jss::max_spend_drops] = to_string(spend);
|
||||
if (totalSpend)
|
||||
*totalSpend += spend;
|
||||
auto authChanged = tx.second.consequences->category ==
|
||||
TxConsequences::blocker;
|
||||
if (authChanged)
|
||||
anyAuthChanged.emplace(authChanged);
|
||||
jvTx[jss::auth_change] = authChanged;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (anyAuthChanged && !*anyAuthChanged)
|
||||
anyAuthChanged.reset();
|
||||
totalSpend.reset();
|
||||
}
|
||||
|
||||
jvQueueTx.append(std::move(jvTx));
|
||||
}
|
||||
|
||||
if (anyAuthChanged)
|
||||
jvQueueData[jss::auth_change_queued] =
|
||||
*anyAuthChanged;
|
||||
if (totalSpend)
|
||||
jvQueueData[jss::max_spend_drops_total] =
|
||||
to_string(*totalSpend);
|
||||
}
|
||||
else
|
||||
jvQueueData[jss::txn_count] = 0u;
|
||||
|
||||
result[jss::queue_data] = std::move(jvQueueData);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -428,7 +428,21 @@ transactionPreProcessImpl (
|
||||
|
||||
return rpcError (rpcSRC_ACT_NOT_FOUND);
|
||||
}
|
||||
tx_json[jss::Sequence] = (*sle)[sfSequence];
|
||||
|
||||
auto seq = (*sle)[sfSequence];
|
||||
auto const queued = app.getTxQ().getAccountTxs(srcAddressID,
|
||||
app.config(), *ledger);
|
||||
// If the account has any txs in the TxQ, skip those sequence
|
||||
// numbers (accounting for possible gaps).
|
||||
if(queued)
|
||||
for(auto const& tx : *queued)
|
||||
{
|
||||
if (tx.first == seq)
|
||||
++seq;
|
||||
else if (tx.first > seq)
|
||||
break;
|
||||
}
|
||||
tx_json[jss::Sequence] = seq;
|
||||
}
|
||||
|
||||
if (!tx_json.isMember (jss::Flags))
|
||||
@@ -680,9 +694,9 @@ Json::Value checkFee (
|
||||
ledger->fees().base, ledger->fees().units, isUnlimited (role));
|
||||
std::uint64_t fee = loadFee;
|
||||
{
|
||||
auto const assumeTx = request.isMember("x-assume-tx") &&
|
||||
request["x-assume-tx"].isConvertibleTo(Json::uintValue) ?
|
||||
request["x-assume-tx"].asUInt() : 0;
|
||||
auto const assumeTx = request.isMember("x_assume_tx") &&
|
||||
request["x_assume_tx"].isConvertibleTo(Json::uintValue) ?
|
||||
request["x_assume_tx"].asUInt() : 0;
|
||||
auto const metrics = txQ.getMetrics(config, *ledger, assumeTx);
|
||||
if(metrics)
|
||||
{
|
||||
@@ -702,9 +716,9 @@ Json::Value checkFee (
|
||||
mult, div);
|
||||
|
||||
if (fee > limit && fee != loadFee &&
|
||||
request.isMember("x-queue-okay") &&
|
||||
request["x-queue-okay"].isBool() &&
|
||||
request["x-queue-okay"].asBool())
|
||||
request.isMember("x_queue_okay") &&
|
||||
request["x_queue_okay"].isBool() &&
|
||||
request["x_queue_okay"].asBool())
|
||||
{
|
||||
fee = std::max(loadFee, limit);
|
||||
}
|
||||
|
||||
@@ -74,12 +74,16 @@ public:
|
||||
{
|
||||
// account_info without the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withoutSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
BEAST_EXPECT(! info[jss::result][jss::account_data].
|
||||
isMember (jss::signer_lists));
|
||||
}
|
||||
{
|
||||
// account_info with the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& data = info[jss::result][jss::account_data];
|
||||
BEAST_EXPECT(data.isMember (jss::signer_lists));
|
||||
auto const& signerLists = data[jss::signer_lists];
|
||||
@@ -95,12 +99,16 @@ public:
|
||||
{
|
||||
// account_info without the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withoutSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
BEAST_EXPECT(! info[jss::result][jss::account_data].
|
||||
isMember (jss::signer_lists));
|
||||
}
|
||||
{
|
||||
// account_info with the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& data = info[jss::result][jss::account_data];
|
||||
BEAST_EXPECT(data.isMember (jss::signer_lists));
|
||||
auto const& signerLists = data[jss::signer_lists];
|
||||
@@ -131,6 +139,8 @@ public:
|
||||
{
|
||||
// account_info with the "signer_lists" argument.
|
||||
auto const info = env.rpc ("json", "account_info", withSigners);
|
||||
BEAST_EXPECT(info.isMember(jss::result) &&
|
||||
info[jss::result].isMember(jss::account_data));
|
||||
auto const& data = info[jss::result][jss::account_data];
|
||||
BEAST_EXPECT(data.isMember (jss::signer_lists));
|
||||
auto const& signerLists = data[jss::signer_lists];
|
||||
|
||||
@@ -1950,7 +1950,7 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader ().parse (R"({
|
||||
"fee_mult_max" : 1000,
|
||||
"x-queue-okay" : false,
|
||||
"x_queue_okay" : false,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -1968,7 +1968,7 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 1000,
|
||||
"x-queue-okay" : true,
|
||||
"x_queue_okay" : true,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -1986,7 +1986,7 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 1000,
|
||||
"x-assume-tx" : 4,
|
||||
"x_assume_tx" : 4,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -2004,8 +2004,8 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 1000,
|
||||
"x-assume-tx" : 4,
|
||||
"x-queue-okay" : true,
|
||||
"x_assume_tx" : 4,
|
||||
"x_queue_okay" : true,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -2041,7 +2041,7 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 5,
|
||||
"x-queue-okay" : true,
|
||||
"x_queue_okay" : true,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -2059,8 +2059,8 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 5,
|
||||
"x-assume-tx" : 4,
|
||||
"x-queue-okay" : false,
|
||||
"x_assume_tx" : 4,
|
||||
"x_queue_okay" : false,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -2077,8 +2077,8 @@ public:
|
||||
Json::Value req;
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 5,
|
||||
"x-assume-tx" : 4,
|
||||
"x-queue-okay" : true,
|
||||
"x_assume_tx" : 4,
|
||||
"x_queue_okay" : true,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
@@ -2097,8 +2097,8 @@ public:
|
||||
Json::Reader().parse(R"({
|
||||
"fee_mult_max" : 1000,
|
||||
"fee_div_max" : 3,
|
||||
"x-assume-tx" : 4,
|
||||
"x-queue-okay" : true,
|
||||
"x_assume_tx" : 4,
|
||||
"x_queue_okay" : true,
|
||||
"tx_json" : { }
|
||||
})", req);
|
||||
Json::Value result =
|
||||
|
||||
Reference in New Issue
Block a user