diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 764635d9279..10a1465192a 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -13,8 +13,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: write - container: - image: docker://rippleci/rippled-ci-builder:2944b78d22db + container: rippleci/rippled-build-ubuntu:aaf5e3e steps: - name: checkout uses: actions/checkout@v4 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 99aa6b7a9df..566fae55c44 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -23,6 +23,10 @@ jobs: steps: - name: checkout uses: actions/checkout@v4 + - name: install Conan + run: | + brew install conan@1 + echo '/opt/homebrew/opt/conan@1/bin' >> $GITHUB_PATH - name: install Ninja if: matrix.generator == 'Ninja' run: brew install ninja @@ -35,7 +39,8 @@ jobs: cmake --version - name: configure Conan run : | - conan profile get env.CXXFLAGS default || true + conan profile new default --detect || true + conan profile update settings.compiler.cppstd=20 default conan profile update 'conf.tools.build:cxxflags+=["-DBOOST_ASIO_DISABLE_CONCEPTS"]' default - name: build dependencies uses: ./.github/actions/dependencies diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f103a40d7f7..6aa2bbe706b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -56,6 +56,7 @@ jobs: shell: bash run: | conan profile new default --detect + conan profile update settings.compiler.cppstd=20 default conan profile update settings.compiler.runtime=MT${{ matrix.configuration == 'Debug' && 'd' || '' }} default - name: build dependencies uses: ./.github/actions/dependencies diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 79de9b23041..2ba2afa727d 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -416,7 +416,6 @@ # # The default list of entries is: # - r.ripple.com 51235 -# - zaphod.alloy.ee 51235 # - sahyadri.isrdc.in 51235 # # Examples: diff --git a/cfg/rippled-reporting.cfg b/cfg/rippled-reporting.cfg index 6ef10df5bf8..290bcc5418a 100644 --- a/cfg/rippled-reporting.cfg +++ b/cfg/rippled-reporting.cfg @@ -388,7 +388,6 @@ # # The default list of entries is: # - r.ripple.com 51235 -# - zaphod.alloy.ee 51235 # - sahyadri.isrdc.in 51235 # # Examples: diff --git a/src/ripple/app/rdb/backend/detail/impl/Node.cpp b/src/ripple/app/rdb/backend/detail/impl/Node.cpp index 8948360a3ad..0905d6121ae 100644 --- a/src/ripple/app/rdb/backend/detail/impl/Node.cpp +++ b/src/ripple/app/rdb/backend/detail/impl/Node.cpp @@ -1125,7 +1125,7 @@ accountTxPage( { sql = boost::str( boost::format( - prefix + (R"(AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u' + prefix + (R"(AccountTransactions.LedgerSeq BETWEEN %u AND %u ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TxnSeq %s LIMIT %u;)")) % @@ -1148,12 +1148,14 @@ accountTxPage( FROM AccountTransactions, Transactions WHERE (AccountTransactions.TransID = Transactions.TransID AND AccountTransactions.Account = '%s' AND - AccountTransactions.LedgerSeq BETWEEN '%u' AND '%u') - OR + AccountTransactions.LedgerSeq BETWEEN %u AND %u) + UNION + SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq,Status,RawTxn,TxnMeta + FROM AccountTransactions, Transactions WHERE (AccountTransactions.TransID = Transactions.TransID AND AccountTransactions.Account = '%s' AND - AccountTransactions.LedgerSeq = '%u' AND - AccountTransactions.TxnSeq %s '%u') + AccountTransactions.LedgerSeq = %u AND + AccountTransactions.TxnSeq %s %u) ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TxnSeq %s LIMIT %u; diff --git a/src/ripple/app/tx/impl/DID.cpp b/src/ripple/app/tx/impl/DID.cpp index c92162c8306..1cad8992ad9 100644 --- a/src/ripple/app/tx/impl/DID.cpp +++ b/src/ripple/app/tx/impl/DID.cpp @@ -161,6 +161,13 @@ DIDSet::doApply() set(sfURI); set(sfDIDDocument); set(sfData); + if (ctx_.view().rules().enabled(fixEmptyDID) && + !sleDID->isFieldPresent(sfURI) && + !sleDID->isFieldPresent(sfDIDDocument) && + !sleDID->isFieldPresent(sfData)) + { + return tecEMPTY_DID; + } return addSLE(ctx_, sleDID, account_); } diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 6b047b7ef81..1bb9a381edd 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -491,9 +491,6 @@ OverlayImpl::start() // Pool of servers operated by Ripple Labs Inc. - https://ripple.com bootstrapIps.push_back("r.ripple.com 51235"); - // Pool of servers operated by Alloy Networks - https://www.alloy.ee - bootstrapIps.push_back("zaphod.alloy.ee 51235"); - // Pool of servers operated by ISRDC - https://isrdc.in bootstrapIps.push_back("sahyadri.isrdc.in 51235"); } diff --git a/src/ripple/peerfinder/impl/Logic.h b/src/ripple/peerfinder/impl/Logic.h index dc325cc480d..7a2b6a7543a 100644 --- a/src/ripple/peerfinder/impl/Logic.h +++ b/src/ripple/peerfinder/impl/Logic.h @@ -273,7 +273,7 @@ class Logic std::lock_guard _(lock_); - // Check for duplicate connection + // Check for connection limit per address if (is_public(remote_endpoint)) { auto const count = @@ -287,6 +287,15 @@ class Logic } } + // Check for duplicate connection + if (slots_.find(remote_endpoint) != slots_.end()) + { + JLOG(m_journal.debug()) + << beast::leftw(18) << "Logic dropping " << remote_endpoint + << " as duplicate incoming"; + return SlotImp::ptr(); + } + // Create the slot SlotImp::ptr const slot(std::make_shared( local_endpoint, diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 785a59bb9e5..08f8b690504 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 = 69; +static constexpr std::size_t numFeatures = 70; /** Amendments that this server supports and the default voting behavior. Whether they are enabled depends on the Rules defined in the validated @@ -355,6 +355,7 @@ extern uint256 const fixFillOrKill; extern uint256 const fixNFTokenReserve; extern uint256 const fixInnerObjTemplate; extern uint256 const featurePriceOracle; +extern uint256 const fixEmptyDID; extern uint256 const fixPreviousTxnID; } // namespace ripple diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 66426a5f125..abeb5750029 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -462,6 +462,7 @@ REGISTER_FIX (fixFillOrKill, Supported::yes, VoteBehavior::De REGISTER_FIX (fixNFTokenReserve, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixInnerObjTemplate, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FEATURE(PriceOracle, Supported::yes, VoteBehavior::DefaultNo); +REGISTER_FIX (fixEmptyDID, Supported::yes, VoteBehavior::DefaultNo); REGISTER_FIX (fixPreviousTxnID, Supported::yes, VoteBehavior::DefaultNo); // The following amendments are obsolete, but must remain supported diff --git a/src/test/app/DID_test.cpp b/src/test/app/DID_test.cpp index 3aa27978bfe..82e77a20264 100644 --- a/src/test/app/DID_test.cpp +++ b/src/test/app/DID_test.cpp @@ -31,7 +31,7 @@ namespace ripple { namespace test { // Helper function that returns the owner count of an account root. -std::uint32_t +static std::uint32_t ownerCount(test::jtx::Env const& env, test::jtx::Account const& acct) { std::uint32_t ret{0}; @@ -130,7 +130,7 @@ struct DID_test : public beast::unit_test::suite using namespace jtx; using namespace std::chrono; - Env env(*this); + Env env{*this, features}; Account const alice{"alice"}; env.fund(XRP(5000), alice); env.close(); @@ -177,6 +177,16 @@ struct DID_test : public beast::unit_test::suite env.close(); BEAST_EXPECT(ownerCount(env, alice) == 0); + // some empty fields, some optional fields + // pre-fix amendment + auto const fixEnabled = env.current()->rules().enabled(fixEmptyDID); + env(did::set(alice), + did::uri(""), + fixEnabled ? ter(tecEMPTY_DID) : ter(tesSUCCESS)); + env.close(); + auto const expectedOwnerReserve = fixEnabled ? 0 : 1; + BEAST_EXPECT(ownerCount(env, alice) == expectedOwnerReserve); + // Modifying a DID to become empty is checked in testSetModify } @@ -188,7 +198,7 @@ struct DID_test : public beast::unit_test::suite using namespace jtx; using namespace std::chrono; - Env env(*this); + Env env{*this, features}; Account const alice{"alice"}; env.fund(XRP(5000), alice); env.close(); @@ -219,7 +229,7 @@ struct DID_test : public beast::unit_test::suite using namespace jtx; using namespace std::chrono; - Env env(*this); + Env env{*this, features}; Account const alice{"alice"}; Account const bob{"bob"}; Account const charlie{"charlie"}; @@ -273,7 +283,7 @@ struct DID_test : public beast::unit_test::suite using namespace jtx; using namespace std::chrono; - Env env(*this); + Env env{*this, features}; Account const alice{"alice"}; env.fund(XRP(5000), alice); env.close(); @@ -390,13 +400,19 @@ struct DID_test : public beast::unit_test::suite run() override { using namespace test::jtx; - FeatureBitset const all{ - supported_amendments() | FeatureBitset{featureDID}}; + FeatureBitset const all{supported_amendments()}; + FeatureBitset const emptyDID{fixEmptyDID}; testEnabled(all); testAccountReserve(all); testSetInvalid(all); testDeleteInvalid(all); testSetModify(all); + + testEnabled(all - emptyDID); + testAccountReserve(all - emptyDID); + testSetInvalid(all - emptyDID); + testDeleteInvalid(all - emptyDID); + testSetModify(all - emptyDID); } }; diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index b0750e689f3..daa316e566c 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -157,6 +158,86 @@ class PeerFinder_test : public beast::unit_test::suite BEAST_EXPECT(n <= (seconds + 59) / 60); } + void + test_duplicateOutIn() + { + testcase("duplicate out/in"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + logic.addFixedPeer( + "test", beast::IP::Endpoint::from_string("65.0.0.1:5")); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const list = logic.autoconnect(); + if (BEAST_EXPECT(!list.empty())) + { + BEAST_EXPECT(list.size() == 1); + auto const remote = list.front(); + auto const slot1 = logic.new_outbound_slot(remote); + if (BEAST_EXPECT(slot1 != nullptr)) + { + BEAST_EXPECT( + logic.connectedAddresses_.count(remote.address()) == 1); + auto const local = + beast::IP::Endpoint::from_string("65.0.0.2:1024"); + auto const slot2 = logic.new_inbound_slot(local, remote); + BEAST_EXPECT( + logic.connectedAddresses_.count(remote.address()) == 1); + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.on_closed(slot2); + logic.on_closed(slot1); + } + } + } + + void + test_duplicateInOut() + { + testcase("duplicate in/out"); + TestStore store; + TestChecker checker; + TestStopwatch clock; + Logic logic(clock, store, checker, journal_); + logic.addFixedPeer( + "test", beast::IP::Endpoint::from_string("65.0.0.1:5")); + { + Config c; + c.autoConnect = false; + c.listeningPort = 1024; + c.ipLimit = 2; + logic.config(c); + } + + auto const list = logic.autoconnect(); + if (BEAST_EXPECT(!list.empty())) + { + BEAST_EXPECT(list.size() == 1); + auto const remote = list.front(); + auto const local = + beast::IP::Endpoint::from_string("65.0.0.2:1024"); + auto const slot1 = logic.new_inbound_slot(local, remote); + if (BEAST_EXPECT(slot1 != nullptr)) + { + BEAST_EXPECT( + logic.connectedAddresses_.count(remote.address()) == 1); + auto const slot2 = logic.new_outbound_slot(remote); + BEAST_EXPECT( + logic.connectedAddresses_.count(remote.address()) == 1); + if (!BEAST_EXPECT(slot2 == nullptr)) + logic.on_closed(slot2); + logic.on_closed(slot1); + } + } + } + void test_config() { @@ -279,6 +360,8 @@ class PeerFinder_test : public beast::unit_test::suite { test_backoff1(); test_backoff2(); + test_duplicateOutIn(); + test_duplicateInOut(); test_config(); test_invalid_config(); }