From e11f6190b74599737f9f554da3b141f269e43803 Mon Sep 17 00:00:00 2001 From: Olek <115580134+oleks-rip@users.noreply.github.com> Date: Tue, 10 Feb 2026 14:02:53 -0500 Subject: [PATCH 1/2] fix: Update invariant checks for Permissioned Domains (#6134) --- include/xrpl/protocol/detail/features.macro | 2 +- src/test/app/Invariants_test.cpp | 636 ++++++++++++++------ src/test/app/PermissionedDomains_test.cpp | 32 +- src/test/jtx/impl/balance.cpp | 24 +- src/xrpld/app/tx/detail/InvariantCheck.cpp | 102 +++- src/xrpld/app/tx/detail/InvariantCheck.h | 6 +- 6 files changed, 555 insertions(+), 247 deletions(-) diff --git a/include/xrpl/protocol/detail/features.macro b/include/xrpl/protocol/detail/features.macro index d8498ffa2f..961bc6e44c 100644 --- a/include/xrpl/protocol/detail/features.macro +++ b/include/xrpl/protocol/detail/features.macro @@ -15,7 +15,7 @@ // Add new amendments to the top of this list. // Keep it sorted in reverse chronological order. - +XRPL_FIX (PermissionedDomainInvariant, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (ExpiredNFTokenOfferRemoval, Supported::yes, VoteBehavior::DefaultNo) XRPL_FIX (BatchInnerSigs, Supported::yes, VoteBehavior::DefaultNo) XRPL_FEATURE(LendingProtocol, Supported::yes, VoteBehavior::DefaultNo) diff --git a/src/test/app/Invariants_test.cpp b/src/test/app/Invariants_test.cpp index b3fa027753..7558c951b0 100644 --- a/src/test/app/Invariants_test.cpp +++ b/src/test/app/Invariants_test.cpp @@ -36,6 +36,12 @@ class Invariants_test : public beast::unit_test::suite // changes that will cause the check to fail. using Precheck = std::function; + static FeatureBitset + defaultAmendments() + { + return xrpl::test::jtx::testable_amendments() | featureInvariantsV1_1 | featureSingleAssetVault; + } + /** Run a specific test case to put the ledger into a state that will be * detected by an invariant. Simulates the actions of a transaction that * would violate an invariant. @@ -62,10 +68,23 @@ class Invariants_test : public beast::unit_test::suite std::initializer_list ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, Preclose const& preclose = {}, TxAccount setTxAccount = TxAccount::None) + { + return doInvariantCheck( + test::jtx::Env(*this, defaultAmendments()), expect_logs, precheck, fee, tx, ters, preclose, setTxAccount); + } + + void + doInvariantCheck( + test::jtx::Env&& env, + std::vector const& expect_logs, + Precheck const& precheck, + XRPAmount fee = XRPAmount{}, + STTx tx = STTx{ttACCOUNT_SET, [](STObject&) {}}, + std::initializer_list ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}, + Preclose const& preclose = {}, + TxAccount setTxAccount = TxAccount::None) { using namespace test::jtx; - FeatureBitset amendments = testable_amendments() | featureInvariantsV1_1 | featureSingleAssetVault; - Env env{*this, amendments}; Account const A1{"A1"}; Account const A2{"A2"}; @@ -74,11 +93,28 @@ class Invariants_test : public beast::unit_test::suite BEAST_EXPECT(preclose(A1, A2, env)); env.close(); + if (setTxAccount != TxAccount::None) + tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? A1.id() : A2.id()); + + return doInvariantCheck(std::move(env), A1, A2, expect_logs, precheck, fee, tx, ters); + } + + void + doInvariantCheck( + test::jtx::Env&& env, + test::jtx::Account const& A1, + test::jtx::Account const& A2, + std::vector const& expect_logs, + Precheck const& precheck, + XRPAmount fee = XRPAmount{}, + STTx tx = STTx{ttACCOUNT_SET, [](STObject&) {}}, + std::initializer_list ters = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}) + { + using namespace test::jtx; + OpenView ov{*env.current()}; test::StreamSink sink{beast::severities::kWarning}; beast::Journal jlog{sink}; - if (setTxAccount != TxAccount::None) - tx.setAccountID(sfAccount, setTxAccount == TxAccount::A1 ? A1.id() : A2.id()); ApplyContext ac{env.app(), ov, tx, tesSUCCESS, env.current()->fees().base, tapNONE, jlog}; BEAST_EXPECT(precheck(A1, A2, ac)); @@ -91,19 +127,21 @@ class Invariants_test : public beast::unit_test::suite for (TER const& terExpect : ters) { terActual = ac.checkInvariants(terActual, fee); - BEAST_EXPECT(terExpect == terActual); + BEAST_EXPECTS(terExpect == terActual, std::to_string(TERtoInt(terActual))); auto const messages = sink.messages().str(); - BEAST_EXPECT( - messages.starts_with("Invariant failed:") || messages.starts_with("Transaction caused an exception")); + + if (terActual != tesSUCCESS) + { + BEAST_EXPECTS( + messages.starts_with("Invariant failed:") || + messages.starts_with("Transaction caused an exception"), + messages); + } + // std::cerr << messages << '\n'; for (auto const& m : expect_logs) { - if (messages.find(m) == std::string::npos) - { - // uncomment if you want to log the invariant failure - // std::cerr << " --> " << m << std::endl; - fail(); - } + BEAST_EXPECTS(messages.find(m) != std::string::npos, m); } } } @@ -1119,86 +1157,80 @@ class Invariants_test : public beast::unit_test::suite }); } - void + static std::shared_ptr createPermissionedDomain( ApplyContext& ac, - std::shared_ptr& sle, test::jtx::Account const& A1, - test::jtx::Account const& A2) + test::jtx::Account const& A2, + std::uint32_t numCreds = 2, + std::uint32_t seq = 10) { - sle->setAccountID(sfOwner, A1); - sle->setFieldU32(sfSequence, 10); + Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), seq); + auto sle = std::make_shared(pdKeylet); - STArray credentials(sfAcceptedCredentials, 2); - for (std::size_t n = 0; n < 2; ++n) + sle->setAccountID(sfOwner, A1); + sle->setFieldU32(sfSequence, seq); + + if (numCreds) { - auto cred = STObject::makeInnerObject(sfCredential); - cred.setAccountID(sfIssuer, A2); - auto credType = "cred_type" + std::to_string(n); - cred.setFieldVL(sfCredentialType, Slice(credType.c_str(), credType.size())); - credentials.push_back(std::move(cred)); + // This array is sorted naturally, but if you willing to change this + // behavior don't forget to use credentials::makeSorted + STArray credentials(sfAcceptedCredentials, numCreds); + for (std::size_t n = 0; n < numCreds; ++n) + { + auto cred = STObject::makeInnerObject(sfCredential); + cred.setAccountID(sfIssuer, A2); + auto credType = "cred_type" + std::to_string(n); + cred.setFieldVL(sfCredentialType, Slice(credType.c_str(), credType.size())); + credentials.push_back(std::move(cred)); + } + sle->setFieldArray(sfAcceptedCredentials, credentials); } - sle->setFieldArray(sfAcceptedCredentials, credentials); + ac.view().insert(sle); + return sle; }; void - testPermissionedDomainInvariants() + testPermissionedDomainInvariants(FeatureBitset features) { using namespace test::jtx; - testcase << "PermissionedDomain"; - doInvariantCheck( - {{"permissioned domain with no rules."}}, - [](Account const& A1, Account const&, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - slePd->setAccountID(sfOwner, A1); - slePd->setFieldU32(sfSequence, 10); + bool const fixPDEnabled = features[fixPermissionedDomainInvariant]; + std::initializer_list badTers = {tecINVARIANT_FAILED, tecINVARIANT_FAILED}; + std::initializer_list failTers = {tecINVARIANT_FAILED, tefINVARIANT_FAILED}; - ac.view().insert(slePd); - return true; + testcase << "PermissionedDomain" + std::string(fixPDEnabled ? " fix" : ""); + + doInvariantCheck( + Env(*this, features), + {{"permissioned domain with no rules."}}, + [](Account const& A1, Account const& A2, ApplyContext& ac) { + return createPermissionedDomain(ac, A1, A2, 0).get(); }, XRPAmount{}, - STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain 2"; auto constexpr tooBig = maxPermissionedDomainCredentialsArraySize + 1; doInvariantCheck( + Env(*this, features), {{"permissioned domain bad credentials size " + std::to_string(tooBig)}}, [](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - slePd->setAccountID(sfOwner, A1); - slePd->setFieldU32(sfSequence, 10); - - STArray credentials(sfAcceptedCredentials, tooBig); - for (std::size_t n = 0; n < tooBig; ++n) - { - auto cred = STObject::makeInnerObject(sfCredential); - cred.setAccountID(sfIssuer, A2); - auto credType = std::string("cred_type") + std::to_string(n); - cred.setFieldVL(sfCredentialType, Slice(credType.c_str(), credType.size())); - credentials.push_back(std::move(cred)); - } - slePd->setFieldArray(sfAcceptedCredentials, credentials); - ac.view().insert(slePd); - return true; + return !!createPermissionedDomain(ac, A1, A2, tooBig); }, XRPAmount{}, STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain 3"; doInvariantCheck( + Env(*this, features), {{"permissioned domain credentials aren't sorted"}}, [](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - slePd->setAccountID(sfOwner, A1); - slePd->setFieldU32(sfSequence, 10); + auto slePd = createPermissionedDomain(ac, A1, A2, 0); STArray credentials(sfAcceptedCredentials, 2); for (std::size_t n = 0; n < 2; ++n) @@ -1210,21 +1242,19 @@ class Invariants_test : public beast::unit_test::suite credentials.push_back(std::move(cred)); } slePd->setFieldArray(sfAcceptedCredentials, credentials); - ac.view().insert(slePd); + ac.view().update(slePd); return true; }, XRPAmount{}, STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain 4"; doInvariantCheck( + Env(*this, features), {{"permissioned domain credentials aren't unique"}}, [](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - slePd->setAccountID(sfOwner, A1); - slePd->setFieldU32(sfSequence, 10); + auto slePd = createPermissionedDomain(ac, A1, A2, 0); STArray credentials(sfAcceptedCredentials, 2); for (std::size_t n = 0; n < 2; ++n) @@ -1235,22 +1265,20 @@ class Invariants_test : public beast::unit_test::suite credentials.push_back(std::move(cred)); } slePd->setFieldArray(sfAcceptedCredentials, credentials); - ac.view().insert(slePd); + ac.view().update(slePd); return true; }, XRPAmount{}, - STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain Set 1"; doInvariantCheck( + Env(*this, features), {{"permissioned domain with no rules."}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - // create PD - createPermissionedDomain(ac, slePd, A1, A2); + auto slePd = createPermissionedDomain(ac, A1, A2); // update PD with empty rules { @@ -1262,18 +1290,16 @@ class Invariants_test : public beast::unit_test::suite return true; }, XRPAmount{}, - STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain Set 2"; doInvariantCheck( + Env(*this, features), {{"permissioned domain bad credentials size " + std::to_string(tooBig)}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - // create PD - createPermissionedDomain(ac, slePd, A1, A2); + auto slePd = createPermissionedDomain(ac, A1, A2); // update PD { @@ -1295,18 +1321,16 @@ class Invariants_test : public beast::unit_test::suite return true; }, XRPAmount{}, - STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain Set 3"; doInvariantCheck( + Env(*this, features), {{"permissioned domain credentials aren't sorted"}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - // create PD - createPermissionedDomain(ac, slePd, A1, A2); + auto slePd = createPermissionedDomain(ac, A1, A2); // update PD { @@ -1327,18 +1351,16 @@ class Invariants_test : public beast::unit_test::suite return true; }, XRPAmount{}, - STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : badTers); testcase << "PermissionedDomain Set 4"; doInvariantCheck( + Env(*this, features), {{"permissioned domain credentials aren't unique"}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - // create PD - createPermissionedDomain(ac, slePd, A1, A2); + auto slePd = createPermissionedDomain(ac, A1, A2); // update PD { @@ -1357,8 +1379,155 @@ class Invariants_test : public beast::unit_test::suite return true; }, XRPAmount{}, - STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : badTers); + + std::initializer_list goodTers = {tesSUCCESS, tesSUCCESS}; + + std::vector badMoreThan1{{"transaction affected more than 1 permissioned domain entry."}}; + std::vector emptyV; + std::vector badNoDomains{{"no domain objects affected by"}}; + std::vector badNotDeleted{{"domain object modified, but not deleted by "}}; + std::vector badDeleted{{"domain object deleted by"}}; + std::vector badTx{{"domain object(s) affected by an unauthorized transaction."}}; + + { + testcase << "PermissionedDomain set 2 domains "; + doInvariantCheck( + Env(*this, features), + fixPDEnabled ? badMoreThan1 : emptyV, + [](Account const& A1, Account const& A2, ApplyContext& ac) { + createPermissionedDomain(ac, A1, A2); + createPermissionedDomain(ac, A1, A2, 2, 11); + return true; + }, + XRPAmount{}, + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : goodTers); + } + + { + testcase << "PermissionedDomain del 2 domains"; + + Env env1(*this, features); + + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); + + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + [[maybe_unused]] auto [seq2, pd2] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); + + doInvariantCheck( + std::move(env1), + A1, + A2, + fixPDEnabled ? badMoreThan1 : emptyV, + [&pd1, &pd2](Account const&, Account const&, ApplyContext& ac) { + auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1}); + auto sle2 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd2}); + ac.view().erase(sle1); + ac.view().erase(sle2); + return true; + }, + XRPAmount{}, + STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}}, + fixPDEnabled ? failTers : goodTers); + } + + { + testcase << "PermissionedDomain set 0 domains "; + doInvariantCheck( + Env(*this, features), + fixPDEnabled ? badNoDomains : emptyV, + [](Account const&, Account const&, ApplyContext&) { return true; }, + XRPAmount{}, + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? badTers : goodTers); + } + + { + testcase << "PermissionedDomain del 0 domains"; + + Env env1(*this, features); + + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); + + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + [[maybe_unused]] auto [seq2, pd2] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); + + doInvariantCheck( + Env(*this, features), + A1, + A2, + fixPDEnabled ? badNoDomains : emptyV, + [](Account const&, Account const&, ApplyContext&) { return true; }, + XRPAmount{}, + STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}}, + fixPDEnabled ? badTers : goodTers); + } + + { + testcase << "PermissionedDomain set, delete domain"; + + Env env1(*this, features); + + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); + + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); + + doInvariantCheck( + std::move(env1), + A1, + A2, + fixPDEnabled ? badDeleted : emptyV, + [&pd1](Account const&, Account const&, ApplyContext& ac) { + auto sle1 = ac.view().peek({ltPERMISSIONED_DOMAIN, pd1}); + ac.view().erase(sle1); + return true; + }, + XRPAmount{}, + STTx{ttPERMISSIONED_DOMAIN_SET, [](STObject&) {}}, + fixPDEnabled ? failTers : goodTers); + } + + { + testcase << "PermissionedDomain del, create domain "; + doInvariantCheck( + Env(*this, features), + fixPDEnabled ? badNotDeleted : emptyV, + [](Account const& A1, Account const& A2, ApplyContext& ac) { + createPermissionedDomain(ac, A1, A2); + return true; + }, + XRPAmount{}, + STTx{ttPERMISSIONED_DOMAIN_DELETE, [](STObject&) {}}, + fixPDEnabled ? failTers : goodTers); + } + + { + testcase << "PermissionedDomain invalid tx"; + + doInvariantCheck( + fixPDEnabled ? badTx : emptyV, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + createPermissionedDomain(ac, A1, A2); + return true; + }, + XRPAmount{}, + STTx{ttPAYMENT, [](STObject&) {}}, + failTers); + } } void @@ -1478,13 +1647,43 @@ class Invariants_test : public beast::unit_test::suite }); } - void - testPermissionedDEX() + static std::pair + createPermissionedDomainEnv( + test::jtx::Env& env, + test::jtx::Account const& A1, + test::jtx::Account const& A2, + std::uint32_t numCreds = 2) { using namespace test::jtx; - testcase << "PermissionedDEX"; + + pdomain::Credentials credentials; + + for (std::size_t n = 0; n < numCreds; ++n) + { + auto credType = "cred_type" + std::to_string(n); + credentials.push_back({A2, credType}); + } + + std::uint32_t const seq = env.seq(A1); + env(pdomain::setTx(A1, credentials)); + uint256 key = pdomain::getNewDomain(env.meta()); + + // std::cout << "PD, acc: " << A1.id() << ", seq: " << seq << ", k: " << + // key << std::endl; + return {seq, key}; + } + + void + testPermissionedDEX(FeatureBitset features) + { + using namespace test::jtx; + + bool const fixPDEnabled = features[fixPermissionedDomainInvariant]; + + testcase << "PermissionedDEX" + std::string(fixPDEnabled ? " fix" : ""); doInvariantCheck( + Env(*this, features), {{"domain doesn't exist"}}, [](Account const& A1, Account const&, ApplyContext& ac) { Keylet const offerKey = keylet::offer(A1.id(), 10); @@ -1511,12 +1710,9 @@ class Invariants_test : public beast::unit_test::suite // missing domain ID in offer object doInvariantCheck( + Env(*this, features), {{"hybrid offer is malformed"}}, [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - createPermissionedDomain(ac, slePd, A1, A2); - Keylet const offerKey = keylet::offer(A2.id(), 10); auto sleOffer = std::make_shared(offerKey); sleOffer->setAccountID(sfAccount, A2); @@ -1531,116 +1727,154 @@ class Invariants_test : public beast::unit_test::suite return true; }, XRPAmount{}, - STTx{ttOFFER_CREATE, [&](STObject& tx) {}}, + STTx{ttOFFER_CREATE, [&](STObject&) {}}, {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); // more than one entry in sfAdditionalBooks - doInvariantCheck( - {{"hybrid offer is malformed"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - createPermissionedDomain(ac, slePd, A1, A2); + { + Env env1(*this, features); - Keylet const offerKey = keylet::offer(A2.id(), 10); - auto sleOffer = std::make_shared(offerKey); - sleOffer->setAccountID(sfAccount, A2); - sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); - sleOffer->setFieldAmount(sfTakerGets, XRP(1)); - sleOffer->setFlag(lsfHybrid); - sleOffer->setFieldH256(sfDomainID, pdKeylet.key); + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); - STArray bookArr; - bookArr.push_back(STObject::makeInnerObject(sfBook)); - bookArr.push_back(STObject::makeInnerObject(sfBook)); - sleOffer->setFieldArray(sfAdditionalBooks, bookArr); - ac.view().insert(sleOffer); - return true; - }, - XRPAmount{}, - STTx{ttOFFER_CREATE, [&](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); + + doInvariantCheck( + std::move(env1), + A1, + A2, + {{"hybrid offer is malformed"}}, + [&pd1](Account const& A1, Account const& A2, ApplyContext& ac) { + Keylet const offerKey = keylet::offer(A2.id(), 10); + auto sleOffer = std::make_shared(offerKey); + sleOffer->setAccountID(sfAccount, A2); + sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); + sleOffer->setFieldAmount(sfTakerGets, XRP(1)); + sleOffer->setFlag(lsfHybrid); + sleOffer->setFieldH256(sfDomainID, pd1); + + STArray bookArr; + bookArr.push_back(STObject::makeInnerObject(sfBook)); + bookArr.push_back(STObject::makeInnerObject(sfBook)); + sleOffer->setFieldArray(sfAdditionalBooks, bookArr); + ac.view().insert(sleOffer); + return true; + }, + XRPAmount{}, + STTx{ttOFFER_CREATE, [&](STObject&) {}}, + {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + } // hybrid offer missing sfAdditionalBooks - doInvariantCheck( - {{"hybrid offer is malformed"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - createPermissionedDomain(ac, slePd, A1, A2); + { + Env env1(*this, features); - Keylet const offerKey = keylet::offer(A2.id(), 10); - auto sleOffer = std::make_shared(offerKey); - sleOffer->setAccountID(sfAccount, A2); - sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); - sleOffer->setFieldAmount(sfTakerGets, XRP(1)); - sleOffer->setFlag(lsfHybrid); - sleOffer->setFieldH256(sfDomainID, pdKeylet.key); - ac.view().insert(sleOffer); - return true; - }, - XRPAmount{}, - STTx{ttOFFER_CREATE, [&](STObject& tx) {}}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); - doInvariantCheck( - {{"transaction consumed wrong domains"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - createPermissionedDomain(ac, slePd, A1, A2); + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); - Keylet const badDomainKeylet = keylet::permissionedDomain(A1.id(), 20); - auto sleBadPd = std::make_shared(badDomainKeylet); - createPermissionedDomain(ac, sleBadPd, A1, A2); + doInvariantCheck( + std::move(env1), + A1, + A2, + {{"hybrid offer is malformed"}}, + [&pd1](Account const& A1, Account const& A2, ApplyContext& ac) { + Keylet const offerKey = keylet::offer(A2.id(), 10); + auto sleOffer = std::make_shared(offerKey); + sleOffer->setAccountID(sfAccount, A2); + sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); + sleOffer->setFieldAmount(sfTakerGets, XRP(1)); + sleOffer->setFlag(lsfHybrid); + sleOffer->setFieldH256(sfDomainID, pd1); + ac.view().insert(sleOffer); + return true; + }, + XRPAmount{}, + STTx{ttOFFER_CREATE, [&](STObject&) {}}, + {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + } - Keylet const offerKey = keylet::offer(A2.id(), 10); - auto sleOffer = std::make_shared(offerKey); - sleOffer->setAccountID(sfAccount, A2); - sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); - sleOffer->setFieldAmount(sfTakerGets, XRP(1)); - sleOffer->setFieldH256(sfDomainID, pdKeylet.key); - ac.view().insert(sleOffer); - return true; - }, - XRPAmount{}, - STTx{ - ttOFFER_CREATE, - [&](STObject& tx) { - Account const A1{"A1"}; - Keylet const badDomainKey = keylet::permissionedDomain(A1.id(), 20); - tx.setFieldH256(sfDomainID, badDomainKey.key); - tx.setFieldAmount(sfTakerPays, A1["USD"](10)); - tx.setFieldAmount(sfTakerGets, XRP(1)); - }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + { + Env env1(*this, features); - doInvariantCheck( - {{"domain transaction affected regular offers"}}, - [&](Account const& A1, Account const& A2, ApplyContext& ac) { - Keylet const pdKeylet = keylet::permissionedDomain(A1.id(), 10); - auto slePd = std::make_shared(pdKeylet); - createPermissionedDomain(ac, slePd, A1, A2); + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); - Keylet const offerKey = keylet::offer(A2.id(), 10); - auto sleOffer = std::make_shared(offerKey); - sleOffer->setAccountID(sfAccount, A2); - sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); - sleOffer->setFieldAmount(sfTakerGets, XRP(1)); - ac.view().insert(sleOffer); - return true; - }, - XRPAmount{}, - STTx{ - ttOFFER_CREATE, - [&](STObject& tx) { - Account const A1{"A1"}; - Keylet const domainKey = keylet::permissionedDomain(A1.id(), 10); - tx.setFieldH256(sfDomainID, domainKey.key); - tx.setFieldAmount(sfTakerPays, A1["USD"](10)); - tx.setFieldAmount(sfTakerGets, XRP(1)); - }}, - {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + [[maybe_unused]] auto [seq2, pd2] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); + + doInvariantCheck( + std::move(env1), + A1, + A2, + {{"transaction consumed wrong domains"}}, + [&pd1](Account const& A1, Account const& A2, ApplyContext& ac) { + Keylet const offerKey = keylet::offer(A2.id(), 10); + auto sleOffer = std::make_shared(offerKey); + sleOffer->setAccountID(sfAccount, A2); + sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); + sleOffer->setFieldAmount(sfTakerGets, XRP(1)); + sleOffer->setFieldH256(sfDomainID, pd1); + ac.view().insert(sleOffer); + return true; + }, + XRPAmount{}, + STTx{ + ttOFFER_CREATE, + [&pd2, &A1](STObject& tx) { + tx.setFieldH256(sfDomainID, pd2); + tx.setFieldAmount(sfTakerPays, A1["USD"](10)); + tx.setFieldAmount(sfTakerGets, XRP(1)); + }}, + {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + } + + { + Env env1(*this, features); + + Account const A1{"A1"}; + Account const A2{"A2"}; + env1.fund(XRP(1000), A1, A2); + env1.close(); + + [[maybe_unused]] auto [seq1, pd1] = createPermissionedDomainEnv(env1, A1, A2); + env1.close(); + + doInvariantCheck( + std::move(env1), + A1, + A2, + {{"domain transaction affected regular offers"}}, + [&](Account const& A1, Account const& A2, ApplyContext& ac) { + Keylet const offerKey = keylet::offer(A2.id(), 10); + auto sleOffer = std::make_shared(offerKey); + sleOffer->setAccountID(sfAccount, A2); + sleOffer->setFieldAmount(sfTakerPays, A1["USD"](10)); + sleOffer->setFieldAmount(sfTakerGets, XRP(1)); + ac.view().insert(sleOffer); + return true; + }, + XRPAmount{}, + STTx{ + ttOFFER_CREATE, + [&](STObject& tx) { + Account const A1{"A1"}; + tx.setFieldH256(sfDomainID, pd1); + tx.setFieldAmount(sfTakerPays, A1["USD"](10)); + tx.setFieldAmount(sfTakerGets, XRP(1)); + }}, + {tecINVARIANT_FAILED, tecINVARIANT_FAILED}); + } } Keylet @@ -3490,8 +3724,10 @@ public: testNoZeroEscrow(); testValidNewAccountRoot(); testNFTokenPageInvariants(); - testPermissionedDomainInvariants(); - testPermissionedDEX(); + testPermissionedDomainInvariants(defaultAmendments() | fixPermissionedDomainInvariant); + testPermissionedDomainInvariants(defaultAmendments() - fixPermissionedDomainInvariant); + testPermissionedDEX(defaultAmendments() | fixPermissionedDomainInvariant); + testPermissionedDEX(defaultAmendments() - fixPermissionedDomainInvariant); testNoModifiedUnmodifiableFields(); testValidPseudoAccounts(); testValidLoanBroker(); diff --git a/src/test/app/PermissionedDomains_test.cpp b/src/test/app/PermissionedDomains_test.cpp index 1a1fe9a9eb..8485202144 100644 --- a/src/test/app/PermissionedDomains_test.cpp +++ b/src/test/app/PermissionedDomains_test.cpp @@ -38,13 +38,17 @@ class PermissionedDomains_test : public beast::unit_test::suite testable_amendments() // | featurePermissionedDomains | featureCredentials}; + FeatureBitset withFix_{ + testable_amendments() // + | featurePermissionedDomains | featureCredentials}; + // Verify that each tx type can execute if the feature is enabled. void - testEnabled() + testEnabled(FeatureBitset features) { testcase("Enabled"); Account const alice("alice"); - Env env(*this, withFeature_); + Env env(*this, features); env.fund(XRP(1000), alice); pdomain::Credentials credentials{{alice, "first credential"}}; env(pdomain::setTx(alice, credentials)); @@ -237,10 +241,10 @@ class PermissionedDomains_test : public beast::unit_test::suite // Test PermissionedDomainSet void - testSet() + testSet(FeatureBitset features) { testcase("Set"); - Env env(*this, withFeature_); + Env env(*this, features); env.set_parse_failure_expected(true); int const accNum = 12; @@ -395,10 +399,10 @@ class PermissionedDomains_test : public beast::unit_test::suite // Test PermissionedDomainDelete void - testDelete() + testDelete(FeatureBitset features) { testcase("Delete"); - Env env(*this, withFeature_); + Env env(*this, features); Account const alice("alice"); env.fund(XRP(1000), alice); @@ -448,14 +452,14 @@ class PermissionedDomains_test : public beast::unit_test::suite } void - testAccountReserve() + testAccountReserve(FeatureBitset features) { // Verify that the reserve behaves as expected for creating. testcase("Account Reserve"); using namespace test::jtx; - Env env(*this, withFeature_); + Env env(*this, features); Account const alice("alice"); // Fund alice enough to exist, but not enough to meet @@ -500,12 +504,16 @@ public: void run() override { - testEnabled(); + testEnabled(withFeature_); + testEnabled(withFix_); testCredentialsDisabled(); testDisabled(); - testSet(); - testDelete(); - testAccountReserve(); + testSet(withFeature_); + testSet(withFix_); + testDelete(withFeature_); + testDelete(withFix_); + testAccountReserve(withFeature_); + testAccountReserve(withFix_); } }; diff --git a/src/test/jtx/impl/balance.cpp b/src/test/jtx/impl/balance.cpp index 9d6f5c6124..1ebeeed437 100644 --- a/src/test/jtx/impl/balance.cpp +++ b/src/test/jtx/impl/balance.cpp @@ -4,6 +4,10 @@ namespace xrpl { namespace test { namespace jtx { +#define TEST_EXPECT(cond) env.test.expect(cond, __FILE__, __LINE__) +#define TEST_EXPECTS(cond, reason) \ + ((cond) ? (env.test.pass(), true) : (env.test.fail((reason), __FILE__, __LINE__), false)) + void doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, Issue const& issue) { @@ -12,11 +16,13 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, auto const sle = env.le(keylet::account(account)); if (none) { - env.test.expect(!sle); + TEST_EXPECT(!sle); } - else if (env.test.expect(sle)) + else if (TEST_EXPECT(sle)) { - env.test.expect(sle->getFieldAmount(sfBalance) == value); + TEST_EXPECTS( + sle->getFieldAmount(sfBalance) == value, + sle->getFieldAmount(sfBalance).getText() + " / " + value.getText()); } } else @@ -24,15 +30,15 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, auto const sle = env.le(keylet::line(account, issue)); if (none) { - env.test.expect(!sle); + TEST_EXPECT(!sle); } - else if (env.test.expect(sle)) + else if (TEST_EXPECT(sle)) { auto amount = sle->getFieldAmount(sfBalance); amount.setIssuer(issue.account); if (account > issue.account) amount.negate(); - env.test.expect(amount == value); + TEST_EXPECTS(amount == value, amount.getText()); } } } @@ -43,12 +49,12 @@ doBalance(Env& env, AccountID const& account, bool none, STAmount const& value, auto const sle = env.le(keylet::mptoken(mptIssue.getMptID(), account)); if (none) { - env.test.expect(!sle); + TEST_EXPECT(!sle); } - else if (env.test.expect(sle)) + else if (TEST_EXPECT(sle)) { STAmount const amount{mptIssue, sle->getFieldU64(sfMPTAmount)}; - env.test.expect(amount == value); + TEST_EXPECT(amount == value); } } diff --git a/src/xrpld/app/tx/detail/InvariantCheck.cpp b/src/xrpld/app/tx/detail/InvariantCheck.cpp index fe47449c36..24a37270ce 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.cpp +++ b/src/xrpld/app/tx/detail/InvariantCheck.cpp @@ -1507,7 +1507,7 @@ ValidMPTIssuance::finalize( void ValidPermissionedDomain::visitEntry( - bool, + bool isDel, std::shared_ptr const& before, std::shared_ptr const& after) { @@ -1516,39 +1516,29 @@ ValidPermissionedDomain::visitEntry( if (after && after->getType() != ltPERMISSIONED_DOMAIN) return; - auto check = [](SleStatus& sleStatus, std::shared_ptr const& sle) { + auto check = [isDel](std::vector& sleStatus, std::shared_ptr const& sle) { auto const& credentials = sle->getFieldArray(sfAcceptedCredentials); - sleStatus.credentialsSize_ = credentials.size(); auto const sorted = credentials::makeSorted(credentials); - sleStatus.isUnique_ = !sorted.empty(); + + SleStatus ss{credentials.size(), false, !sorted.empty(), isDel}; // If array have duplicates then all the other checks are invalid - sleStatus.isSorted_ = false; - - if (sleStatus.isUnique_) + if (ss.isUnique_) { unsigned i = 0; for (auto const& cred : sorted) { auto const& credTx = credentials[i++]; - sleStatus.isSorted_ = (cred.first == credTx[sfIssuer]) && (cred.second == credTx[sfCredentialType]); - if (!sleStatus.isSorted_) + ss.isSorted_ = (cred.first == credTx[sfIssuer]) && (cred.second == credTx[sfCredentialType]); + if (!ss.isSorted_) break; } } + sleStatus.emplace_back(std::move(ss)); }; - if (before) - { - sleStatus_[0] = SleStatus(); - check(*sleStatus_[0], after); - } - if (after) - { - sleStatus_[1] = SleStatus(); - check(*sleStatus_[1], after); - } + check(sleStatus_, after); } bool @@ -1559,9 +1549,6 @@ ValidPermissionedDomain::finalize( ReadView const& view, beast::Journal const& j) { - if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || result != tesSUCCESS) - return true; - auto check = [](SleStatus const& sleStatus, beast::Journal const& j) { if (!sleStatus.credentialsSize_) { @@ -1595,7 +1582,76 @@ ValidPermissionedDomain::finalize( return true; }; - return (sleStatus_[0] ? check(*sleStatus_[0], j) : true) && (sleStatus_[1] ? check(*sleStatus_[1], j) : true); + if (view.rules().enabled(fixPermissionedDomainInvariant)) + { + // No permissioned domains should be affected if the transaction failed + if (result != tesSUCCESS) + // If nothing changed, all is good. If there were changes, that's + // bad. + return sleStatus_.empty(); + + if (sleStatus_.size() > 1) + { + JLOG(j.fatal()) << "Invariant failed: transaction affected more " + "than 1 permissioned domain entry."; + return false; + } + + switch (tx.getTxnType()) + { + case ttPERMISSIONED_DOMAIN_SET: { + if (sleStatus_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: no domain objects affected by " + "PermissionedDomainSet"; + return false; + } + + auto const& sleStatus = sleStatus_[0]; + if (sleStatus.isDelete_) + { + JLOG(j.fatal()) << "Invariant failed: domain object " + "deleted by PermissionedDomainSet"; + return false; + } + return check(sleStatus, j); + } + case ttPERMISSIONED_DOMAIN_DELETE: { + if (sleStatus_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: no domain objects affected by " + "PermissionedDomainDelete"; + return false; + } + + if (!sleStatus_[0].isDelete_) + { + JLOG(j.fatal()) << "Invariant failed: domain object " + "modified, but not deleted by " + "PermissionedDomainDelete"; + return false; + } + return true; + } + default: { + if (!sleStatus_.empty()) + { + JLOG(j.fatal()) << "Invariant failed: " << sleStatus_.size() + << " domain object(s) affected by an " + "unauthorized transaction. " + << tx.getTxnType(); + return false; + } + return true; + } + } + } + else + { + if (tx.getTxnType() != ttPERMISSIONED_DOMAIN_SET || result != tesSUCCESS || sleStatus_.empty()) + return true; + return check(sleStatus_[0], j); + } } //------------------------------------------------------------------------------ diff --git a/src/xrpld/app/tx/detail/InvariantCheck.h b/src/xrpld/app/tx/detail/InvariantCheck.h index 9a20e372ba..7e9a62d9e2 100644 --- a/src/xrpld/app/tx/detail/InvariantCheck.h +++ b/src/xrpld/app/tx/detail/InvariantCheck.h @@ -449,9 +449,11 @@ class ValidPermissionedDomain struct SleStatus { std::size_t credentialsSize_{0}; - bool isSorted_ = false, isUnique_ = false; + bool isSorted_ = false; + bool isUnique_ = false; + bool isDelete_ = false; }; - std::optional sleStatus_[2]; + std::vector sleStatus_; public: void From ef284692db4588082eac8106b1f5262bf1ad7534 Mon Sep 17 00:00:00 2001 From: Jingchen Date: Wed, 11 Feb 2026 13:42:31 +0000 Subject: [PATCH 2/2] refactor: Modularize WalletDB and Manifest (#6223) This change modularizes the `WalletDB` and `Manifest`. Note that the wallet db has nothing to do with account wallets and it stores node configuration, which is why it depends on the manifest code. --- .../scripts/levelization/results/loops.txt | 5 +- .../scripts/levelization/results/ordering.txt | 18 +- cmake/XrplCore.cmake | 21 +- cmake/XrplInstall.cmake | 1 + .../xrpl/core}/PeerReservationTable.h | 0 include/xrpl/core/ServiceRegistry.h | 24 ++ include/xrpl/core/StartUpType.h | 16 ++ .../app/main => include/xrpl/rdb}/DBInit.h | 0 .../core => include/xrpl/rdb}/DatabaseCon.h | 16 +- {src/xrpld/core => include/xrpl/rdb}/SociDB.h | 0 .../misc => include/xrpl/server}/Manifest.h | 0 .../app/rdb => include/xrpl/server}/State.h | 8 +- .../app/rdb => include/xrpl/server}/Vacuum.h | 2 +- .../app/rdb => include/xrpl/server}/Wallet.h | 7 +- src/libxrpl/rdb/DatabaseCon.cpp | 92 +++++++ .../core/detail => libxrpl/rdb}/SociDB.cpp | 6 +- .../rdb/detail => libxrpl/server}/State.cpp | 2 +- .../rdb/detail => libxrpl/server}/Vacuum.cpp | 4 +- .../rdb/detail => libxrpl/server}/Wallet.cpp | 3 +- src/test/app/LedgerLoad_test.cpp | 22 +- src/test/app/Manifest_test.cpp | 6 +- src/test/app/ValidatorKeys_test.cpp | 2 +- src/test/core/SociDB_test.cpp | 3 +- src/test/nodestore/Database_test.cpp | 3 +- src/test/rpc/LedgerEntry_test.cpp | 4 +- src/xrpld/app/ledger/Ledger.cpp | 1 - src/xrpld/app/main/Application.cpp | 26 +- src/xrpld/app/main/Application.h | 14 +- src/xrpld/app/main/Main.cpp | 16 +- src/xrpld/app/main/NodeIdentity.cpp | 3 +- src/xrpld/app/misc/SHAMapStoreImp.cpp | 2 +- src/xrpld/app/misc/SHAMapStoreImp.h | 4 +- src/xrpld/app/misc/ValidatorList.h | 2 +- src/xrpld/app/misc/detail/AmendmentTable.cpp | 2 +- src/xrpld/app/misc/detail/Manifest.cpp | 7 +- src/xrpld/app/misc/detail/ValidatorKeys.cpp | 2 +- src/xrpld/app/rdb/PeerFinder.h | 3 +- src/xrpld/app/rdb/RelationalDatabase.h | 8 +- src/xrpld/app/rdb/backend/detail/Node.cpp | 8 +- .../app/rdb/backend/detail/SQLiteDatabase.cpp | 48 ++-- .../app/rdb/detail/RelationalDatabase.cpp | 7 +- src/xrpld/core/Config.h | 8 +- src/xrpld/core/detail/Config.cpp | 146 +++++++++++ src/xrpld/core/detail/DatabaseCon.cpp | 242 ------------------ src/xrpld/overlay/detail/OverlayImpl.cpp | 2 +- .../overlay/detail/PeerReservationTable.cpp | 4 +- src/xrpld/peerfinder/detail/StoreSqdb.h | 3 +- src/xrpld/rpc/InfoSub.h | 3 +- 48 files changed, 435 insertions(+), 391 deletions(-) rename {src/xrpld/overlay => include/xrpl/core}/PeerReservationTable.h (100%) create mode 100644 include/xrpl/core/StartUpType.h rename {src/xrpld/app/main => include/xrpl/rdb}/DBInit.h (100%) rename {src/xrpld/core => include/xrpl/rdb}/DatabaseCon.h (93%) rename {src/xrpld/core => include/xrpl/rdb}/SociDB.h (100%) rename {src/xrpld/app/misc => include/xrpl/server}/Manifest.h (100%) rename {src/xrpld/app/rdb => include/xrpl/server}/State.h (91%) rename {src/xrpld/app/rdb => include/xrpl/server}/Vacuum.h (90%) rename {src/xrpld/app/rdb => include/xrpl/server}/Wallet.h (96%) create mode 100644 src/libxrpl/rdb/DatabaseCon.cpp rename src/{xrpld/core/detail => libxrpl/rdb}/SociDB.cpp (98%) rename src/{xrpld/app/rdb/detail => libxrpl/server}/State.cpp (98%) rename src/{xrpld/app/rdb/detail => libxrpl/server}/Vacuum.cpp (96%) rename src/{xrpld/app/rdb/detail => libxrpl/server}/Wallet.cpp (99%) delete mode 100644 src/xrpld/core/detail/DatabaseCon.cpp diff --git a/.github/scripts/levelization/results/loops.txt b/.github/scripts/levelization/results/loops.txt index d15843ceb0..34842d7f48 100644 --- a/.github/scripts/levelization/results/loops.txt +++ b/.github/scripts/levelization/results/loops.txt @@ -4,14 +4,11 @@ Loop: test.jtx test.toplevel Loop: test.jtx test.unit_test test.unit_test == test.jtx -Loop: xrpld.app xrpld.core - xrpld.app > xrpld.core - Loop: xrpld.app xrpld.overlay xrpld.overlay > xrpld.app Loop: xrpld.app xrpld.peerfinder - xrpld.peerfinder ~= xrpld.app + xrpld.peerfinder == xrpld.app Loop: xrpld.app xrpld.rpc xrpld.rpc > xrpld.app diff --git a/.github/scripts/levelization/results/ordering.txt b/.github/scripts/levelization/results/ordering.txt index 88a3441fa1..fabc7b49ca 100644 --- a/.github/scripts/levelization/results/ordering.txt +++ b/.github/scripts/levelization/results/ordering.txt @@ -17,12 +17,15 @@ libxrpl.nodestore > xrpl.protocol libxrpl.protocol > xrpl.basics libxrpl.protocol > xrpl.json libxrpl.protocol > xrpl.protocol +libxrpl.rdb > xrpl.basics +libxrpl.rdb > xrpl.rdb libxrpl.resource > xrpl.basics libxrpl.resource > xrpl.json libxrpl.resource > xrpl.resource libxrpl.server > xrpl.basics libxrpl.server > xrpl.json libxrpl.server > xrpl.protocol +libxrpl.server > xrpl.rdb libxrpl.server > xrpl.server libxrpl.shamap > xrpl.basics libxrpl.shamap > xrpl.protocol @@ -41,7 +44,9 @@ test.app > xrpl.json test.app > xrpl.ledger test.app > xrpl.nodestore test.app > xrpl.protocol +test.app > xrpl.rdb test.app > xrpl.resource +test.app > xrpl.server test.basics > test.jtx test.basics > test.unit_test test.basics > xrpl.basics @@ -67,6 +72,7 @@ test.core > xrpl.basics test.core > xrpl.core test.core > xrpld.core test.core > xrpl.json +test.core > xrpl.rdb test.core > xrpl.server test.csf > xrpl.basics test.csf > xrpld.consensus @@ -95,8 +101,8 @@ test.nodestore > test.jtx test.nodestore > test.toplevel test.nodestore > test.unit_test test.nodestore > xrpl.basics -test.nodestore > xrpld.core test.nodestore > xrpl.nodestore +test.nodestore > xrpl.rdb test.overlay > test.jtx test.overlay > test.toplevel test.overlay > test.unit_test @@ -154,6 +160,7 @@ tests.libxrpl > xrpl.net xrpl.core > xrpl.basics xrpl.core > xrpl.json xrpl.core > xrpl.ledger +xrpl.core > xrpl.protocol xrpl.json > xrpl.basics xrpl.ledger > xrpl.basics xrpl.ledger > xrpl.protocol @@ -162,12 +169,16 @@ xrpl.nodestore > xrpl.basics xrpl.nodestore > xrpl.protocol xrpl.protocol > xrpl.basics xrpl.protocol > xrpl.json +xrpl.rdb > xrpl.basics +xrpl.rdb > xrpl.core xrpl.resource > xrpl.basics xrpl.resource > xrpl.json xrpl.resource > xrpl.protocol xrpl.server > xrpl.basics +xrpl.server > xrpl.core xrpl.server > xrpl.json xrpl.server > xrpl.protocol +xrpl.server > xrpl.rdb xrpl.shamap > xrpl.basics xrpl.shamap > xrpl.nodestore xrpl.shamap > xrpl.protocol @@ -176,12 +187,15 @@ xrpld.app > xrpl.basics xrpld.app > xrpl.core xrpld.app > xrpld.conditions xrpld.app > xrpld.consensus +xrpld.app > xrpld.core xrpld.app > xrpl.json xrpld.app > xrpl.ledger xrpld.app > xrpl.net xrpld.app > xrpl.nodestore xrpld.app > xrpl.protocol +xrpld.app > xrpl.rdb xrpld.app > xrpl.resource +xrpld.app > xrpl.server xrpld.app > xrpl.shamap xrpld.conditions > xrpl.basics xrpld.conditions > xrpl.protocol @@ -193,6 +207,7 @@ xrpld.core > xrpl.core xrpld.core > xrpl.json xrpld.core > xrpl.net xrpld.core > xrpl.protocol +xrpld.core > xrpl.rdb xrpld.overlay > xrpl.basics xrpld.overlay > xrpl.core xrpld.overlay > xrpld.core @@ -204,6 +219,7 @@ xrpld.overlay > xrpl.server xrpld.peerfinder > xrpl.basics xrpld.peerfinder > xrpld.core xrpld.peerfinder > xrpl.protocol +xrpld.peerfinder > xrpl.rdb xrpld.perflog > xrpl.basics xrpld.perflog > xrpl.core xrpld.perflog > xrpld.rpc diff --git a/cmake/XrplCore.cmake b/cmake/XrplCore.cmake index 57d0e83348..cea19db9bc 100644 --- a/cmake/XrplCore.cmake +++ b/cmake/XrplCore.cmake @@ -84,9 +84,6 @@ add_module(xrpl net) target_link_libraries(xrpl.libxrpl.net PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol xrpl.libxrpl.resource) -add_module(xrpl server) -target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol) - add_module(xrpl nodestore) target_link_libraries(xrpl.libxrpl.nodestore PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol) @@ -94,8 +91,15 @@ add_module(xrpl shamap) target_link_libraries(xrpl.libxrpl.shamap PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.crypto xrpl.libxrpl.protocol xrpl.libxrpl.nodestore) +add_module(xrpl rdb) +target_link_libraries(xrpl.libxrpl.rdb PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.core) + +add_module(xrpl server) +target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol xrpl.libxrpl.core xrpl.libxrpl.rdb) + add_module(xrpl ledger) -target_link_libraries(xrpl.libxrpl.ledger PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol) +target_link_libraries(xrpl.libxrpl.ledger PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol + xrpl.libxrpl.rdb) add_library(xrpl.libxrpl) set_target_properties(xrpl.libxrpl PROPERTIES OUTPUT_NAME xrpl) @@ -113,13 +117,14 @@ target_link_modules( core crypto json + ledger + net + nodestore protocol + rdb resource server - nodestore - shamap - net - ledger) + shamap) # All headers in libxrpl are in modules. # Uncomment this stanza if you have not yet moved new headers into a module. diff --git a/cmake/XrplInstall.cmake b/cmake/XrplInstall.cmake index 141dc56089..340dca553b 100644 --- a/cmake/XrplInstall.cmake +++ b/cmake/XrplInstall.cmake @@ -23,6 +23,7 @@ install(TARGETS common xrpl.libxrpl.core xrpl.libxrpl.crypto xrpl.libxrpl.json + xrpl.libxrpl.rdb xrpl.libxrpl.ledger xrpl.libxrpl.net xrpl.libxrpl.nodestore diff --git a/src/xrpld/overlay/PeerReservationTable.h b/include/xrpl/core/PeerReservationTable.h similarity index 100% rename from src/xrpld/overlay/PeerReservationTable.h rename to include/xrpl/core/PeerReservationTable.h diff --git a/include/xrpl/core/ServiceRegistry.h b/include/xrpl/core/ServiceRegistry.h index 7147242339..86591a815f 100644 --- a/include/xrpl/core/ServiceRegistry.h +++ b/include/xrpl/core/ServiceRegistry.h @@ -5,6 +5,8 @@ #include #include +#include + namespace xrpl { // Forward declarations @@ -18,6 +20,10 @@ namespace perf { class PerfLog; } +// This is temporary until we migrate all code to use ServiceRegistry. +class Application; + +// Forward declarations class AcceptedLedger; class AmendmentTable; class Cluster; @@ -194,6 +200,24 @@ public: virtual perf::PerfLog& getPerfLog() = 0; + + // Configuration and state + virtual bool + isStopping() const = 0; + + virtual beast::Journal + journal(std::string const& name) = 0; + + virtual boost::asio::io_context& + getIOContext() = 0; + + virtual Logs& + logs() = 0; + + // Temporary: Get the underlying Application for functions that haven't + // been migrated yet. This should be removed once all code is migrated. + virtual Application& + app() = 0; }; } // namespace xrpl diff --git a/include/xrpl/core/StartUpType.h b/include/xrpl/core/StartUpType.h new file mode 100644 index 0000000000..74a1898806 --- /dev/null +++ b/include/xrpl/core/StartUpType.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace xrpl { + +enum class StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK }; + +inline std::ostream& +operator<<(std::ostream& os, StartUpType const& type) +{ + return os << static_cast>(type); +} + +} // namespace xrpl diff --git a/src/xrpld/app/main/DBInit.h b/include/xrpl/rdb/DBInit.h similarity index 100% rename from src/xrpld/app/main/DBInit.h rename to include/xrpl/rdb/DBInit.h diff --git a/src/xrpld/core/DatabaseCon.h b/include/xrpl/rdb/DatabaseCon.h similarity index 93% rename from src/xrpld/core/DatabaseCon.h rename to include/xrpl/rdb/DatabaseCon.h index 89d582257b..37a53044e4 100644 --- a/src/xrpld/core/DatabaseCon.h +++ b/include/xrpl/rdb/DatabaseCon.h @@ -1,10 +1,9 @@ #pragma once -#include -#include -#include - #include +#include +#include +#include #include @@ -68,7 +67,7 @@ public: { explicit Setup() = default; - Config::StartUpType startUp = Config::NORMAL; + StartUpType startUp = StartUpType::NORMAL; bool standAlone = false; boost::filesystem::path dataDir; // Indicates whether or not to return the `globalPragma` @@ -105,8 +104,8 @@ public: beast::Journal journal) // Use temporary files or regular DB files? : DatabaseCon( - setup.standAlone && setup.startUp != Config::LOAD && setup.startUp != Config::LOAD_FILE && - setup.startUp != Config::REPLAY + setup.standAlone && setup.startUp != StartUpType::LOAD && setup.startUp != StartUpType::LOAD_FILE && + setup.startUp != StartUpType::REPLAY ? "" : (setup.dataDir / dbName), setup.commonPragma(), @@ -229,7 +228,4 @@ private: std::shared_ptr checkpointerFromId(std::uintptr_t id); -DatabaseCon::Setup -setup_DatabaseCon(Config const& c, std::optional j = std::nullopt); - } // namespace xrpl diff --git a/src/xrpld/core/SociDB.h b/include/xrpl/rdb/SociDB.h similarity index 100% rename from src/xrpld/core/SociDB.h rename to include/xrpl/rdb/SociDB.h diff --git a/src/xrpld/app/misc/Manifest.h b/include/xrpl/server/Manifest.h similarity index 100% rename from src/xrpld/app/misc/Manifest.h rename to include/xrpl/server/Manifest.h diff --git a/src/xrpld/app/rdb/State.h b/include/xrpl/server/State.h similarity index 91% rename from src/xrpld/app/rdb/State.h rename to include/xrpl/server/State.h index 52118b3cf8..48e11869f4 100644 --- a/src/xrpld/app/rdb/State.h +++ b/include/xrpl/server/State.h @@ -1,10 +1,8 @@ #pragma once -#include -#include -#include -#include -#include +#include +#include +#include #include diff --git a/src/xrpld/app/rdb/Vacuum.h b/include/xrpl/server/Vacuum.h similarity index 90% rename from src/xrpld/app/rdb/Vacuum.h rename to include/xrpl/server/Vacuum.h index f592b4537e..5f80eced87 100644 --- a/src/xrpld/app/rdb/Vacuum.h +++ b/include/xrpl/server/Vacuum.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace xrpl { diff --git a/src/xrpld/app/rdb/Wallet.h b/include/xrpl/server/Wallet.h similarity index 96% rename from src/xrpld/app/rdb/Wallet.h rename to include/xrpl/server/Wallet.h index 141ef53f27..dcfbada8eb 100644 --- a/src/xrpld/app/rdb/Wallet.h +++ b/include/xrpl/server/Wallet.h @@ -1,9 +1,8 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include namespace xrpl { diff --git a/src/libxrpl/rdb/DatabaseCon.cpp b/src/libxrpl/rdb/DatabaseCon.cpp new file mode 100644 index 0000000000..344df85b4a --- /dev/null +++ b/src/libxrpl/rdb/DatabaseCon.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace xrpl { + +class CheckpointersCollection +{ + std::uintptr_t nextId_{0}; + // Mutex protects the CheckpointersCollection + std::mutex mutex_; + // Each checkpointer is given a unique id. All the checkpointers that are + // part of a DatabaseCon are part of this collection. When the DatabaseCon + // is destroyed, its checkpointer is removed from the collection + std::unordered_map> checkpointers_; + +public: + std::shared_ptr + fromId(std::uintptr_t id) + { + std::lock_guard l{mutex_}; + auto it = checkpointers_.find(id); + if (it != checkpointers_.end()) + return it->second; + return {}; + } + + void + erase(std::uintptr_t id) + { + std::lock_guard lock{mutex_}; + checkpointers_.erase(id); + } + + std::shared_ptr + create(std::shared_ptr const& session, JobQueue& jobQueue, Logs& logs) + { + std::lock_guard lock{mutex_}; + auto const id = nextId_++; + auto const r = makeCheckpointer(id, session, jobQueue, logs); + checkpointers_[id] = r; + return r; + } +}; + +CheckpointersCollection checkpointers; + +std::shared_ptr +checkpointerFromId(std::uintptr_t id) +{ + return checkpointers.fromId(id); +} + +DatabaseCon::~DatabaseCon() +{ + if (checkpointer_) + { + checkpointers.erase(checkpointer_->id()); + + std::weak_ptr wk(checkpointer_); + checkpointer_.reset(); + + // The references to our Checkpointer held by 'checkpointer_' and + // 'checkpointers' have been removed, so if the use count is nonzero, a + // checkpoint is currently in progress. Wait for it to end, otherwise + // creating a new DatabaseCon to the same database may fail due to the + // database being locked by our (now old) Checkpointer. + while (wk.use_count()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } +} + +std::unique_ptr const> DatabaseCon::Setup::globalPragma; + +void +DatabaseCon::setupCheckpointing(JobQueue* q, Logs& l) +{ + if (!q) + Throw("No JobQueue"); + checkpointer_ = checkpointers.create(session_, *q, l); +} + +} // namespace xrpl diff --git a/src/xrpld/core/detail/SociDB.cpp b/src/libxrpl/rdb/SociDB.cpp similarity index 98% rename from src/xrpld/core/detail/SociDB.cpp rename to src/libxrpl/rdb/SociDB.cpp index ff2fa1d9c1..2f8c5f1ac6 100644 --- a/src/xrpld/core/detail/SociDB.cpp +++ b/src/libxrpl/rdb/SociDB.cpp @@ -3,12 +3,10 @@ #pragma clang diagnostic ignored "-Wdeprecated" #endif -#include -#include -#include - #include #include +#include +#include #include diff --git a/src/xrpld/app/rdb/detail/State.cpp b/src/libxrpl/server/State.cpp similarity index 98% rename from src/xrpld/app/rdb/detail/State.cpp rename to src/libxrpl/server/State.cpp index ad8944e54d..4e3a1584c2 100644 --- a/src/xrpld/app/rdb/detail/State.cpp +++ b/src/libxrpl/server/State.cpp @@ -1,4 +1,4 @@ -#include +#include namespace xrpl { diff --git a/src/xrpld/app/rdb/detail/Vacuum.cpp b/src/libxrpl/server/Vacuum.cpp similarity index 96% rename from src/xrpld/app/rdb/detail/Vacuum.cpp rename to src/libxrpl/server/Vacuum.cpp index 5aaa04f040..cb31c6fa7a 100644 --- a/src/xrpld/app/rdb/detail/Vacuum.cpp +++ b/src/libxrpl/server/Vacuum.cpp @@ -1,7 +1,9 @@ -#include +#include #include +#include + namespace xrpl { bool diff --git a/src/xrpld/app/rdb/detail/Wallet.cpp b/src/libxrpl/server/Wallet.cpp similarity index 99% rename from src/xrpld/app/rdb/detail/Wallet.cpp rename to src/libxrpl/server/Wallet.cpp index 88a5dcf985..51f1326674 100644 --- a/src/xrpld/app/rdb/detail/Wallet.cpp +++ b/src/libxrpl/server/Wallet.cpp @@ -1,4 +1,5 @@ -#include +#include +#include #include diff --git a/src/test/app/LedgerLoad_test.cpp b/src/test/app/LedgerLoad_test.cpp index f27edadd58..b4e84a3123 100644 --- a/src/test/app/LedgerLoad_test.cpp +++ b/src/test/app/LedgerLoad_test.cpp @@ -21,7 +21,7 @@ class LedgerLoad_test : public beast::unit_test::suite std::unique_ptr cfg, std::string const& dbPath, std::string const& ledger, - Config::StartUpType type, + StartUpType type, std::optional trapTxHash) { cfg->START_LEDGER = ledger; @@ -105,7 +105,7 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with the ledger file specified for startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, sd.ledgerFile, Config::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, sd.ledgerFile, StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -123,7 +123,7 @@ class LedgerLoad_test : public beast::unit_test::suite except([&] { Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "", Config::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "", StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -132,7 +132,7 @@ class LedgerLoad_test : public beast::unit_test::suite except([&] { Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "badfile.json", Config::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "badfile.json", StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -153,7 +153,7 @@ class LedgerLoad_test : public beast::unit_test::suite except([&] { Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerFileCorrupt.string(), Config::LOAD_FILE, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerFileCorrupt.string(), StartUpType::LOAD_FILE, std::nullopt), nullptr, beast::severities::kDisabled); }); @@ -170,7 +170,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::LOAD, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -189,7 +189,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, std::nullopt), nullptr, beast::severities::kDisabled); auto const jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -213,7 +213,7 @@ class LedgerLoad_test : public beast::unit_test::suite boost::erase_all(ledgerHash, "\""); Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, sd.trapTxHash), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, sd.trapTxHash), nullptr, beast::severities::kDisabled); auto const jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -241,7 +241,7 @@ class LedgerLoad_test : public beast::unit_test::suite // replay when trapTxHash is set to an invalid transaction Env env( *this, - envconfig(ledgerConfig, sd.dbPath, ledgerHash, Config::REPLAY, ~sd.trapTxHash), + envconfig(ledgerConfig, sd.dbPath, ledgerHash, StartUpType::REPLAY, ~sd.trapTxHash), nullptr, beast::severities::kDisabled); BEAST_EXPECT(false); @@ -265,7 +265,7 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with the ledger "latest" specified for startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "latest", Config::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "latest", StartUpType::LOAD, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; @@ -281,7 +281,7 @@ class LedgerLoad_test : public beast::unit_test::suite // create a new env with specific ledger index at startup Env env( *this, - envconfig(ledgerConfig, sd.dbPath, "43", Config::LOAD, std::nullopt), + envconfig(ledgerConfig, sd.dbPath, "43", StartUpType::LOAD, std::nullopt), nullptr, beast::severities::kDisabled); auto jrb = env.rpc("ledger", "current", "full")[jss::result]; diff --git a/src/test/app/Manifest_test.cpp b/src/test/app/Manifest_test.cpp index 598949f662..5b7e34ad5b 100644 --- a/src/test/app/Manifest_test.cpp +++ b/src/test/app/Manifest_test.cpp @@ -1,15 +1,15 @@ #include -#include -#include #include -#include #include #include #include #include #include +#include +#include +#include #include #include diff --git a/src/test/app/ValidatorKeys_test.cpp b/src/test/app/ValidatorKeys_test.cpp index fac8a4bb7e..c688b2661f 100644 --- a/src/test/app/ValidatorKeys_test.cpp +++ b/src/test/app/ValidatorKeys_test.cpp @@ -1,12 +1,12 @@ #include -#include #include #include #include #include #include +#include #include diff --git a/src/test/core/SociDB_test.cpp b/src/test/core/SociDB_test.cpp index 001022aa95..fe73d42b0a 100644 --- a/src/test/core/SociDB_test.cpp +++ b/src/test/core/SociDB_test.cpp @@ -1,9 +1,8 @@ #include -#include - #include #include +#include #include #include diff --git a/src/test/nodestore/Database_test.cpp b/src/test/nodestore/Database_test.cpp index 03f0c11990..1229923e7d 100644 --- a/src/test/nodestore/Database_test.cpp +++ b/src/test/nodestore/Database_test.cpp @@ -4,11 +4,10 @@ #include #include -#include - #include #include #include +#include namespace xrpl { diff --git a/src/test/rpc/LedgerEntry_test.cpp b/src/test/rpc/LedgerEntry_test.cpp index aa959629b1..5d12e7bb83 100644 --- a/src/test/rpc/LedgerEntry_test.cpp +++ b/src/test/rpc/LedgerEntry_test.cpp @@ -2050,7 +2050,7 @@ class LedgerEntry_test : public beast::unit_test::suite Account const bob{"bob"}; Env env{*this, envconfig([](auto cfg) { - cfg->START_UP = Config::FRESH; + cfg->START_UP = StartUpType::FRESH; return cfg; })}; @@ -2241,7 +2241,7 @@ class LedgerEntry_test : public beast::unit_test::suite Account const bob{"bob"}; Env env{*this, envconfig([](auto cfg) { - cfg->START_UP = Config::FRESH; + cfg->START_UP = StartUpType::FRESH; return cfg; })}; diff --git a/src/xrpld/app/ledger/Ledger.cpp b/src/xrpld/app/ledger/Ledger.cpp index 9ad08f9894..0f1b81d53d 100644 --- a/src/xrpld/app/ledger/Ledger.cpp +++ b/src/xrpld/app/ledger/Ledger.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 2c0d3c2b82..5f7a86e2c2 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -26,11 +25,8 @@ #include #include #include -#include #include -#include #include -#include #include #include #include @@ -40,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +46,9 @@ #include #include #include +#include #include +#include #include #include @@ -1021,6 +1020,12 @@ private: void setMaxDisallowedLedger(); + + Application& + app() override + { + return *this; + } }; //------------------------------------------------------------------------------ @@ -1116,18 +1121,21 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) auto const startUp = config_->START_UP; JLOG(m_journal.debug()) << "startUp: " << startUp; - if (startUp == Config::FRESH) + if (startUp == StartUpType::FRESH) { JLOG(m_journal.info()) << "Starting new Ledger"; startGenesisLedger(); } - else if (startUp == Config::LOAD || startUp == Config::LOAD_FILE || startUp == Config::REPLAY) + else if (startUp == StartUpType::LOAD || startUp == StartUpType::LOAD_FILE || startUp == StartUpType::REPLAY) { JLOG(m_journal.info()) << "Loading specified Ledger"; if (!loadOldLedger( - config_->START_LEDGER, startUp == Config::REPLAY, startUp == Config::LOAD_FILE, config_->TRAP_TX_HASH)) + config_->START_LEDGER, + startUp == StartUpType::REPLAY, + startUp == StartUpType::LOAD_FILE, + config_->TRAP_TX_HASH)) { JLOG(m_journal.error()) << "The specified ledger could not be loaded."; if (config_->FAST_LOAD) @@ -1142,7 +1150,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) } } } - else if (startUp == Config::NETWORK) + else if (startUp == StartUpType::NETWORK) { // This should probably become the default once we have a stable // network. @@ -1529,7 +1537,7 @@ void ApplicationImp::startGenesisLedger() { std::vector const initialAmendments = - (config_->START_UP == Config::FRESH) ? m_amendmentTable->getDesired() : std::vector{}; + (config_->START_UP == StartUpType::FRESH) ? m_amendmentTable->getDesired() : std::vector{}; std::shared_ptr const genesis = std::make_shared(create_genesis, *config_, initialAmendments, nodeFamily_); diff --git a/src/xrpld/app/main/Application.h b/src/xrpld/app/main/Application.h index 53cc264ad4..5ecc84c11c 100644 --- a/src/xrpld/app/main/Application.h +++ b/src/xrpld/app/main/Application.h @@ -1,10 +1,10 @@ #pragma once #include -#include #include #include +#include #include #include #include @@ -112,8 +112,6 @@ public: public: Application(); - virtual ~Application() = default; - virtual bool setup(boost::program_options::variables_map const& options) = 0; @@ -127,8 +125,6 @@ public: checkSigs() const = 0; virtual void checkSigs(bool) = 0; - virtual bool - isStopping() const = 0; // // --- @@ -138,14 +134,9 @@ public: virtual std::uint64_t instanceID() const = 0; - virtual Logs& - logs() = 0; virtual Config& config() = 0; - virtual boost::asio::io_context& - getIOContext() = 0; - virtual std::pair const& nodeIdentity() = 0; @@ -158,9 +149,6 @@ public: virtual bool serverOkay(std::string& reason) = 0; - virtual beast::Journal - journal(std::string const& name) = 0; - /* Returns the number of file descriptors the application needs */ virtual int fdRequired() const = 0; diff --git a/src/xrpld/app/main/Main.cpp b/src/xrpld/app/main/Main.cpp index aaf7af95ab..7bdccd12a7 100644 --- a/src/xrpld/app/main/Main.cpp +++ b/src/xrpld/app/main/Main.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include #include #include @@ -601,7 +601,7 @@ run(int argc, char** argv) if (vm.count("start")) { - config->START_UP = Config::FRESH; + config->START_UP = StartUpType::FRESH; } if (vm.count("import")) @@ -612,7 +612,7 @@ run(int argc, char** argv) config->START_LEDGER = vm["ledger"].as(); if (vm.count("replay")) { - config->START_UP = Config::REPLAY; + config->START_UP = StartUpType::REPLAY; if (vm.count("trap_tx_hash")) { uint256 tmp = {}; @@ -631,16 +631,16 @@ run(int argc, char** argv) } } else - config->START_UP = Config::LOAD; + config->START_UP = StartUpType::LOAD; } else if (vm.count("ledgerfile")) { config->START_LEDGER = vm["ledgerfile"].as(); - config->START_UP = Config::LOAD_FILE; + config->START_UP = StartUpType::LOAD_FILE; } else if (vm.count("load") || config->FAST_LOAD) { - config->START_UP = Config::LOAD; + config->START_UP = StartUpType::LOAD; } if (vm.count("trap_tx_hash") && vm.count("replay") == 0) @@ -651,13 +651,13 @@ run(int argc, char** argv) if (vm.count("net") && !config->FAST_LOAD) { - if ((config->START_UP == Config::LOAD) || (config->START_UP == Config::REPLAY)) + if ((config->START_UP == StartUpType::LOAD) || (config->START_UP == StartUpType::REPLAY)) { std::cerr << "Net and load/replay options are incompatible" << std::endl; return -1; } - config->START_UP = Config::NETWORK; + config->START_UP = StartUpType::NETWORK; } if (vm.count("valid")) diff --git a/src/xrpld/app/main/NodeIdentity.cpp b/src/xrpld/app/main/NodeIdentity.cpp index b585b80b5b..3019caeb31 100644 --- a/src/xrpld/app/main/NodeIdentity.cpp +++ b/src/xrpld/app/main/NodeIdentity.cpp @@ -1,9 +1,10 @@ #include #include -#include #include #include +#include + namespace xrpl { std::pair diff --git a/src/xrpld/app/misc/SHAMapStoreImp.cpp b/src/xrpld/app/misc/SHAMapStoreImp.cpp index dbdd682ef8..7f276ca2d8 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.cpp +++ b/src/xrpld/app/misc/SHAMapStoreImp.cpp @@ -1,13 +1,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include diff --git a/src/xrpld/app/misc/SHAMapStoreImp.h b/src/xrpld/app/misc/SHAMapStoreImp.h index b046a78979..df3c16b24f 100644 --- a/src/xrpld/app/misc/SHAMapStoreImp.h +++ b/src/xrpld/app/misc/SHAMapStoreImp.h @@ -2,11 +2,11 @@ #include #include -#include -#include #include #include +#include +#include #include #include diff --git a/src/xrpld/app/misc/ValidatorList.h b/src/xrpld/app/misc/ValidatorList.h index 4fd610be04..f93ac8a32f 100644 --- a/src/xrpld/app/misc/ValidatorList.h +++ b/src/xrpld/app/misc/ValidatorList.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include diff --git a/src/xrpld/app/misc/detail/AmendmentTable.cpp b/src/xrpld/app/misc/detail/AmendmentTable.cpp index 2942c8bde6..3addfd2235 100644 --- a/src/xrpld/app/misc/detail/AmendmentTable.cpp +++ b/src/xrpld/app/misc/detail/AmendmentTable.cpp @@ -1,12 +1,12 @@ #include #include -#include #include #include #include #include #include +#include #include #include diff --git a/src/xrpld/app/misc/detail/Manifest.cpp b/src/xrpld/app/misc/detail/Manifest.cpp index 952814656b..dfcbdbb3ad 100644 --- a/src/xrpld/app/misc/detail/Manifest.cpp +++ b/src/xrpld/app/misc/detail/Manifest.cpp @@ -1,13 +1,12 @@ -#include -#include -#include - #include #include #include #include #include #include +#include +#include +#include #include diff --git a/src/xrpld/app/misc/detail/ValidatorKeys.cpp b/src/xrpld/app/misc/detail/ValidatorKeys.cpp index 675ce4ac6f..8f24f14b40 100644 --- a/src/xrpld/app/misc/detail/ValidatorKeys.cpp +++ b/src/xrpld/app/misc/detail/ValidatorKeys.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include +#include namespace xrpl { ValidatorKeys::ValidatorKeys(Config const& config, beast::Journal j) diff --git a/src/xrpld/app/rdb/PeerFinder.h b/src/xrpld/app/rdb/PeerFinder.h index 2b4080255f..e5ac6dda8c 100644 --- a/src/xrpld/app/rdb/PeerFinder.h +++ b/src/xrpld/app/rdb/PeerFinder.h @@ -1,9 +1,10 @@ #pragma once #include -#include #include +#include + namespace xrpl { /** diff --git a/src/xrpld/app/rdb/RelationalDatabase.h b/src/xrpld/app/rdb/RelationalDatabase.h index c0cc61f757..078b8fe8db 100644 --- a/src/xrpld/app/rdb/RelationalDatabase.h +++ b/src/xrpld/app/rdb/RelationalDatabase.h @@ -1,13 +1,13 @@ #pragma once #include -#include #include #include -#include #include #include +#include +#include #include #include @@ -93,13 +93,13 @@ public: /** * @brief init Creates and returns an appropriate RelationalDatabase * instance based on configuration. - * @param app Application object. + * @param registry The service registry. * @param config Config object. * @param jobQueue JobQueue object. * @return Unique pointer to the interface. */ static std::unique_ptr - init(Application& app, Config const& config, JobQueue& jobQueue); + init(ServiceRegistry& registry, Config const& config, JobQueue& jobQueue); virtual ~RelationalDatabase() = default; diff --git a/src/xrpld/app/rdb/backend/detail/Node.cpp b/src/xrpld/app/rdb/backend/detail/Node.cpp index 90c95f3a2d..1e814c3589 100644 --- a/src/xrpld/app/rdb/backend/detail/Node.cpp +++ b/src/xrpld/app/rdb/backend/detail/Node.cpp @@ -5,12 +5,12 @@ #include #include #include -#include -#include #include #include #include +#include +#include #include @@ -64,8 +64,8 @@ makeLedgerDBs( tx->getSession() << boost::str( boost::format("PRAGMA cache_size=-%d;") % kilobytes(config.getValueFor(SizedItem::txnDBCache))); - if (!setup.standAlone || setup.startUp == Config::LOAD || setup.startUp == Config::LOAD_FILE || - setup.startUp == Config::REPLAY) + if (!setup.standAlone || setup.startUp == StartUpType::LOAD || setup.startUp == StartUpType::LOAD_FILE || + setup.startUp == StartUpType::REPLAY) { // Check if AccountTransactions has primary key std::string cid, name, type; diff --git a/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp b/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp index d65b12dc7f..4f1430ee4c 100644 --- a/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp +++ b/src/xrpld/app/rdb/backend/detail/SQLiteDatabase.cpp @@ -3,21 +3,21 @@ #include #include #include -#include -#include #include +#include +#include namespace xrpl { class SQLiteDatabaseImp final : public SQLiteDatabase { public: - SQLiteDatabaseImp(Application& app, Config const& config, JobQueue& jobQueue) - : app_(app), useTxTables_(config.useTxTables()), j_(app_.journal("SQLiteDatabaseImp")) + SQLiteDatabaseImp(ServiceRegistry& registry, Config const& config, JobQueue& jobQueue) + : registry_(registry), useTxTables_(config.useTxTables()), j_(registry.journal("SQLiteDatabaseImp")) { DatabaseCon::Setup const setup = setup_DatabaseCon(config, j_); - if (!makeLedgerDBs(config, setup, DatabaseCon::CheckpointerSetup{&jobQueue, &app_.logs()})) + if (!makeLedgerDBs(config, setup, DatabaseCon::CheckpointerSetup{&jobQueue, ®istry_.logs()})) { std::string_view constexpr error = "Failed to create ledger databases"; @@ -139,7 +139,7 @@ public: closeTransactionDB() override; private: - Application& app_; + ServiceRegistry& registry_; bool const useTxTables_; beast::Journal j_; std::unique_ptr ledgerDb_, txdb_; @@ -370,7 +370,7 @@ SQLiteDatabaseImp::saveValidatedLedger(std::shared_ptr const& ledg { if (existsLedger()) { - if (!detail::saveValidatedLedger(*ledgerDb_, txdb_, app_, ledger, current)) + if (!detail::saveValidatedLedger(*ledgerDb_, txdb_, registry_.app(), ledger, current)) return false; } @@ -506,7 +506,7 @@ SQLiteDatabaseImp::getTxHistory(LedgerIndex startIndex) if (existsTransaction()) { auto db = checkoutTransaction(); - auto const res = detail::getTxHistory(*db, app_, startIndex, 20).first; + auto const res = detail::getTxHistory(*db, registry_.app(), startIndex, 20).first; if (!res.empty()) return res; @@ -521,12 +521,12 @@ SQLiteDatabaseImp::getOldestAccountTxs(AccountTxOptions const& options) if (!useTxTables_) return {}; - LedgerMaster& ledgerMaster = app_.getLedgerMaster(); + LedgerMaster& ledgerMaster = registry_.getLedgerMaster(); if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getOldestAccountTxs(*db, app_, ledgerMaster, options, j_).first; + return detail::getOldestAccountTxs(*db, registry_.app(), ledgerMaster, options, j_).first; } return {}; @@ -538,12 +538,12 @@ SQLiteDatabaseImp::getNewestAccountTxs(AccountTxOptions const& options) if (!useTxTables_) return {}; - LedgerMaster& ledgerMaster = app_.getLedgerMaster(); + LedgerMaster& ledgerMaster = registry_.getLedgerMaster(); if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getNewestAccountTxs(*db, app_, ledgerMaster, options, j_).first; + return detail::getNewestAccountTxs(*db, registry_.app(), ledgerMaster, options, j_).first; } return {}; @@ -558,7 +558,7 @@ SQLiteDatabaseImp::getOldestAccountTxsB(AccountTxOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getOldestAccountTxsB(*db, app_, options, j_).first; + return detail::getOldestAccountTxsB(*db, registry_.app(), options, j_).first; } return {}; @@ -573,7 +573,7 @@ SQLiteDatabaseImp::getNewestAccountTxsB(AccountTxOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getNewestAccountTxsB(*db, app_, options, j_).first; + return detail::getNewestAccountTxsB(*db, registry_.app(), options, j_).first; } return {}; @@ -586,10 +586,9 @@ SQLiteDatabaseImp::oldestAccountTxPage(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(200); - auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); + auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); AccountTxs ret; - Application& app = app_; - auto onTransaction = [&ret, &app]( + auto onTransaction = [&ret, &app = registry_.app()]( std::uint32_t ledger_index, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); }; @@ -611,10 +610,9 @@ SQLiteDatabaseImp::newestAccountTxPage(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(200); - auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); + auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); AccountTxs ret; - Application& app = app_; - auto onTransaction = [&ret, &app]( + auto onTransaction = [&ret, &app = registry_.app()]( std::uint32_t ledger_index, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { convertBlobsToTxResult(ret, ledger_index, status, rawTxn, rawMeta, app); }; @@ -636,7 +634,7 @@ SQLiteDatabaseImp::oldestAccountTxPageB(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(500); - auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); + auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); MetaTxsList ret; auto onTransaction = [&ret](std::uint32_t ledgerIndex, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { ret.emplace_back(std::move(rawTxn), std::move(rawMeta), ledgerIndex); @@ -659,7 +657,7 @@ SQLiteDatabaseImp::newestAccountTxPageB(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(500); - auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); + auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(registry_.app()), std::placeholders::_1); MetaTxsList ret; auto onTransaction = [&ret](std::uint32_t ledgerIndex, std::string const& status, Blob&& rawTxn, Blob&& rawMeta) { ret.emplace_back(std::move(rawTxn), std::move(rawMeta), ledgerIndex); @@ -687,7 +685,7 @@ SQLiteDatabaseImp::getTransaction( if (existsTransaction()) { auto db = checkoutTransaction(); - return detail::getTransaction(*db, app_, id, range, ec); + return detail::getTransaction(*db, registry_.app(), id, range, ec); } return TxSearched::unknown; @@ -769,9 +767,9 @@ SQLiteDatabaseImp::closeTransactionDB() } std::unique_ptr -getSQLiteDatabase(Application& app, Config const& config, JobQueue& jobQueue) +getSQLiteDatabase(ServiceRegistry& registry, Config const& config, JobQueue& jobQueue) { - return std::make_unique(app, config, jobQueue); + return std::make_unique(registry, config, jobQueue); } } // namespace xrpl diff --git a/src/xrpld/app/rdb/detail/RelationalDatabase.cpp b/src/xrpld/app/rdb/detail/RelationalDatabase.cpp index 2ceb15d1e7..bc65a817a4 100644 --- a/src/xrpld/app/rdb/detail/RelationalDatabase.cpp +++ b/src/xrpld/app/rdb/detail/RelationalDatabase.cpp @@ -1,14 +1,13 @@ -#include #include #include namespace xrpl { extern std::unique_ptr -getSQLiteDatabase(Application& app, Config const& config, JobQueue& jobQueue); +getSQLiteDatabase(ServiceRegistry& registry, Config const& config, JobQueue& jobQueue); std::unique_ptr -RelationalDatabase::init(Application& app, Config const& config, JobQueue& jobQueue) +RelationalDatabase::init(ServiceRegistry& registry, Config const& config, JobQueue& jobQueue) { bool use_sqlite = false; @@ -31,7 +30,7 @@ RelationalDatabase::init(Application& app, Config const& config, JobQueue& jobQu if (use_sqlite) { - return getSQLiteDatabase(app, config, jobQueue); + return getSQLiteDatabase(registry, config, jobQueue); } return std::unique_ptr(); diff --git a/src/xrpld/core/Config.h b/src/xrpld/core/Config.h index 86b663a212..c40d13c83a 100644 --- a/src/xrpld/core/Config.h +++ b/src/xrpld/core/Config.h @@ -4,7 +4,9 @@ #include #include #include +#include #include // VFALCO Breaks levelization +#include #include // VFALCO FIX: This include should not be here @@ -124,8 +126,7 @@ public: // Entries from [ips_fixed] config stanza std::vector IPS_FIXED; - enum StartUpType { FRESH, NORMAL, LOAD, LOAD_FILE, REPLAY, NETWORK }; - StartUpType START_UP = NORMAL; + StartUpType START_UP = StartUpType::NORMAL; bool START_VALID = false; @@ -355,4 +356,7 @@ public: FeeSetup setup_FeeVote(Section const& section); +DatabaseCon::Setup +setup_DatabaseCon(Config const& c, std::optional j = std::nullopt); + } // namespace xrpl diff --git a/src/xrpld/core/detail/Config.cpp b/src/xrpld/core/detail/Config.cpp index 0a60416af7..4a2bae7c27 100644 --- a/src/xrpld/core/detail/Config.cpp +++ b/src/xrpld/core/detail/Config.cpp @@ -1039,4 +1039,150 @@ setup_FeeVote(Section const& section) return setup; } +DatabaseCon::Setup +setup_DatabaseCon(Config const& c, std::optional j) +{ + DatabaseCon::Setup setup; + + setup.startUp = c.START_UP; + setup.standAlone = c.standalone(); + setup.dataDir = c.legacy("database_path"); + if (!setup.standAlone && setup.dataDir.empty()) + { + Throw("database_path must be set."); + } + + if (!setup.globalPragma) + { + auto const& sqlite = c.section("sqlite"); + auto result = std::make_unique>(); + result->reserve(3); + + // defaults + std::string safety_level; + std::string journal_mode = "wal"; + std::string synchronous = "normal"; + std::string temp_store = "file"; + bool showRiskWarning = false; + + if (set(safety_level, "safety_level", sqlite)) + { + if (boost::iequals(safety_level, "low")) + { + // low safety defaults + journal_mode = "memory"; + synchronous = "off"; + temp_store = "memory"; + showRiskWarning = true; + } + else if (!boost::iequals(safety_level, "high")) + { + Throw("Invalid safety_level value: " + safety_level); + } + } + + { + // #journal_mode Valid values : delete, truncate, persist, + // memory, wal, off + if (set(journal_mode, "journal_mode", sqlite) && !safety_level.empty()) + { + Throw( + "Configuration file may not define both " + "\"safety_level\" and \"journal_mode\""); + } + bool higherRisk = boost::iequals(journal_mode, "memory") || boost::iequals(journal_mode, "off"); + showRiskWarning = showRiskWarning || higherRisk; + if (higherRisk || boost::iequals(journal_mode, "delete") || boost::iequals(journal_mode, "truncate") || + boost::iequals(journal_mode, "persist") || boost::iequals(journal_mode, "wal")) + { + result->emplace_back(boost::str(boost::format(CommonDBPragmaJournal) % journal_mode)); + } + else + { + Throw("Invalid journal_mode value: " + journal_mode); + } + } + + { + // #synchronous Valid values : off, normal, full, extra + if (set(synchronous, "synchronous", sqlite) && !safety_level.empty()) + { + Throw( + "Configuration file may not define both " + "\"safety_level\" and \"synchronous\""); + } + bool higherRisk = boost::iequals(synchronous, "off"); + showRiskWarning = showRiskWarning || higherRisk; + if (higherRisk || boost::iequals(synchronous, "normal") || boost::iequals(synchronous, "full") || + boost::iequals(synchronous, "extra")) + { + result->emplace_back(boost::str(boost::format(CommonDBPragmaSync) % synchronous)); + } + else + { + Throw("Invalid synchronous value: " + synchronous); + } + } + + { + // #temp_store Valid values : default, file, memory + if (set(temp_store, "temp_store", sqlite) && !safety_level.empty()) + { + Throw( + "Configuration file may not define both " + "\"safety_level\" and \"temp_store\""); + } + bool higherRisk = boost::iequals(temp_store, "memory"); + showRiskWarning = showRiskWarning || higherRisk; + if (higherRisk || boost::iequals(temp_store, "default") || boost::iequals(temp_store, "file")) + { + result->emplace_back(boost::str(boost::format(CommonDBPragmaTemp) % temp_store)); + } + else + { + Throw("Invalid temp_store value: " + temp_store); + } + } + + if (showRiskWarning && j && c.LEDGER_HISTORY > SQLITE_TUNING_CUTOFF) + { + JLOG(j->warn()) << "reducing the data integrity guarantees from the " + "default [sqlite] behavior is not recommended for " + "nodes storing large amounts of history, because of the " + "difficulty inherent in rebuilding corrupted data."; + } + XRPL_ASSERT(result->size() == 3, "xrpl::setup_DatabaseCon::globalPragma : result size is 3"); + setup.globalPragma = std::move(result); + } + setup.useGlobalPragma = true; + + auto setPragma = [](std::string& pragma, std::string const& key, int64_t value) { + pragma = "PRAGMA " + key + "=" + std::to_string(value) + ";"; + }; + + // Lgr Pragma + setPragma(setup.lgrPragma[0], "journal_size_limit", 1582080); + + // TX Pragma + int64_t page_size = 4096; + int64_t journal_size_limit = 1582080; + if (c.exists("sqlite")) + { + auto& s = c.section("sqlite"); + set(journal_size_limit, "journal_size_limit", s); + set(page_size, "page_size", s); + if (page_size < 512 || page_size > 65536) + Throw("Invalid page_size. Must be between 512 and 65536."); + + if (page_size & (page_size - 1)) + Throw("Invalid page_size. Must be a power of 2."); + } + + setPragma(setup.txPragma[0], "page_size", page_size); + setPragma(setup.txPragma[1], "journal_size_limit", journal_size_limit); + setPragma(setup.txPragma[2], "max_page_count", 4294967294); + setPragma(setup.txPragma[3], "mmap_size", 17179869184); + + return setup; +} } // namespace xrpl diff --git a/src/xrpld/core/detail/DatabaseCon.cpp b/src/xrpld/core/detail/DatabaseCon.cpp deleted file mode 100644 index 64f87f7a39..0000000000 --- a/src/xrpld/core/detail/DatabaseCon.cpp +++ /dev/null @@ -1,242 +0,0 @@ -#include -#include - -#include -#include - -#include -#include - -#include -#include - -namespace xrpl { - -class CheckpointersCollection -{ - std::uintptr_t nextId_{0}; - // Mutex protects the CheckpointersCollection - std::mutex mutex_; - // Each checkpointer is given a unique id. All the checkpointers that are - // part of a DatabaseCon are part of this collection. When the DatabaseCon - // is destroyed, its checkpointer is removed from the collection - std::unordered_map> checkpointers_; - -public: - std::shared_ptr - fromId(std::uintptr_t id) - { - std::lock_guard l{mutex_}; - auto it = checkpointers_.find(id); - if (it != checkpointers_.end()) - return it->second; - return {}; - } - - void - erase(std::uintptr_t id) - { - std::lock_guard lock{mutex_}; - checkpointers_.erase(id); - } - - std::shared_ptr - create(std::shared_ptr const& session, JobQueue& jobQueue, Logs& logs) - { - std::lock_guard lock{mutex_}; - auto const id = nextId_++; - auto const r = makeCheckpointer(id, session, jobQueue, logs); - checkpointers_[id] = r; - return r; - } -}; - -CheckpointersCollection checkpointers; - -std::shared_ptr -checkpointerFromId(std::uintptr_t id) -{ - return checkpointers.fromId(id); -} - -DatabaseCon::~DatabaseCon() -{ - if (checkpointer_) - { - checkpointers.erase(checkpointer_->id()); - - std::weak_ptr wk(checkpointer_); - checkpointer_.reset(); - - // The references to our Checkpointer held by 'checkpointer_' and - // 'checkpointers' have been removed, so if the use count is nonzero, a - // checkpoint is currently in progress. Wait for it to end, otherwise - // creating a new DatabaseCon to the same database may fail due to the - // database being locked by our (now old) Checkpointer. - while (wk.use_count()) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } -} - -DatabaseCon::Setup -setup_DatabaseCon(Config const& c, std::optional j) -{ - DatabaseCon::Setup setup; - - setup.startUp = c.START_UP; - setup.standAlone = c.standalone(); - setup.dataDir = c.legacy("database_path"); - if (!setup.standAlone && setup.dataDir.empty()) - { - Throw("database_path must be set."); - } - - if (!setup.globalPragma) - { - setup.globalPragma = [&c, &j]() { - auto const& sqlite = c.section("sqlite"); - auto result = std::make_unique>(); - result->reserve(3); - - // defaults - std::string safety_level; - std::string journal_mode = "wal"; - std::string synchronous = "normal"; - std::string temp_store = "file"; - bool showRiskWarning = false; - - if (set(safety_level, "safety_level", sqlite)) - { - if (boost::iequals(safety_level, "low")) - { - // low safety defaults - journal_mode = "memory"; - synchronous = "off"; - temp_store = "memory"; - showRiskWarning = true; - } - else if (!boost::iequals(safety_level, "high")) - { - Throw("Invalid safety_level value: " + safety_level); - } - } - - { - // #journal_mode Valid values : delete, truncate, persist, - // memory, wal, off - if (set(journal_mode, "journal_mode", sqlite) && !safety_level.empty()) - { - Throw( - "Configuration file may not define both " - "\"safety_level\" and \"journal_mode\""); - } - bool higherRisk = boost::iequals(journal_mode, "memory") || boost::iequals(journal_mode, "off"); - showRiskWarning = showRiskWarning || higherRisk; - if (higherRisk || boost::iequals(journal_mode, "delete") || boost::iequals(journal_mode, "truncate") || - boost::iequals(journal_mode, "persist") || boost::iequals(journal_mode, "wal")) - { - result->emplace_back(boost::str(boost::format(CommonDBPragmaJournal) % journal_mode)); - } - else - { - Throw("Invalid journal_mode value: " + journal_mode); - } - } - - { - // #synchronous Valid values : off, normal, full, extra - if (set(synchronous, "synchronous", sqlite) && !safety_level.empty()) - { - Throw( - "Configuration file may not define both " - "\"safety_level\" and \"synchronous\""); - } - bool higherRisk = boost::iequals(synchronous, "off"); - showRiskWarning = showRiskWarning || higherRisk; - if (higherRisk || boost::iequals(synchronous, "normal") || boost::iequals(synchronous, "full") || - boost::iequals(synchronous, "extra")) - { - result->emplace_back(boost::str(boost::format(CommonDBPragmaSync) % synchronous)); - } - else - { - Throw("Invalid synchronous value: " + synchronous); - } - } - - { - // #temp_store Valid values : default, file, memory - if (set(temp_store, "temp_store", sqlite) && !safety_level.empty()) - { - Throw( - "Configuration file may not define both " - "\"safety_level\" and \"temp_store\""); - } - bool higherRisk = boost::iequals(temp_store, "memory"); - showRiskWarning = showRiskWarning || higherRisk; - if (higherRisk || boost::iequals(temp_store, "default") || boost::iequals(temp_store, "file")) - { - result->emplace_back(boost::str(boost::format(CommonDBPragmaTemp) % temp_store)); - } - else - { - Throw("Invalid temp_store value: " + temp_store); - } - } - - if (showRiskWarning && j && c.LEDGER_HISTORY > SQLITE_TUNING_CUTOFF) - { - JLOG(j->warn()) << "reducing the data integrity guarantees from the " - "default [sqlite] behavior is not recommended for " - "nodes storing large amounts of history, because of the " - "difficulty inherent in rebuilding corrupted data."; - } - XRPL_ASSERT(result->size() == 3, "xrpl::setup_DatabaseCon::globalPragma : result size is 3"); - return result; - }(); - } - setup.useGlobalPragma = true; - - auto setPragma = [](std::string& pragma, std::string const& key, int64_t value) { - pragma = "PRAGMA " + key + "=" + std::to_string(value) + ";"; - }; - - // Lgr Pragma - setPragma(setup.lgrPragma[0], "journal_size_limit", 1582080); - - // TX Pragma - int64_t page_size = 4096; - int64_t journal_size_limit = 1582080; - if (c.exists("sqlite")) - { - auto& s = c.section("sqlite"); - set(journal_size_limit, "journal_size_limit", s); - set(page_size, "page_size", s); - if (page_size < 512 || page_size > 65536) - Throw("Invalid page_size. Must be between 512 and 65536."); - - if (page_size & (page_size - 1)) - Throw("Invalid page_size. Must be a power of 2."); - } - - setPragma(setup.txPragma[0], "page_size", page_size); - setPragma(setup.txPragma[1], "journal_size_limit", journal_size_limit); - setPragma(setup.txPragma[2], "max_page_count", 4294967294); - setPragma(setup.txPragma[3], "mmap_size", 17179869184); - - return setup; -} - -std::unique_ptr const> DatabaseCon::Setup::globalPragma; - -void -DatabaseCon::setupCheckpointing(JobQueue* q, Logs& l) -{ - if (!q) - Throw("No JobQueue"); - checkpointer_ = checkpointers.create(session_, *q, l); -} - -} // namespace xrpl diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index 350631b8e6..6ac6e454d2 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -20,6 +19,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/overlay/detail/PeerReservationTable.cpp b/src/xrpld/overlay/detail/PeerReservationTable.cpp index 1e3452ca17..27d9df1129 100644 --- a/src/xrpld/overlay/detail/PeerReservationTable.cpp +++ b/src/xrpld/overlay/detail/PeerReservationTable.cpp @@ -1,10 +1,10 @@ #include -#include -#include +#include #include #include #include +#include #include #include diff --git a/src/xrpld/peerfinder/detail/StoreSqdb.h b/src/xrpld/peerfinder/detail/StoreSqdb.h index f5461d489a..b945ae970b 100644 --- a/src/xrpld/peerfinder/detail/StoreSqdb.h +++ b/src/xrpld/peerfinder/detail/StoreSqdb.h @@ -1,9 +1,10 @@ #pragma once #include -#include #include +#include + namespace xrpl { namespace PeerFinder { diff --git a/src/xrpld/rpc/InfoSub.h b/src/xrpld/rpc/InfoSub.h index 7d4d4f06c8..d49e401bd3 100644 --- a/src/xrpld/rpc/InfoSub.h +++ b/src/xrpld/rpc/InfoSub.h @@ -1,12 +1,11 @@ #pragma once -#include - #include #include #include #include #include +#include namespace xrpl {