Compare commits

..

5 Commits

Author SHA1 Message Date
Denis Angell
74ce9743a7 compiling 2024-09-20 16:49:00 +02:00
Denis Angell
79d83bd424 fix240911 (#363) 2024-09-11 13:43:03 +10:00
Richard Holland
1a4d54f9d9 force build 2024-09-07 15:41:00 +10:00
Wietse Wind
26cd629d28 Merge/2.2.2 jobqueue (#360)
* Merge fbbea9e6e2

* Merge 7741483894

* clang-format

* Oops

---------

Co-authored-by: Wietse Wind <wrw@Wietses-MacBook-Pro.local>
2024-09-07 15:22:15 +10:00
RichardAH
2fb93f874b fixPageCap (#359)
* page cap fix

---------

Co-authored-by: Denis Angell <dangell@transia.co>
2024-09-07 11:39:24 +10:00
18 changed files with 797 additions and 17 deletions

View File

@@ -30,7 +30,7 @@ jobs:
git diff --exit-code | tee "clang-format.patch"
- name: Upload patch
if: failure() && steps.assert.outcome == 'failure'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
continue-on-error: true
with:
name: clang-format.patch

View File

@@ -18,7 +18,7 @@ jobs:
git diff --exit-code | tee "levelization.patch"
- name: Upload patch
if: failure() && steps.assert.outcome == 'failure'
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
continue-on-error: true
with:
name: levelization.patch

View File

@@ -145,6 +145,7 @@ target_link_libraries (xrpl_core
OpenSSL::Crypto
Ripple::boost
NIH::WasmEdge
NIH::MongoCxx
Ripple::syslibs
NIH::secp256k1
NIH::ed25519-donna

View File

@@ -0,0 +1,100 @@
#[===================================================================[
NIH dep: mongo: MongoDB C++ driver (bsoncxx and mongocxx).
#]===================================================================]
include(FetchContent)
FetchContent_Declare(
mongo_c_driver_src
GIT_REPOSITORY https://github.com/mongodb/mongo-c-driver.git
GIT_TAG 1.17.4
)
FetchContent_GetProperties(mongo_c_driver_src)
if(NOT mongo_c_driver_src_POPULATED)
message(STATUS "Pausing to download MongoDB C driver...")
FetchContent_Populate(mongo_c_driver_src)
endif()
set(MONGO_C_DRIVER_BUILD_DIR "${mongo_c_driver_src_BINARY_DIR}")
set(MONGO_C_DRIVER_INCLUDE_DIR "${mongo_c_driver_src_SOURCE_DIR}/src/libbson/src")
set(MONGO_C_DRIVER_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/mongo_c_install")
set(MONGO_C_DRIVER_CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DENABLE_AUTOMATIC_INIT_AND_CLEANUP=OFF
-DENABLE_STATIC=ON
-DENABLE_SHARED=OFF
-DCMAKE_INSTALL_PREFIX=${MONGO_C_DRIVER_INSTALL_PREFIX}
)
ExternalProject_Add(mongo_c_driver
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/mongo_c
SOURCE_DIR ${mongo_c_driver_src_SOURCE_DIR}
CMAKE_ARGS ${MONGO_C_DRIVER_CMAKE_ARGS}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>
INSTALL_COMMAND ${CMAKE_COMMAND} --install .
)
FetchContent_Declare(
mongo_cxx_driver_src
GIT_REPOSITORY https://github.com/mongodb/mongo-cxx-driver.git
GIT_TAG r3.10.2
)
FetchContent_GetProperties(mongo_cxx_driver_src)
if(NOT mongo_cxx_driver_src_POPULATED)
message(STATUS "Pausing to download MongoDB C++ driver...")
FetchContent_Populate(mongo_cxx_driver_src)
endif()
set(MONGO_CXX_DRIVER_BUILD_DIR "${mongo_cxx_driver_src_BINARY_DIR}")
set(MONGO_CXX_DRIVER_INCLUDE_DIR "${mongo_cxx_driver_src_SOURCE_DIR}/include")
set(MONGO_CXX_DRIVER_INSTALL_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/mongo_cxx_install")
set(MONGO_CXX_DRIVER_CMAKE_ARGS
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DBUILD_SHARED_AND_STATIC_LIBS=ON
-DBSONCXX_ENABLE_MONGOC=ON
-DCMAKE_INSTALL_PREFIX=${MONGO_CXX_DRIVER_INSTALL_PREFIX}
-DCMAKE_PREFIX_PATH=${MONGO_C_DRIVER_INSTALL_PREFIX}
)
ExternalProject_Add(mongo_cxx_driver
PREFIX ${CMAKE_CURRENT_BINARY_DIR}/mongo_cxx
SOURCE_DIR ${mongo_cxx_driver_src_SOURCE_DIR}
CMAKE_ARGS ${MONGO_CXX_DRIVER_CMAKE_ARGS}
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config $<CONFIG>
INSTALL_COMMAND ${CMAKE_COMMAND} --install .
DEPENDS mongo_c_driver
)
add_library(bsoncxx STATIC IMPORTED GLOBAL)
add_library(mongocxx STATIC IMPORTED GLOBAL)
add_dependencies(bsoncxx mongo_cxx_driver)
add_dependencies(mongocxx mongo_cxx_driver)
ExternalProject_Get_Property(mongo_cxx_driver BINARY_DIR)
execute_process(
COMMAND
mkdir -p "${BINARY_DIR}/include/bsoncxx/v_noabi"
mkdir -p "${BINARY_DIR}/include/mongocxx/v_noabi"
)
set_target_properties(bsoncxx PROPERTIES
IMPORTED_LOCATION "${MONGO_CXX_DRIVER_INSTALL_PREFIX}/lib/libbsoncxx-static.a"
INTERFACE_INCLUDE_DIRECTORIES "${MONGO_CXX_DRIVER_INSTALL_PREFIX}/include/bsoncxx/v_noabi"
)
set_target_properties(mongocxx PROPERTIES
IMPORTED_LOCATION "${MONGO_CXX_DRIVER_INSTALL_PREFIX}/lib/libmongocxx-static.a"
INTERFACE_INCLUDE_DIRECTORIES "${MONGO_CXX_DRIVER_INSTALL_PREFIX}/include/mongocxx/v_noabi"
)
# Link the C driver libraries
find_library(BSON_LIB bson-1.0 PATHS ${MONGO_C_DRIVER_INSTALL_PREFIX}/lib)
find_library(MONGOC_LIB mongoc-1.0 PATHS ${MONGO_C_DRIVER_INSTALL_PREFIX}/lib)
target_link_libraries(ripple_libs INTERFACE bsoncxx mongocxx ${BSON_LIB} ${MONGOC_LIB})
add_library(NIH::MongoCxx ALIAS mongocxx)

View File

@@ -75,6 +75,7 @@ include(deps/gRPC)
include(deps/cassandra)
include(deps/Postgres)
include(deps/WasmEdge)
include(deps/Mongo)
###

View File

@@ -1,4 +1,4 @@
# Xahau
# Xahau
**Note:** Throughout this README, references to "we" or "our" pertain to the community and contributors involved in the Xahau network. It does not imply a legal entity or a specific collection of individuals.
@@ -68,4 +68,4 @@ git-subtree. See those directories' README files for more details.
- **Testnet & Faucet**: Test applications and obtain test XAH at [xahau-test.net](https://xahau-test.net) and use the testnet explorer at [explorer.xahau.network](https://explorer.xahau.network).
- **Supporting Wallets**: A list of wallets that support XAH and Xahau-based assets.
- [Xumm](https://xumm.app)
- [Crossmark](https://crossmark.io)
- [Crossmark](https://crossmark.io)

View File

@@ -134,8 +134,12 @@ RCLConsensus::Adaptor::acquireLedger(LedgerHash const& hash)
acquiringLedger_ = hash;
app_.getJobQueue().addJob(
jtADVANCE, "getConsensusLedger", [id = hash, &app = app_]() {
app.getInboundLedgers().acquire(
jtADVANCE,
"getConsensusLedger1",
[id = hash, &app = app_, this]() {
JLOG(j_.debug())
<< "JOB advanceLedger getConsensusLedger1 started";
app.getInboundLedgers().acquireAsync(
id, 0, InboundLedger::Reason::CONSENSUS);
});
}

View File

@@ -135,8 +135,10 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash)
Application* pApp = &app_;
app_.getJobQueue().addJob(
jtADVANCE, "getConsensusLedger", [pApp, hash]() {
pApp->getInboundLedgers().acquire(
jtADVANCE, "getConsensusLedger2", [pApp, hash, this]() {
JLOG(j_.debug())
<< "JOB advanceLedger getConsensusLedger2 started";
pApp->getInboundLedgers().acquireAsync(
hash, 0, InboundLedger::Reason::CONSENSUS);
});
return std::nullopt;
@@ -152,7 +154,9 @@ void
handleNewValidation(
Application& app,
std::shared_ptr<STValidation> const& val,
std::string const& source)
std::string const& source,
BypassAccept const bypassAccept,
std::optional<beast::Journal> j)
{
auto const& signingKey = val->getSignerPublic();
auto const& hash = val->getLedgerHash();
@@ -177,7 +181,23 @@ handleNewValidation(
if (outcome == ValStatus::current)
{
if (val->isTrusted())
app.getLedgerMaster().checkAccept(hash, seq);
{
// Was: app.getLedgerMaster().checkAccept(hash, seq);
// https://github.com/XRPLF/rippled/commit/fbbea9e6e25795a8a6bd1bf64b780771933a9579
if (bypassAccept == BypassAccept::yes)
{
assert(j.has_value());
if (j.has_value())
{
JLOG(j->trace()) << "Bypassing checkAccept for validation "
<< val->getLedgerHash();
}
}
else
{
app.getLedgerMaster().checkAccept(hash, seq);
}
}
return;
}

View File

@@ -25,12 +25,16 @@
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/RippleLedgerHash.h>
#include <ripple/protocol/STValidation.h>
#include <optional>
#include <set>
#include <vector>
namespace ripple {
class Application;
enum class BypassAccept : bool { no = false, yes };
/** Wrapper over STValidation for generic Validation code
Wraps an STValidation for compatibility with the generic validation code.
@@ -248,7 +252,9 @@ void
handleNewValidation(
Application& app,
std::shared_ptr<STValidation> const& val,
std::string const& source);
std::string const& source,
BypassAccept const bypassAccept = BypassAccept::no,
std::optional<beast::Journal> j = std::nullopt);
} // namespace ripple

View File

@@ -38,10 +38,21 @@ public:
virtual ~InboundLedgers() = default;
// VFALCO TODO Should this be called findOrAdd ?
// Callers should use this if they possibly need an authoritative
// response immediately.
//
virtual std::shared_ptr<Ledger const>
acquire(uint256 const& hash, std::uint32_t seq, InboundLedger::Reason) = 0;
// Callers should use this if they are known to be executing on the Job
// Queue. TODO review whether all callers of acquire() can use this
// instead. Inbound ledger acquisition is asynchronous anyway.
virtual void
acquireAsync(
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) = 0;
virtual std::shared_ptr<InboundLedger>
find(LedgerHash const& hash) = 0;

View File

@@ -560,7 +560,7 @@ InboundLedger::trigger(std::shared_ptr<Peer> const& peer, TriggerReason reason)
return;
}
if (auto stream = journal_.trace())
if (auto stream = journal_.debug())
{
if (peer)
stream << "Trigger acquiring ledger " << hash_ << " from " << peer;

View File

@@ -28,6 +28,7 @@
#include <ripple/core/JobQueue.h>
#include <ripple/nodestore/DatabaseShard.h>
#include <ripple/protocol/jss.h>
#include <exception>
#include <memory>
#include <mutex>
#include <vector>
@@ -141,6 +142,37 @@ public:
return inbound->getLedger();
}
void
acquireAsync(
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) override
{
std::unique_lock lock(acquiresMutex_);
try
{
if (pendingAcquires_.contains(hash))
return;
pendingAcquires_.insert(hash);
lock.unlock();
acquire(hash, seq, reason);
}
catch (std::exception const& e)
{
JLOG(j_.warn())
<< "Exception thrown for acquiring new inbound ledger " << hash
<< ": " << e.what();
}
catch (...)
{
JLOG(j_.warn())
<< "Unknown exception thrown for acquiring new inbound ledger "
<< hash;
}
lock.lock();
pendingAcquires_.erase(hash);
}
std::shared_ptr<InboundLedger>
find(uint256 const& hash) override
{
@@ -426,6 +458,9 @@ private:
beast::insight::Counter mCounter;
std::unique_ptr<PeerSetBuilder> mPeerSetBuilder;
std::set<uint256> pendingAcquires_;
std::mutex acquiresMutex_;
};
//------------------------------------------------------------------------------

View File

@@ -70,7 +70,9 @@
#include <boost/asio/ip/host_name.hpp>
#include <boost/asio/steady_timer.hpp>
#include <exception>
#include <mutex>
#include <set>
#include <string>
#include <tuple>
#include <unordered_map>
@@ -776,6 +778,9 @@ private:
StateAccounting accounting_{};
std::set<uint256> pendingValidations_;
std::mutex validationsMutex_;
private:
struct Stats
{
@@ -1791,7 +1796,8 @@ NetworkOPsImp::checkLastClosedLedger(
}
JLOG(m_journal.warn()) << "We are not running on the consensus ledger";
JLOG(m_journal.info()) << "Our LCL: " << getJson({*ourClosed, {}});
JLOG(m_journal.info()) << "Our LCL: " << ourClosed->info().hash
<< getJson({*ourClosed, {}});
JLOG(m_journal.info()) << "Net LCL " << closedLedger;
if ((mMode == OperatingMode::TRACKING) || (mMode == OperatingMode::FULL))
@@ -2345,7 +2351,37 @@ NetworkOPsImp::recvValidation(
JLOG(m_journal.trace())
<< "recvValidation " << val->getLedgerHash() << " from " << source;
handleNewValidation(app_, val, source);
// handleNewValidation(app_, val, source);
// https://github.com/XRPLF/rippled/commit/fbbea9e6e25795a8a6bd1bf64b780771933a9579
std::unique_lock lock(validationsMutex_);
BypassAccept bypassAccept = BypassAccept::no;
try
{
if (pendingValidations_.contains(val->getLedgerHash()))
bypassAccept = BypassAccept::yes;
else
pendingValidations_.insert(val->getLedgerHash());
lock.unlock();
handleNewValidation(app_, val, source, bypassAccept, m_journal);
}
catch (std::exception const& e)
{
JLOG(m_journal.warn())
<< "Exception thrown for handling new validation "
<< val->getLedgerHash() << ": " << e.what();
}
catch (...)
{
JLOG(m_journal.warn())
<< "Unknown exception thrown for handling new validation "
<< val->getLedgerHash();
}
if (bypassAccept == BypassAccept::no)
{
lock.lock();
pendingValidations_.erase(val->getLedgerHash());
lock.unlock();
}
pubValidation(val);

View File

@@ -1923,6 +1923,7 @@ Transactor::operator()()
uint32_t lgrCur = view().seq();
bool const has240819 = view().rules().enabled(fix240819);
bool const has240911 = view().rules().enabled(fix240911);
auto const& sfRewardFields =
*(ripple::SField::knownCodeToField.at(917511 - has240819));
@@ -1971,7 +1972,11 @@ Transactor::operator()()
uint32_t lgrElapsed = lgrCur - lgrLast;
// overflow safety
if (lgrElapsed > lgrCur || lgrElapsed > lgrLast || lgrElapsed == 0)
if (!has240911 &&
(lgrElapsed > lgrCur || lgrElapsed > lgrLast ||
lgrElapsed == 0))
continue;
if (has240911 && (lgrElapsed > lgrCur || lgrElapsed == 0))
continue;
uint64_t accum = sle->getFieldU64(sfRewardAccumulator);

View File

@@ -74,7 +74,7 @@ namespace detail {
// Feature.cpp. Because it's only used to reserve storage, and determine how
// large to make the FeatureBitset, it MAY be larger. It MUST NOT be less than
// the actual number of amendments. A LogicError on startup will verify this.
static constexpr std::size_t numFeatures = 72;
static constexpr std::size_t numFeatures = 73;
/** Amendments that this server supports and the default voting behavior.
Whether they are enabled depends on the Rules defined in the validated
@@ -360,6 +360,7 @@ extern uint256 const featureZeroB2M;
extern uint256 const fixNSDelete;
extern uint256 const fix240819;
extern uint256 const fixPageCap;
extern uint256 const fix240911;
} // namespace ripple

View File

@@ -466,6 +466,7 @@ REGISTER_FEATURE(ZeroB2M, Supported::yes, VoteBehavior::De
REGISTER_FIX (fixNSDelete, Supported::yes, VoteBehavior::DefaultNo);
REGISTER_FIX (fix240819, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fixPageCap, Supported::yes, VoteBehavior::DefaultYes);
REGISTER_FIX (fix240911, Supported::yes, VoteBehavior::DefaultYes);
// The following amendments are obsolete, but must remain supported
// because they could potentially get enabled.

View File

@@ -106,6 +106,14 @@ public:
return {};
}
virtual void
acquireAsync(
uint256 const& hash,
std::uint32_t seq,
InboundLedger::Reason reason) override
{
}
virtual std::shared_ptr<InboundLedger>
find(LedgerHash const& hash) override
{

View File

@@ -5020,6 +5020,550 @@ struct XahauGenesis_test : public beast::unit_test::suite
BEAST_EXPECT(asPercent == 4);
}
void
testDeposit(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test deposit");
Env env{*this, envconfig(), features - featureXahauGenesis};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
auto const user = Account("user");
env.fund(XRP(1000), user);
env.close();
// setup governance
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const david = Account("david");
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// verify unl report does not exist
BEAST_EXPECT(hasUNLReport(env) == false);
// opt in claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
env(pay(alice, user, XRP(1000)));
env.close();
// close ledgers
for (int i = 0; i < 10; ++i)
{
env.close(10s);
}
// close claim ledger & time
STAmount const preUser = env.balance(user);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] = accountKeyAndSle(*env.current(), user);
// claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// trigger emitted txn
env.close();
// calculate rewards
bool const has240819 = env.current()->rules().enabled(fix240819);
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
BEAST_EXPECT(netReward == (has240819 ? XRP(6.383333) : XRP(6.663333)));
// validate account fields
STAmount const postUser = preUser + netReward;
BEAST_EXPECT(expectAccountFields(
env,
user,
preLedger,
preLedger + 1,
has240819 ? (preUser - feesXRP) : postUser,
preTime));
BEAST_EXPECT(
postUser == (has240819 ? XRP(2005.383333) : XRP(2005.663333)));
}
void
testDepositWithdraw(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test deposit withdraw");
Env env{*this, envconfig(), features - featureXahauGenesis};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
auto const user = Account("user");
env.fund(XRP(1000), user);
env.close();
// setup governance
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const david = Account("david");
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// verify unl report does not exist
BEAST_EXPECT(hasUNLReport(env) == false);
// opt in claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
env(pay(alice, user, XRP(1000)));
env.close();
env(pay(user, alice, XRP(1000)));
env.close();
// close ledgers
for (int i = 0; i < 10; ++i)
{
env.close(10s);
}
// close claim ledger & time
STAmount const preUser = env.balance(user);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] = accountKeyAndSle(*env.current(), user);
// claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// trigger emitted txn
env.close();
// calculate rewards
bool const has240819 = env.current()->rules().enabled(fix240819);
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
BEAST_EXPECT(netReward == XRP(3.583333));
// validate account fields
STAmount const postUser = preUser + netReward;
BEAST_EXPECT(expectAccountFields(
env,
user,
preLedger,
preLedger + 1,
has240819 ? (preUser - feesXRP) : postUser,
preTime));
BEAST_EXPECT(postUser == XRP(1002.583323));
}
void
testDepositLate(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test deposit late");
Env env{*this, envconfig(), features - featureXahauGenesis};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
auto const user = Account("user");
env.fund(XRP(1000), user);
env.close();
// setup governance
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const david = Account("david");
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// verify unl report does not exist
BEAST_EXPECT(hasUNLReport(env) == false);
// opt in claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// close ledgers
for (int i = 0; i < 10; ++i)
{
env.close(10s);
}
env(pay(alice, user, XRP(1000)));
env.close();
// close claim ledger & time
STAmount const preUser = env.balance(user);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] = accountKeyAndSle(*env.current(), user);
// claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// trigger emitted txn
env.close();
// calculate rewards
bool const has240819 = env.current()->rules().enabled(fix240819);
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
BEAST_EXPECT(netReward == (has240819 ? XRP(3.606666) : XRP(6.663333)));
// validate account fields
STAmount const postUser = preUser + netReward;
BEAST_EXPECT(expectAccountFields(
env,
user,
preLedger,
preLedger + 1,
has240819 ? (preUser - feesXRP) : postUser,
preTime));
BEAST_EXPECT(
postUser == (has240819 ? XRP(2002.606666) : XRP(2005.663333)));
}
void
testDepositWithdrawLate(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test deposit late withdraw");
Env env{*this, envconfig(), features - featureXahauGenesis};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
auto const user = Account("user");
env.fund(XRP(1000), user);
env.close();
// setup governance
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const david = Account("david");
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// verify unl report does not exist
BEAST_EXPECT(hasUNLReport(env) == false);
// opt in claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// close ledgers
for (int i = 0; i < 10; ++i)
{
env.close(10s);
}
env(pay(alice, user, XRP(1000)));
env.close();
env(pay(user, alice, XRP(1000)));
env.close();
// close claim ledger & time
STAmount const preUser = env.balance(user);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] = accountKeyAndSle(*env.current(), user);
// claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// trigger emitted txn
env.close();
// calculate rewards
bool const has240819 = env.current()->rules().enabled(fix240819);
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
BEAST_EXPECT(netReward == (has240819 ? XRP(3.583333) : XRP(6.149999)));
// validate account fields
STAmount const postUser = preUser + netReward;
BEAST_EXPECT(expectAccountFields(
env,
user,
preLedger,
preLedger + 1,
has240819 ? (preUser - feesXRP) : postUser,
preTime));
BEAST_EXPECT(
postUser == (has240819 ? XRP(1002.583323) : XRP(1005.149989)));
}
void
testNoClaim(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test no claim");
Env env{*this, envconfig(), features - featureXahauGenesis};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
auto const user = Account("user");
env.fund(XRP(1000), user);
env.close();
// setup governance
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const david = Account("david");
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// verify unl report does not exist
BEAST_EXPECT(hasUNLReport(env) == false);
// opt in claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// close ledgers (2 cycles)
for (int i = 0; i < 20; ++i)
{
env.close(10s);
}
// close claim ledger & time
STAmount const preUser = env.balance(user);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] = accountKeyAndSle(*env.current(), user);
// claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// trigger emitted txn
env.close();
// calculate rewards
bool const hasFix = env.current()->rules().enabled(fix240819) &&
env.current()->rules().enabled(fix240911);
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
BEAST_EXPECT(netReward == (hasFix ? XRP(3.329999) : XRP(3.329999)));
// validate account fields
STAmount const postUser = preUser + netReward;
BEAST_EXPECT(expectAccountFields(
env,
user,
preLedger,
preLedger + 1,
hasFix ? (preUser - feesXRP) : postUser,
preTime));
BEAST_EXPECT(
postUser == (hasFix ? XRP(1002.329999) : XRP(1002.329999)));
}
void
testNoClaimLate(FeatureBitset features)
{
using namespace jtx;
using namespace std::chrono_literals;
testcase("test no claim late");
Env env{*this, envconfig(), features - featureXahauGenesis};
double const rateDrops = 0.00333333333 * 1'000'000;
STAmount const feesXRP = XRP(1);
auto const user = Account("user");
env.fund(XRP(1000), user);
env.close();
// setup governance
auto const alice = Account("alice");
auto const bob = Account("bob");
auto const carol = Account("carol");
auto const david = Account("david");
auto const edward = Account("edward");
env.fund(XRP(10000), alice, bob, carol, david, edward);
env.close();
std::vector<AccountID> initial_members_ids{
alice.id(), bob.id(), carol.id(), david.id(), edward.id()};
setupGov(env, initial_members_ids);
// update reward delay
{
// this will be the new reward delay
// 100
std::vector<uint8_t> vote_data{
0x00U, 0x80U, 0xC6U, 0xA4U, 0x7EU, 0x8DU, 0x03U, 0x55U};
updateTopic(
env, alice, bob, carol, david, edward, 'R', 'D', vote_data);
}
// verify unl report does not exist
BEAST_EXPECT(hasUNLReport(env) == false);
// opt in claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// close ledgers (2 cycles)
for (int i = 0; i < 20; ++i)
{
env.close(10s);
}
env(pay(alice, user, XRP(1000)));
env.close();
// close claim ledger & time
STAmount const preUser = env.balance(user);
NetClock::time_point const preTime = lastClose(env);
std::uint32_t const preLedger = env.current()->seq();
auto const [acct, acctSle] = accountKeyAndSle(*env.current(), user);
// claim reward
env(claimReward(user, env.master), fee(feesXRP), ter(tesSUCCESS));
env.close();
// trigger emitted txn
env.close();
// calculate rewards
bool const hasFix = env.current()->rules().enabled(fix240819) &&
env.current()->rules().enabled(fix240911);
STAmount const netReward =
rewardUserAmount(*acctSle, preLedger, rateDrops);
BEAST_EXPECT(netReward == (hasFix ? XRP(3.479999) : XRP(6.663333)));
// validate account fields
STAmount const postUser = preUser + netReward;
BEAST_EXPECT(expectAccountFields(
env,
user,
preLedger,
preLedger + 1,
hasFix ? (preUser - feesXRP) : postUser,
preTime));
BEAST_EXPECT(
postUser == (hasFix ? XRP(2002.479999) : XRP(2005.663333)));
}
void
testRewardHookWithFeats(FeatureBitset features)
{
@@ -5038,6 +5582,12 @@ struct XahauGenesis_test : public beast::unit_test::suite
testInvalidElapsed0(features);
testInvalidElapsedNegative(features);
testCompoundInterest(features);
testDeposit(features);
testDepositWithdraw(features);
testDepositLate(features);
testDepositWithdrawLate(features);
testNoClaim(features);
testNoClaimLate(features);
}
void
@@ -5056,8 +5606,9 @@ struct XahauGenesis_test : public beast::unit_test::suite
using namespace test::jtx;
auto const sa = supported_amendments();
testGovernHookWithFeats(sa);
testRewardHookWithFeats(sa - fix240819);
testRewardHookWithFeats(sa);
testRewardHookWithFeats(sa - fix240819);
testRewardHookWithFeats(sa - fix240819 - fix240911);
}
};