add ttCron tests

This commit is contained in:
tequ
2025-10-15 20:45:47 +09:00
parent ac1bf88596
commit e64692fc8b
5 changed files with 187 additions and 9 deletions

View File

@@ -1489,7 +1489,8 @@ TxQ::accept(Application& app, OpenView& view)
std::set<AccountID> cronAccs;
auto counter = 0;
while (++counter < 128 && klStart < klEnd)
// include max 128 cron txns in the ledger
while (++counter < 129 && klStart < klEnd)
{
std::optional<uint256 const> next = view.succ(klStart, klEnd);
if (!next.has_value())

View File

@@ -173,6 +173,12 @@ Cron::doApply()
return tesSUCCESS;
}
void
Cron::preCompute()
{
assert(account_ == beast::zero);
}
XRPAmount
Cron::calculateBaseFee(ReadView const& view, STTx const& tx)
{

View File

@@ -50,6 +50,9 @@ public:
TER
doApply() override;
void
preCompute() override;
};
} // namespace ripple

View File

@@ -185,6 +185,8 @@ invoke_preflight(PreflightContext const& ctx)
return invoke_preflight_helper<URIToken>(ctx);
case ttCRON_SET:
return invoke_preflight_helper<SetCron>(ctx);
case ttCRON:
return invoke_preflight_helper<Cron>(ctx);
default:
assert(false);
return {temUNKNOWN, TxConsequences{temUNKNOWN}};
@@ -312,6 +314,8 @@ invoke_preclaim(PreclaimContext const& ctx)
return invoke_preclaim<URIToken>(ctx);
case ttCRON_SET:
return invoke_preclaim<SetCron>(ctx);
case ttCRON:
return invoke_preclaim<Cron>(ctx);
default:
assert(false);
return temUNKNOWN;
@@ -401,6 +405,8 @@ invoke_calculateBaseFee(ReadView const& view, STTx const& tx)
return URIToken::calculateBaseFee(view, tx);
case ttCRON_SET:
return SetCron::calculateBaseFee(view, tx);
case ttCRON:
return Cron::calculateBaseFee(view, tx);
default:
return XRPAmount{0};
}
@@ -598,6 +604,10 @@ invoke_apply(ApplyContext& ctx)
SetCron p(ctx);
return p();
}
case ttCRON: {
Cron p(ctx);
return p();
}
default:
assert(false);
return {temUNKNOWN, false};

View File

