mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
claim reward tests (#105)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
||||
Copyright (c) 2023 XRPL Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/PublicKey.h>
|
||||
#include <ripple/protocol/Quality.h>
|
||||
#include <ripple/protocol/st.h>
|
||||
|
||||
namespace ripple {
|
||||
@@ -38,13 +37,15 @@ ClaimReward::makeTxConsequences(PreflightContext const& ctx)
|
||||
NotTEC
|
||||
ClaimReward::preflight(PreflightContext const& ctx)
|
||||
{
|
||||
if (!ctx.rules.enabled(featureBalanceRewards))
|
||||
return temDISABLED;
|
||||
|
||||
if (auto const ret = preflight1(ctx); !isTesSuccess(ret))
|
||||
return ret;
|
||||
|
||||
// can have flag 1 set to opt-out of rewards
|
||||
if (ctx.tx.isFieldPresent(sfFlags) &&
|
||||
ctx.tx.getFieldU32(sfFlags) > 1)
|
||||
return temMALFORMED;
|
||||
if (ctx.tx.isFieldPresent(sfFlags) && ctx.tx.getFieldU32(sfFlags) > 1)
|
||||
return temINVALID_FLAG;
|
||||
|
||||
return preflight2(ctx);
|
||||
}
|
||||
@@ -65,7 +66,6 @@ ClaimReward::preclaim(PreclaimContext const& ctx)
|
||||
std::optional<AccountID const> issuer = ctx.tx[~sfIssuer];
|
||||
|
||||
bool isOptOut = flags && *flags == 1;
|
||||
|
||||
if ((issuer && isOptOut) || (!issuer && !isOptOut))
|
||||
return temMALFORMED;
|
||||
|
||||
@@ -81,12 +81,10 @@ ClaimReward::doApply()
|
||||
auto const sle = view().peek(keylet::account(account_));
|
||||
if (!sle)
|
||||
return tefINTERNAL;
|
||||
|
||||
|
||||
std::optional<uint32_t> flags = ctx_.tx[~sfFlags];
|
||||
std::optional<AccountID const> issuer = ctx_.tx[~sfIssuer];
|
||||
|
||||
bool isOptOut = flags && *flags == 1;
|
||||
|
||||
if (isOptOut)
|
||||
{
|
||||
if (sle->isFieldPresent(sfRewardLgrFirst))
|
||||
@@ -106,15 +104,14 @@ ClaimReward::doApply()
|
||||
sle->setFieldU32(sfRewardLgrFirst, lgrCur);
|
||||
sle->setFieldU32(sfRewardLgrLast, lgrCur);
|
||||
sle->setFieldU64(sfRewardAccumulator, 0ULL);
|
||||
sle->setFieldU32(sfRewardTime,
|
||||
std::chrono::duration_cast<std::chrono::seconds>
|
||||
(
|
||||
sle->setFieldU32(
|
||||
sfRewardTime,
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
ctx_.app.getLedgerMaster()
|
||||
.getValidatedLedger()->info()
|
||||
.parentCloseTime
|
||||
.time_since_epoch()
|
||||
).count());
|
||||
|
||||
.getValidatedLedger()
|
||||
->info()
|
||||
.parentCloseTime.time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
|
||||
view().update(sle);
|
||||
|
||||
361
src/test/app/ClaimReward_test.cpp
Normal file
361
src/test/app/ClaimReward_test.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2023 XRPL Labs
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Feature.h>
|
||||
#include <test/jtx.h>
|
||||
|
||||
namespace ripple {
|
||||
namespace test {
|
||||
struct ClaimReward_test : public beast::unit_test::suite
|
||||
{
|
||||
static Json::Value
|
||||
claim(jtx::Account const& account)
|
||||
{
|
||||
using namespace jtx;
|
||||
Json::Value jv;
|
||||
jv[jss::TransactionType] = jss::ClaimReward;
|
||||
jv[jss::Account] = account.human();
|
||||
return jv;
|
||||
}
|
||||
|
||||
std::unique_ptr<Config>
|
||||
makeNetworkConfig(uint32_t networkID)
|
||||
{
|
||||
using namespace jtx;
|
||||
return envconfig([&](std::unique_ptr<Config> cfg) {
|
||||
cfg->NETWORK_ID = networkID;
|
||||
Section config;
|
||||
config.append(
|
||||
{"reference_fee = 10",
|
||||
"account_reserve = 1000000",
|
||||
"owner_reserve = 200000"});
|
||||
auto setup = setup_FeeVote(config);
|
||||
cfg->FEES = setup;
|
||||
return cfg;
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
expectRewards(
|
||||
jtx::Env const& env,
|
||||
jtx::Account const& acct,
|
||||
std::uint32_t ledgerFirst,
|
||||
std::uint32_t ledgerLast,
|
||||
std::uint64_t accumulator,
|
||||
std::uint32_t time)
|
||||
{
|
||||
auto const sle = env.le(keylet::account(acct));
|
||||
if (!sle->isFieldPresent(sfRewardLgrFirst) ||
|
||||
sle->getFieldU32(sfRewardLgrFirst) != ledgerFirst)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!sle->isFieldPresent(sfRewardLgrLast) ||
|
||||
sle->getFieldU32(sfRewardLgrLast) != ledgerLast)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!sle->isFieldPresent(sfRewardAccumulator) ||
|
||||
sle->getFieldU64(sfRewardAccumulator) != accumulator)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!sle->isFieldPresent(sfRewardTime) ||
|
||||
sle->getFieldU32(sfRewardTime) != time)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
expectNoRewards(jtx::Env const& env, jtx::Account const& acct)
|
||||
{
|
||||
auto const sle = env.le(keylet::account(acct));
|
||||
if (sle->isFieldPresent(sfRewardLgrFirst))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (sle->isFieldPresent(sfRewardLgrLast))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (sle->isFieldPresent(sfRewardAccumulator))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (sle->isFieldPresent(sfRewardTime))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
testEnabled(FeatureBitset features)
|
||||
{
|
||||
testcase("enabled");
|
||||
using namespace jtx;
|
||||
using namespace std::literals::chrono_literals;
|
||||
|
||||
// setup env
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
|
||||
for (bool const withClaimReward : {false, true})
|
||||
{
|
||||
// If the BalanceRewards amendment is not enabled, you should not be
|
||||
// able to claim rewards.
|
||||
auto const amend =
|
||||
withClaimReward ? features : features - featureBalanceRewards;
|
||||
Env env{*this, amend};
|
||||
|
||||
env.fund(XRP(1000), alice, issuer);
|
||||
env.close();
|
||||
|
||||
auto const txResult =
|
||||
withClaimReward ? ter(tesSUCCESS) : ter(temDISABLED);
|
||||
|
||||
auto const currentLedger = env.current()->seq();
|
||||
auto const currentTime =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
env.app()
|
||||
.getLedgerMaster()
|
||||
.getValidatedLedger()
|
||||
->info()
|
||||
.parentCloseTime.time_since_epoch())
|
||||
.count();
|
||||
|
||||
// CLAIM
|
||||
auto tx = claim(alice);
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, txResult);
|
||||
env.close();
|
||||
|
||||
if (withClaimReward)
|
||||
{
|
||||
BEAST_EXPECT(
|
||||
expectRewards(
|
||||
env,
|
||||
alice,
|
||||
currentLedger,
|
||||
currentLedger,
|
||||
0,
|
||||
currentTime) == true);
|
||||
}
|
||||
else
|
||||
{
|
||||
BEAST_EXPECT(expectNoRewards(env, alice) == true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidPreflight(FeatureBitset features)
|
||||
{
|
||||
testcase("invalid preflight");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preflight
|
||||
|
||||
// temDISABLED
|
||||
// amendment is disabled
|
||||
{
|
||||
test::jtx::Env env{
|
||||
*this,
|
||||
makeNetworkConfig(21337),
|
||||
features - featureBalanceRewards};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
|
||||
env.fund(XRP(1000), alice, issuer);
|
||||
env.close();
|
||||
|
||||
auto tx = claim(alice);
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, ter(temDISABLED));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// temINVALID_FLAG
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
|
||||
env.fund(XRP(1000), alice, issuer);
|
||||
env.close();
|
||||
|
||||
auto tx = claim(alice);
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, txflags(tfClose), ter(temINVALID_FLAG));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testInvalidPreclaim(FeatureBitset features)
|
||||
{
|
||||
testcase("invalid preclaim");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// preclaim
|
||||
|
||||
// temDISABLED: DA tested in testEnabled() & testInvalidPreflight()
|
||||
// amendment is disabled
|
||||
|
||||
// terNO_ACCOUNT
|
||||
// otxn account does not exist.
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
env.memoize(alice);
|
||||
|
||||
env.fund(XRP(1000), issuer);
|
||||
env.close();
|
||||
|
||||
auto tx = claim(alice);
|
||||
tx[jss::Sequence] = 0;
|
||||
tx[jss::Fee] = 10;
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, ter(terNO_ACCOUNT));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// temMALFORMED
|
||||
// (issuer && isOptOut) || (!issuer && !isOptOut)
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
|
||||
env.fund(XRP(1000), alice, issuer);
|
||||
env.close();
|
||||
|
||||
auto tx = claim(alice);
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, txflags(1), ter(temMALFORMED));
|
||||
env.close();
|
||||
}
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
env(claim(alice), ter(temMALFORMED));
|
||||
env.close();
|
||||
}
|
||||
|
||||
// tecNO_ISSUER
|
||||
// issuer account does not exist.
|
||||
{
|
||||
test::jtx::Env env{*this, makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
env.memoize(issuer);
|
||||
|
||||
env.fund(XRP(1000), alice);
|
||||
env.close();
|
||||
|
||||
auto tx = claim(alice);
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, ter(tecNO_ISSUER));
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
testValidNoHook(FeatureBitset features)
|
||||
{
|
||||
testcase("valid no hook");
|
||||
using namespace test::jtx;
|
||||
using namespace std::literals;
|
||||
|
||||
test::jtx::Env env{*this, makeNetworkConfig(21337)};
|
||||
|
||||
auto const alice = Account("alice");
|
||||
auto const issuer = Account("issuer");
|
||||
|
||||
env.fund(XRP(1000), alice, issuer);
|
||||
env.close();
|
||||
|
||||
// test claim rewards - no opt out
|
||||
auto const currentLedger = env.current()->seq();
|
||||
auto const currentTime =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
env.app()
|
||||
.getLedgerMaster()
|
||||
.getValidatedLedger()
|
||||
->info()
|
||||
.parentCloseTime.time_since_epoch())
|
||||
.count();
|
||||
|
||||
auto tx = claim(alice);
|
||||
tx[sfIssuer.jsonName] = issuer.human();
|
||||
env(tx, ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(
|
||||
expectRewards(
|
||||
env, alice, currentLedger, currentLedger, 0, currentTime) ==
|
||||
true);
|
||||
|
||||
// test claim rewards - opt out
|
||||
env(claim(alice), txflags(1), ter(tesSUCCESS));
|
||||
env.close();
|
||||
|
||||
BEAST_EXPECT(expectNoRewards(env, alice) == true);
|
||||
}
|
||||
|
||||
void
|
||||
testWithFeats(FeatureBitset features)
|
||||
{
|
||||
testEnabled(features);
|
||||
testInvalidPreflight(features);
|
||||
testInvalidPreclaim(features);
|
||||
testValidNoHook(features);
|
||||
}
|
||||
|
||||
public:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
using namespace test::jtx;
|
||||
auto const sa = supported_amendments();
|
||||
testWithFeats(sa);
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ClaimReward, app, ripple);
|
||||
|
||||
} // namespace test
|
||||
} // namespace ripple
|
||||
Reference in New Issue
Block a user