diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h index 91cf4d97ca..78863908c2 100644 --- a/src/ripple/protocol/Feature.h +++ b/src/ripple/protocol/Feature.h @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -67,6 +68,11 @@ namespace ripple { enum class VoteBehavior : int { Obsolete = -1, DefaultNo = 0, DefaultYes }; +enum class AmendmentSupport : int { Retired = -1, Supported = 0, Unsupported }; + +/** All amendments libxrpl knows about. */ +std::map const& +allAmendments(); namespace detail { diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp index 4c34737576..6543498cef 100644 --- a/src/ripple/protocol/impl/Feature.cpp +++ b/src/ripple/protocol/impl/Feature.cpp @@ -116,6 +116,7 @@ class FeatureCollections // name, index, and uint256 feature identifier boost::multi_index::multi_index_container features; + std::map all; std::map supported; std::size_t upVotes = 0; std::size_t downVotes = 0; @@ -179,6 +180,13 @@ public: std::string featureToName(uint256 const& f) const; + /** All amendments that are registered within the table. */ + std::map const& + allAmendments() const + { + return all; + } + /** Amendments that this server supports. Whether they are enabled depends on the Rules defined in the validated ledger */ @@ -251,6 +259,14 @@ FeatureCollections::registerFeature( features.emplace_back(name, f); + auto const getAmendmentSupport = [=]() { + if (vote == VoteBehavior::Obsolete) + return AmendmentSupport::Retired; + return support == Supported::yes ? AmendmentSupport::Supported + : AmendmentSupport::Unsupported; + }; + all.emplace(name, getAmendmentSupport()); + if (support == Supported::yes) { supported.emplace(name, vote); @@ -266,6 +282,9 @@ FeatureCollections::registerFeature( check( supported.size() <= features.size(), "More supported features than defined features"); + check( + features.size() == all.size(), + "The 'all' features list is populated incorrectly"); return f; } else @@ -313,6 +332,13 @@ static FeatureCollections featureCollections; } // namespace +/** All amendments libxrpl knows of. */ +std::map const& +allAmendments() +{ + return featureCollections.allAmendments(); +} + /** Amendments that this server supports. Whether they are enabled depends on the Rules defined in the validated ledger */ diff --git a/src/test/rpc/Feature_test.cpp b/src/test/rpc/Feature_test.cpp index fd63aee98e..010a61fbfc 100644 --- a/src/test/rpc/Feature_test.cpp +++ b/src/test/rpc/Feature_test.cpp @@ -31,34 +31,75 @@ class Feature_test : public beast::unit_test::suite { testcase("internals"); - std::map const& supported = - ripple::detail::supportedAmendments(); + auto const& supportedAmendments = ripple::detail::supportedAmendments(); + auto const& allAmendments = ripple::allAmendments(); + BEAST_EXPECT( - supported.size() == + supportedAmendments.size() == ripple::detail::numDownVotedAmendments() + ripple::detail::numUpVotedAmendments()); - std::size_t up = 0, down = 0, obsolete = 0; - for (std::pair const& amendment : - supported) { - switch (amendment.second) + std::size_t up = 0, down = 0, obsolete = 0; + for (auto const& [name, vote] : supportedAmendments) { - case VoteBehavior::DefaultYes: - ++up; - break; - case VoteBehavior::DefaultNo: - ++down; - break; - case VoteBehavior::Obsolete: - ++obsolete; - break; - default: - fail("Unknown VoteBehavior", __FILE__, __LINE__); + switch (vote) + { + case VoteBehavior::DefaultYes: + ++up; + break; + case VoteBehavior::DefaultNo: + ++down; + break; + case VoteBehavior::Obsolete: + ++obsolete; + break; + default: + fail("Unknown VoteBehavior", __FILE__, __LINE__); + } + + if (vote == VoteBehavior::Obsolete) + { + BEAST_EXPECT( + allAmendments.contains(name) && + allAmendments.at(name) == AmendmentSupport::Retired); + } + else + { + BEAST_EXPECT( + allAmendments.contains(name) && + allAmendments.at(name) == AmendmentSupport::Supported); + } } + BEAST_EXPECT( + down + obsolete == ripple::detail::numDownVotedAmendments()); + BEAST_EXPECT(up == ripple::detail::numUpVotedAmendments()); + } + { + std::size_t supported = 0, unsupported = 0, retired = 0; + for (auto const& [name, support] : allAmendments) + { + switch (support) + { + case AmendmentSupport::Supported: + ++supported; + BEAST_EXPECT(supportedAmendments.contains(name)); + break; + case AmendmentSupport::Unsupported: + ++unsupported; + break; + case AmendmentSupport::Retired: + ++retired; + break; + default: + fail("Unknown AmendmentSupport", __FILE__, __LINE__); + } + } + + BEAST_EXPECT(supported + retired == supportedAmendments.size()); + BEAST_EXPECT( + allAmendments.size() - unsupported == + supportedAmendments.size()); } - BEAST_EXPECT( - down + obsolete == ripple::detail::numDownVotedAmendments()); - BEAST_EXPECT(up == ripple::detail::numUpVotedAmendments()); } void