@@ -20,11 +20,6 @@
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/protocol/Feature.h>
#include <ripple/protocol/jss.h>
#include "ripple/protocol/Indexes.h"
#include "ripple/protocol/TER.h"
#include "ripple/protocol/TxFlags.h"
#include "test/jtx/TestHelpers.h"
#include "test/jtx/cron.h"
#include <test/jtx.h>
namespace ripple {
@@ -44,8 +39,6 @@ struct Cron_test : public beast::unit_test::suite
for (bool const withCron : {false, true})
{
// If the BalanceRewards amendment is not enabled, you should not be
// able to claim rewards.
auto const amend = withCron ? features : features - featureCron;
Env env{*this, amend};
@@ -135,7 +128,6 @@ struct Cron_test : public beast::unit_test::suite
// preflight
// temINVALID_FLAG
// can have flag 1 set to opt-out of rewards
{
env(cron::set(alice), txflags(tfClose), ter(temINVALID_FLAG));
env(cron::set(alice),
@@ -252,6 +244,170 @@ struct Cron_test : public beast::unit_test::suite
BEAST_EXPECT(!env.le(cronKey2));
}
void
testCronExecution(FeatureBitset features)
{
testcase("cron execution");
using namespace jtx;
using namespace std::literals::chrono_literals;
auto const alice = Account("alice");
{
// test ttCron execution and repeatCount
Env env{*this, features | featureCron};
env.fund(XRP(1000), alice);
env.close();
auto baseTime = env.timeKeeper().now().time_since_epoch().count();
auto repeatCount = 10;
env(cron::set(alice),
cron::delay(100),
cron::repeat(repeatCount),
fee(XRP(1)));
env.close(10s);
auto lastCronKeylet =
keylet::child(env.le(alice)->getFieldH256(sfCron));
while (repeatCount >= 0)
{
// close ledger until 100 seconds has passed
while (env.timeKeeper().now().time_since_epoch().count() -
baseTime <
100)
{
env.close(10s);
auto txns = env.closed()->txs;
auto size = std::distance(txns.begin(), txns.end());
BEAST_EXPECT(size == 0);
}
// close after 100 seconds passed
env.close();
auto txns = env.closed()->txs;
auto size = std::distance(txns.begin(), txns.end());
BEAST_EXPECT(size == 1);
for (auto it = txns.begin(); it != txns.end(); ++it)
{
auto const& tx = *it->first;
// check pseudo txn format
BEAST_EXPECT(tx.getTxnType() == ttCRON);
BEAST_EXPECT(tx.getAccountID(sfAccount) == AccountID());
BEAST_EXPECT(tx.getAccountID(sfOwner) == alice.id());
BEAST_EXPECT(
tx.getFieldU32(sfLedgerSequence) ==
env.closed()->info().seq);
BEAST_EXPECT(tx.getFieldAmount(sfFee) == XRP(0));
BEAST_EXPECT(tx.getFieldVL(sfSigningPubKey).size() == 0);
// check old Cron object is deleted
BEAST_EXPECT(!env.le(lastCronKeylet));
if (repeatCount > 0)
{
// check new Cron object
auto const cronKeylet =
keylet::child(env.le(alice)->getFieldH256(sfCron));
auto const cronSle = env.le(cronKeylet);
BEAST_EXPECT(cronSle);
BEAST_EXPECT(
cronSle->getFieldU32(sfDelaySeconds) == 100);
BEAST_EXPECT(
cronSle->getFieldU32(sfRepeatCount) ==
--repeatCount);
BEAST_EXPECT(
cronSle->getAccountID(sfOwner) == alice.id());
// set new base time
baseTime =
env.timeKeeper().now().time_since_epoch().count();
lastCronKeylet = cronKeylet;
}
else
{
// after all executions, the cron object should be
// deleted
BEAST_EXPECT(!env.le(alice)->isFieldPresent(sfCron));
BEAST_EXPECT(!env.le(lastCronKeylet));
--repeatCount; // decrement for break double loop
}
}
}
}
{
// test ttCron limit in a ledger
Env env{*this, features | featureCron};
std::vector<Account> accounts;
accounts.reserve(300);
for (int i = 0; i < 300; ++i)
{
auto const& account = accounts.emplace_back(
Account("account_" + std::to_string(i)));
accounts.emplace_back(account);
env.fund(XRP(10000), account);
}
env.close();
for (auto const& account : accounts)
{
env(cron::set(account), cron::delay(0), fee(XRP(1)));
}
env.close();
// proceed ledger
env.close();
{
auto const txns = env.closed()->txs;
auto size = std::distance(txns.begin(), txns.end());
BEAST_EXPECT(size == 128);
for (auto it = txns.begin(); it != txns.end(); ++it)
{
auto const& tx = *it->first;
BEAST_EXPECT(tx.getTxnType() == ttCRON);
}
}
// proceed ledger
env.close();
{
auto const txns = env.closed()->txs;
auto size = std::distance(txns.begin(), txns.end());
BEAST_EXPECT(size == 128);
for (auto it = txns.begin(); it != txns.end(); ++it)
{
auto const& tx = *it->first;
BEAST_EXPECT(tx.getTxnType() == ttCRON);
}
}
// proceed ledger
env.close();
{
auto const txns = env.closed()->txs;
auto size = std::distance(txns.begin(), txns.end());
BEAST_EXPECT(size == 44);
for (auto it = txns.begin(); it != txns.end(); ++it)
{
auto const& tx = *it->first;
BEAST_EXPECT(tx.getTxnType() == ttCRON);
}
}
// proceed ledger
env.close();
{
auto const txns = env.closed()->txs;
auto size = std::distance(txns.begin(), txns.end());
BEAST_EXPECT(size == 0);
}
}
}
void
testWithFeats(FeatureBitset features)
{
@@ -260,6 +416,8 @@ struct Cron_test : public beast::unit_test::suite
testInvalidPreflight(features);
testInvalidPreclaim(features);
testDoApply(features);
testCronExecution(features);
}
public: