From 79d83bd424db07a51fa3d1ea41a01ecfdf7af99c Mon Sep 17 00:00:00 2001 From: Denis Angell Date: Wed, 11 Sep 2024 05:43:03 +0200 Subject: [PATCH] fix240911 (#363) --- .github/workflows/clang-format.yml | 2 +- .github/workflows/levelization.yml | 2 +- src/ripple/app/tx/impl/Transactor.cpp | 7 +- src/ripple/protocol/Feature.h | 3 +- src/ripple/protocol/impl/Feature.cpp | 1 + src/test/app/XahauGenesis_test.cpp | 553 +++++++++++++++++++++++++- 6 files changed, 563 insertions(+), 5 deletions(-) diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4c2f5a944..0cc8dbe59 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -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 diff --git a/.github/workflows/levelization.yml b/.github/workflows/levelization.yml index 3128513f6..c8284c5fb 100644 --- a/.github/workflows/levelization.yml +++ b/.github/workflows/levelization.yml @@ -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 diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 6c9337d07..7c2734f20 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -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); diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index e91f147ad..54628c9cd 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -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 diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index a13c210cd..27ec16cb7 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -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. diff --git a/src/test/app/XahauGenesis_test.cpp b/src/test/app/XahauGenesis_test.cpp index ce8a3c115..aa546a116 100644 --- a/src/test/app/XahauGenesis_test.cpp +++ b/src/test/app/XahauGenesis_test.cpp @@ -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 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 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 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 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 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 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 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 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 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 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 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 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); } };