mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
TxQ full queue RPC info (RIPD-1404):
* RPC `ledger` command returns all queue entries in "queue_data" when requesting open ledger, and including boolean "queue: true". * Includes queue state. e.g.: fee_level, retries, last_result, tx. * Respects "expand" and "binary" parameters for the txs. * Remove some unused code.
This commit is contained in:
committed by
Scott Schurr
parent
846723d771
commit
7265729446
@@ -447,27 +447,21 @@ public:
|
||||
{
|
||||
auto& txQ = env.app().getTxQ();
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(), *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);
|
||||
}
|
||||
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.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);
|
||||
}
|
||||
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.current());
|
||||
BEAST_EXPECT(!noStat);
|
||||
BEAST_EXPECT(noStat.empty());
|
||||
}
|
||||
|
||||
env.close();
|
||||
@@ -792,21 +786,18 @@ public:
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(), *env.current());
|
||||
std::int64_t fee = 20;
|
||||
auto seq = env.seq(alice);
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
BEAST_EXPECT(aliceStat.size() == 7);
|
||||
for (auto const& tx : 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1823,25 +1814,22 @@ public:
|
||||
checkMetrics(env, 5, boost::none, 7, 6, 256);
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(), *env.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
auto seq = aliceSeq;
|
||||
BEAST_EXPECT(aliceStat.size() == 5);
|
||||
for (auto const& tx : 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.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;
|
||||
BEAST_EXPECT(tx.second.lastValid &&
|
||||
*tx.second.lastValid == lastLedgerSeq);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(!tx.second.lastValid);
|
||||
}
|
||||
++seq;
|
||||
}
|
||||
}
|
||||
// Put some txs in the queue for bob.
|
||||
@@ -1870,27 +1858,24 @@ public:
|
||||
{
|
||||
// Bob has nothing left in the queue.
|
||||
auto bobStat = txQ.getAccountTxs(bob.id(), *env.current());
|
||||
BEAST_EXPECT(!bobStat);
|
||||
BEAST_EXPECT(bobStat.empty());
|
||||
}
|
||||
// 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.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
auto seq = aliceSeq;
|
||||
BEAST_EXPECT(aliceStat.size() == 4);
|
||||
for (auto const& tx : 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);
|
||||
// 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.
|
||||
@@ -1898,17 +1883,14 @@ public:
|
||||
checkMetrics(env, 5, 18, 10, 9, 256);
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(), *env.current());
|
||||
if (BEAST_EXPECT(aliceStat))
|
||||
auto seq = aliceSeq;
|
||||
BEAST_EXPECT(aliceStat.size() == 5);
|
||||
for (auto const& tx : 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;
|
||||
}
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == 25600);
|
||||
BEAST_EXPECT(!tx.second.lastValid);
|
||||
++seq;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1917,11 +1899,11 @@ public:
|
||||
{
|
||||
// Bob's data has been cleaned up.
|
||||
auto bobStat = txQ.getAccountTxs(bob.id(), *env.current());
|
||||
BEAST_EXPECT(!bobStat);
|
||||
BEAST_EXPECT(bobStat.empty());
|
||||
}
|
||||
{
|
||||
auto aliceStat = txQ.getAccountTxs(alice.id(), *env.current());
|
||||
BEAST_EXPECT(!aliceStat);
|
||||
BEAST_EXPECT(aliceStat.empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2723,16 +2705,13 @@ public:
|
||||
checkMetrics(env, 2, 24, 16, 12, 256);
|
||||
auto const aliceQueue = env.app().getTxQ().getAccountTxs(
|
||||
alice.id(), *env.current());
|
||||
if (BEAST_EXPECT(aliceQueue))
|
||||
BEAST_EXPECT(aliceQueue.size() == 2);
|
||||
auto seq = aliceSeq;
|
||||
for (auto const& tx : aliceQueue)
|
||||
{
|
||||
BEAST_EXPECT(aliceQueue->size() == 2);
|
||||
auto seq = aliceSeq;
|
||||
for (auto const& tx : *aliceQueue)
|
||||
{
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == 2560);
|
||||
++seq;
|
||||
}
|
||||
BEAST_EXPECT(tx.first == seq);
|
||||
BEAST_EXPECT(tx.second.feeLevel == 2560);
|
||||
++seq;
|
||||
}
|
||||
|
||||
// Close the ledger to clear the queue
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/app/misc/TxQ.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/JsonFields.h>
|
||||
#include <test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
@@ -110,6 +112,15 @@ class LedgerRPC_test : public beast::unit_test::suite
|
||||
checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound");
|
||||
}
|
||||
|
||||
{
|
||||
// Request queue for closed ledger
|
||||
Json::Value jvParams;
|
||||
jvParams[jss::ledger_index] = "validated";
|
||||
jvParams[jss::queue] = true;
|
||||
auto const jrr = env.rpc ( "json", "ledger", to_string(jvParams) ) [jss::result];
|
||||
checkErrorValue(jrr, "invalidParams", "Invalid parameters.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void testLedgerCurrent()
|
||||
@@ -440,6 +451,207 @@ class LedgerRPC_test : public beast::unit_test::suite
|
||||
|
||||
}
|
||||
|
||||
void testNoQueue()
|
||||
{
|
||||
testcase("Ledger with queueing disabled");
|
||||
using namespace test::jtx;
|
||||
Env env{ *this };
|
||||
|
||||
Json::Value jv;
|
||||
jv[jss::ledger_index] = "current";
|
||||
jv[jss::queue] = true;
|
||||
jv[jss::expand] = true;
|
||||
|
||||
auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(! jrr.isMember(jss::queue_data));
|
||||
}
|
||||
|
||||
void testQueue()
|
||||
{
|
||||
testcase("Ledger with Queued Transactions");
|
||||
using namespace test::jtx;
|
||||
Env env{ *this, []()
|
||||
{
|
||||
auto p = std::make_unique<Config>();
|
||||
test::setupConfigForUnitTests(*p);
|
||||
auto& section = p->section("transaction_queue");
|
||||
section.set("minimum_txn_in_ledger_standalone", "3");
|
||||
return p;
|
||||
}(),
|
||||
features(featureFeeEscalation) };
|
||||
|
||||
Json::Value jv;
|
||||
jv[jss::ledger_index] = "current";
|
||||
jv[jss::queue] = true;
|
||||
jv[jss::expand] = true;
|
||||
|
||||
Account const alice{ "alice" };
|
||||
Account const bob{ "bob" };
|
||||
Account const charlie{ "charlie" };
|
||||
Account const daria{ "daria" };
|
||||
env.fund(XRP(10000), alice);
|
||||
env.fund(XRP(10000), bob);
|
||||
env.close();
|
||||
env.fund(XRP(10000), charlie);
|
||||
env.fund(XRP(10000), daria);
|
||||
env.close();
|
||||
|
||||
auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(! jrr.isMember(jss::queue_data));
|
||||
|
||||
// Fill the open ledger
|
||||
for (;;)
|
||||
{
|
||||
auto metrics = env.app().getTxQ().getMetrics(*env.current());
|
||||
if (! BEAST_EXPECT(metrics))
|
||||
break;
|
||||
if (metrics->expFeeLevel > metrics->minFeeLevel)
|
||||
break;
|
||||
env(noop(alice));
|
||||
}
|
||||
|
||||
BEAST_EXPECT(env.current()->info().seq == 5);
|
||||
// Put some txs in the queue
|
||||
// Alice
|
||||
auto aliceSeq = env.seq(alice);
|
||||
env(pay(alice, "george", XRP(1000)), json(R"({"LastLedgerSequence":7})"),
|
||||
ter(terQUEUED));
|
||||
env(offer(alice, XRP(50000), alice["USD"](5000)), seq(aliceSeq + 1),
|
||||
ter(terQUEUED));
|
||||
env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED));
|
||||
// Bob
|
||||
auto batch = [&env](Account a)
|
||||
{
|
||||
auto aSeq = env.seq(a);
|
||||
// Enough fee to get in front of alice in the queue
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED));
|
||||
}
|
||||
};
|
||||
batch(bob);
|
||||
// Charlie
|
||||
batch(charlie);
|
||||
// Daria
|
||||
batch(daria);
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::queue_data].size() == 33);
|
||||
|
||||
// Close enough ledgers so that alice's first tx expires.
|
||||
env.close();
|
||||
env.close();
|
||||
env.close();
|
||||
BEAST_EXPECT(env.current()->info().seq == 8);
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
BEAST_EXPECT(jrr[jss::queue_data].size() == 11);
|
||||
|
||||
env.close();
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
std::string txid1;
|
||||
std::string txid2;
|
||||
if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
|
||||
{
|
||||
auto const& txj = jrr[jss::queue_data][0u];
|
||||
BEAST_EXPECT(txj[jss::account] == alice.human());
|
||||
BEAST_EXPECT(txj[jss::fee_level] == "256");
|
||||
BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
|
||||
BEAST_EXPECT(txj["retries_remaining"] == 10);
|
||||
BEAST_EXPECT(txj.isMember(jss::tx));
|
||||
auto const& tx = txj[jss::tx];
|
||||
BEAST_EXPECT(tx[jss::Account] == alice.human());
|
||||
BEAST_EXPECT(tx[jss::TransactionType] == "OfferCreate");
|
||||
txid1 = tx[jss::hash].asString();
|
||||
}
|
||||
|
||||
env.close();
|
||||
|
||||
jv[jss::expand] = false;
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
|
||||
{
|
||||
auto const& txj = jrr[jss::queue_data][0u];
|
||||
BEAST_EXPECT(txj[jss::account] == alice.human());
|
||||
BEAST_EXPECT(txj[jss::fee_level] == "256");
|
||||
BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
|
||||
BEAST_EXPECT(txj["retries_remaining"] == 9);
|
||||
BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
|
||||
BEAST_EXPECT(txj.isMember(jss::tx));
|
||||
BEAST_EXPECT(txj[jss::tx] == txid1);
|
||||
}
|
||||
|
||||
env.close();
|
||||
|
||||
jv[jss::expand] = true;
|
||||
jv[jss::binary] = true;
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2))
|
||||
{
|
||||
auto const& txj = jrr[jss::queue_data][0u];
|
||||
BEAST_EXPECT(txj[jss::account] == alice.human());
|
||||
BEAST_EXPECT(txj[jss::fee_level] == "256");
|
||||
BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
|
||||
BEAST_EXPECT(txj["retries_remaining"] == 8);
|
||||
BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
|
||||
BEAST_EXPECT(txj.isMember(jss::tx));
|
||||
BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob));
|
||||
|
||||
auto const& txj2 = jrr[jss::queue_data][1u];
|
||||
BEAST_EXPECT(txj2[jss::account] == alice.human());
|
||||
BEAST_EXPECT(txj2[jss::fee_level] == "256");
|
||||
BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS");
|
||||
BEAST_EXPECT(txj2["retries_remaining"] == 10);
|
||||
BEAST_EXPECT(! txj2.isMember("last_result"));
|
||||
BEAST_EXPECT(txj2.isMember(jss::tx));
|
||||
BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob));
|
||||
}
|
||||
|
||||
for (int i = 0; i != 9; ++i)
|
||||
{
|
||||
env.close();
|
||||
}
|
||||
|
||||
jv[jss::expand] = false;
|
||||
jv[jss::binary] = false;
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
|
||||
{
|
||||
auto const& txj = jrr[jss::queue_data][0u];
|
||||
BEAST_EXPECT(txj[jss::account] == alice.human());
|
||||
BEAST_EXPECT(txj[jss::fee_level] == "256");
|
||||
BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
|
||||
BEAST_EXPECT(txj["retries_remaining"] == 1);
|
||||
BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
|
||||
BEAST_EXPECT(txj.isMember(jss::tx));
|
||||
BEAST_EXPECT(txj[jss::tx] != txid1);
|
||||
txid2 = txj[jss::tx].asString();
|
||||
}
|
||||
|
||||
jv[jss::full] = true;
|
||||
|
||||
jrr = env.rpc("json", "ledger", to_string(jv))[jss::result];
|
||||
if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1))
|
||||
{
|
||||
auto const& txj = jrr[jss::queue_data][0u];
|
||||
BEAST_EXPECT(txj[jss::account] == alice.human());
|
||||
BEAST_EXPECT(txj[jss::fee_level] == "256");
|
||||
BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS");
|
||||
BEAST_EXPECT(txj["retries_remaining"] == 1);
|
||||
BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ");
|
||||
BEAST_EXPECT(txj.isMember(jss::tx));
|
||||
auto const& tx = txj[jss::tx];
|
||||
BEAST_EXPECT(tx[jss::Account] == alice.human());
|
||||
BEAST_EXPECT(tx[jss::TransactionType] == "AccountSet");
|
||||
BEAST_EXPECT(tx[jss::hash] == txid2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
void run ()
|
||||
{
|
||||
@@ -454,6 +666,8 @@ public:
|
||||
testNotFoundAccountRoot();
|
||||
testAccountRootFromIndex();
|
||||
testLookupLedger();
|
||||
testNoQueue();
|
||||
testQueue();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user