rippled
Feature_test.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012-2017 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/app/misc/AmendmentTable.h>
21 #include <ripple/protocol/Feature.h>
22 #include <ripple/protocol/jss.h>
23 #include <test/jtx.h>
24 
25 namespace ripple {
26 
27 class Feature_test : public beast::unit_test::suite
28 {
29  void
31  {
32  testcase("internals");
33 
34  std::map<std::string, DefaultVote> const& supported =
36  BEAST_EXPECT(
37  supported.size() ==
40  std::size_t up = 0, down = 0;
41  for (std::pair<std::string const, DefaultVote> const& amendment :
42  supported)
43  {
44  if (amendment.second == DefaultVote::no)
45  ++down;
46  else
47  {
48  if (BEAST_EXPECT(amendment.second == DefaultVote::yes))
49  ++up;
50  }
51  }
53  BEAST_EXPECT(up == ripple::detail::numUpVotedAmendments());
54  }
55 
56  void
58  {
59  testcase("featureToName");
60 
61  // Test all the supported features. In a perfect world, this would test
62  // FeatureCollections::featureNames, but that's private. Leave it that
63  // way.
64  auto const supported = ripple::detail::supportedAmendments();
65 
66  for (auto const& [feature, vote] : supported)
67  {
68  (void)vote;
69  auto const registered = getRegisteredFeature(feature);
70  if (BEAST_EXPECT(registered))
71  {
72  BEAST_EXPECT(featureToName(*registered) == feature);
73  BEAST_EXPECT(
75  *registered);
76  }
77  }
78 
79  // Test an arbitrary unknown feature
80  uint256 zero{0};
81  BEAST_EXPECT(featureToName(zero) == to_string(zero));
82  BEAST_EXPECT(
83  featureToName(zero) ==
84  "0000000000000000000000000000000000000000000000000000000000000000");
85 
86  // Test looking up an unknown feature
87  BEAST_EXPECT(!getRegisteredFeature("unknown"));
88 
89  // Test a random sampling of the variables. If any of these get retired
90  // or removed, swap out for any other feature.
91  BEAST_EXPECT(featureToName(featureOwnerPaysFee) == "OwnerPaysFee");
92  BEAST_EXPECT(featureToName(featureFlow) == "Flow");
93  BEAST_EXPECT(featureToName(featureNegativeUNL) == "NegativeUNL");
94  BEAST_EXPECT(featureToName(fix1578) == "fix1578");
95  BEAST_EXPECT(
97  "fixTakerDryOfferRemoval");
98  }
99 
100  void
102  {
103  testcase("No Params, None Enabled");
104 
105  using namespace test::jtx;
106  Env env{*this};
107 
110 
111  auto jrr = env.rpc("feature")[jss::result];
112  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
113  return;
114  for (auto const& feature : jrr[jss::features])
115  {
116  if (!BEAST_EXPECT(feature.isMember(jss::name)))
117  return;
118  // default config - so all should be disabled, and
119  // supported. Some may be vetoed.
120  bool expectVeto =
121  !(votes.at(feature[jss::name].asString()) == DefaultVote::yes);
122  BEAST_EXPECTS(
123  !feature[jss::enabled].asBool(),
124  feature[jss::name].asString() + " enabled");
125  BEAST_EXPECTS(
126  feature[jss::vetoed].asBool() == expectVeto,
127  feature[jss::name].asString() + " vetoed");
128  BEAST_EXPECTS(
129  feature[jss::supported].asBool(),
130  feature[jss::name].asString() + " supported");
131  }
132  }
133 
134  void
136  {
137  testcase("Feature Param");
138 
139  using namespace test::jtx;
140  Env env{*this};
141 
142  auto jrr = env.rpc("feature", "MultiSignReserve")[jss::result];
143  BEAST_EXPECTS(jrr[jss::status] == jss::success, "status");
144  jrr.removeMember(jss::status);
145  BEAST_EXPECT(jrr.size() == 1);
146  BEAST_EXPECT(
147  jrr.isMember("586480873651E106F1D6339B0C4A8945BA705A777F3F4524626FF"
148  "1FC07EFE41D"));
149  auto feature = *(jrr.begin());
150 
151  BEAST_EXPECTS(feature[jss::name] == "MultiSignReserve", "name");
152  BEAST_EXPECTS(!feature[jss::enabled].asBool(), "enabled");
153  BEAST_EXPECTS(!feature[jss::vetoed].asBool(), "vetoed");
154  BEAST_EXPECTS(feature[jss::supported].asBool(), "supported");
155 
156  // feature names are case-sensitive - expect error here
157  jrr = env.rpc("feature", "multiSignReserve")[jss::result];
158  BEAST_EXPECT(jrr[jss::error] == "badFeature");
159  BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
160  }
161 
162  void
164  {
165  testcase("Invalid Feature");
166 
167  using namespace test::jtx;
168  Env env{*this};
169 
170  auto jrr = env.rpc("feature", "AllTheThings")[jss::result];
171  BEAST_EXPECT(jrr[jss::error] == "badFeature");
172  BEAST_EXPECT(jrr[jss::error_message] == "Feature unknown or invalid.");
173  }
174 
175  void
177  {
178  testcase("Feature Without Admin");
179 
180  using namespace test::jtx;
181  Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
182  (*cfg)["port_rpc"].set("admin", "");
183  (*cfg)["port_ws"].set("admin", "");
184  return cfg;
185  })};
186 
187  auto jrr = env.rpc("feature")[jss::result];
188  // The current HTTP/S ServerHandler returns an HTTP 403 error code here
189  // rather than a noPermission JSON error. The JSONRPCClient just eats
190  // that error and returns an null result.
191  BEAST_EXPECT(jrr.isNull());
192  }
193 
194  void
196  {
197  testcase("No Params, Some Enabled");
198 
199  using namespace test::jtx;
200  Env env{
202 
205 
206  auto jrr = env.rpc("feature")[jss::result];
207  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
208  return;
209  for (auto it = jrr[jss::features].begin();
210  it != jrr[jss::features].end();
211  ++it)
212  {
213  uint256 id;
214  (void)id.parseHex(it.key().asString().c_str());
215  if (!BEAST_EXPECT((*it).isMember(jss::name)))
216  return;
217  bool expectEnabled = env.app().getAmendmentTable().isEnabled(id);
218  bool expectSupported =
219  env.app().getAmendmentTable().isSupported(id);
220  bool expectVeto =
221  !(votes.at((*it)[jss::name].asString()) == DefaultVote::yes);
222  BEAST_EXPECTS(
223  (*it)[jss::enabled].asBool() == expectEnabled,
224  (*it)[jss::name].asString() + " enabled");
225  BEAST_EXPECTS(
226  (*it)[jss::vetoed].asBool() == expectVeto,
227  (*it)[jss::name].asString() + " vetoed");
228  BEAST_EXPECTS(
229  (*it)[jss::supported].asBool() == expectSupported,
230  (*it)[jss::name].asString() + " supported");
231  }
232  }
233 
234  void
236  {
237  testcase("With Majorities");
238 
239  using namespace test::jtx;
240  Env env{*this, envconfig(validator, "")};
241 
242  auto jrr = env.rpc("feature")[jss::result];
243  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
244  return;
245 
246  // at this point, there are no majorities so no fields related to
247  // amendment voting
248  for (auto const& feature : jrr[jss::features])
249  {
250  if (!BEAST_EXPECT(feature.isMember(jss::name)))
251  return;
252  BEAST_EXPECTS(
253  !feature.isMember(jss::majority),
254  feature[jss::name].asString() + " majority");
255  BEAST_EXPECTS(
256  !feature.isMember(jss::count),
257  feature[jss::name].asString() + " count");
258  BEAST_EXPECTS(
259  !feature.isMember(jss::threshold),
260  feature[jss::name].asString() + " threshold");
261  BEAST_EXPECTS(
262  !feature.isMember(jss::validations),
263  feature[jss::name].asString() + " validations");
264  BEAST_EXPECTS(
265  !feature.isMember(jss::vote),
266  feature[jss::name].asString() + " vote");
267  }
268 
269  auto majorities = getMajorityAmendments(*env.closed());
270  if (!BEAST_EXPECT(majorities.empty()))
271  return;
272 
273  // close ledgers until the amendments show up.
274  for (auto i = 0; i <= 256; ++i)
275  {
276  env.close();
277  majorities = getMajorityAmendments(*env.closed());
278  if (!majorities.empty())
279  break;
280  }
281 
282  // There should be at least 5 amendments. Don't do exact comparison
283  // to avoid maintenance as more amendments are added in the future.
284  BEAST_EXPECT(majorities.size() >= 5);
287 
288  jrr = env.rpc("feature")[jss::result];
289  if (!BEAST_EXPECT(jrr.isMember(jss::features)))
290  return;
291  for (auto const& feature : jrr[jss::features])
292  {
293  if (!BEAST_EXPECT(feature.isMember(jss::name)))
294  return;
295  bool expectVeto =
296  !(votes.at(feature[jss::name].asString()) == DefaultVote::yes);
297  BEAST_EXPECTS(
298  expectVeto ^ feature.isMember(jss::majority),
299  feature[jss::name].asString() + " majority");
300  BEAST_EXPECTS(
301  feature.isMember(jss::vetoed) &&
302  feature[jss::vetoed].asBool() == expectVeto,
303  feature[jss::name].asString() + " vetoed");
304  BEAST_EXPECTS(
305  feature.isMember(jss::count),
306  feature[jss::name].asString() + " count");
307  BEAST_EXPECTS(
308  feature.isMember(jss::threshold),
309  feature[jss::name].asString() + " threshold");
310  BEAST_EXPECTS(
311  feature.isMember(jss::validations),
312  feature[jss::name].asString() + " validations");
313  BEAST_EXPECT(feature[jss::count] == (expectVeto ? 0 : 1));
314  BEAST_EXPECT(feature[jss::threshold] == 1);
315  BEAST_EXPECT(feature[jss::validations] == 1);
316  BEAST_EXPECTS(
317  expectVeto || feature[jss::majority] == 2540,
318  "Majority: " + feature[jss::majority].asString());
319  }
320  }
321 
322  void
324  {
325  testcase("Veto");
326 
327  using namespace test::jtx;
328  Env env{*this, FeatureBitset(featureMultiSignReserve)};
329 
330  auto jrr = env.rpc("feature", "MultiSignReserve")[jss::result];
331  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
332  return;
333  jrr.removeMember(jss::status);
334  if (!BEAST_EXPECT(jrr.size() == 1))
335  return;
336  auto feature = *(jrr.begin());
337  BEAST_EXPECTS(feature[jss::name] == "MultiSignReserve", "name");
338  BEAST_EXPECTS(!feature[jss::vetoed].asBool(), "vetoed");
339 
340  jrr = env.rpc("feature", "MultiSignReserve", "reject")[jss::result];
341  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
342  return;
343  jrr.removeMember(jss::status);
344  if (!BEAST_EXPECT(jrr.size() == 1))
345  return;
346  feature = *(jrr.begin());
347  BEAST_EXPECTS(feature[jss::name] == "MultiSignReserve", "name");
348  BEAST_EXPECTS(feature[jss::vetoed].asBool(), "vetoed");
349 
350  jrr = env.rpc("feature", "MultiSignReserve", "accept")[jss::result];
351  if (!BEAST_EXPECTS(jrr[jss::status] == jss::success, "status"))
352  return;
353  jrr.removeMember(jss::status);
354  if (!BEAST_EXPECT(jrr.size() == 1))
355  return;
356  feature = *(jrr.begin());
357  BEAST_EXPECTS(feature[jss::name] == "MultiSignReserve", "name");
358  BEAST_EXPECTS(!feature[jss::vetoed].asBool(), "vetoed");
359 
360  // anything other than accept or reject is an error
361  jrr = env.rpc("feature", "MultiSignReserve", "maybe");
362  BEAST_EXPECT(jrr[jss::error] == "invalidParams");
363  BEAST_EXPECT(jrr[jss::error_message] == "Invalid parameters.");
364  }
365 
366 public:
367  void
368  run() override
369  {
370  testInternals();
372  testNoParams();
375  testNonAdmin();
376  testSomeEnabled();
378  testVeto();
379  }
380 };
381 
382 BEAST_DEFINE_TESTSUITE(Feature, rpc, ripple);
383 
384 } // namespace ripple
ripple::getMajorityAmendments
majorityAmendments_t getMajorityAmendments(ReadView const &view)
Definition: View.cpp:621
ripple::AmendmentVote::up
@ up
ripple::Feature_test::testSingleFeature
void testSingleFeature()
Definition: Feature_test.cpp:135
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::Feature_test::testVeto
void testVeto()
Definition: Feature_test.cpp:323
std::pair
ripple::featureDepositPreauth
const uint256 featureDepositPreauth
ripple::Feature_test::run
void run() override
Definition: Feature_test.cpp:368
ripple::Feature_test::testFeatureLookups
void testFeatureLookups()
Definition: Feature_test.cpp:57
ripple::featureDepositAuth
const uint256 featureDepositAuth
ripple::featureMultiSignReserve
const uint256 featureMultiSignReserve
ripple::detail::numUpVotedAmendments
std::size_t numUpVotedAmendments()
Amendments that this server will vote for by default.
Definition: Feature.cpp:333
ripple::AmendmentVote::down
@ down
ripple::base_uint< 256 >
ripple::detail::numDownVotedAmendments
std::size_t numDownVotedAmendments()
Amendments that this server won't vote for by default.
Definition: Feature.cpp:326
ripple::Feature_test::testSomeEnabled
void testSomeEnabled()
Definition: Feature_test.cpp:195
ripple::Feature_test
Definition: Feature_test.cpp:27
ripple::detail::supportedAmendments
std::map< std::string, DefaultVote > const & supportedAmendments()
Amendments that this server supports and the default voting behavior.
Definition: Feature.cpp:319
std::map::at
T at(T... args)
ripple::DefaultVote::yes
@ yes
ripple::fixTakerDryOfferRemoval
const uint256 fixTakerDryOfferRemoval
ripple::featureToBitsetIndex
size_t featureToBitsetIndex(uint256 const &f)
Definition: Feature.cpp:368
std::map
STL class.
ripple::Feature_test::testInternals
void testInternals()
Definition: Feature_test.cpp:30
ripple::fix1578
const uint256 fix1578
ripple::Feature_test::testWithMajorities
void testWithMajorities()
Definition: Feature_test.cpp:235
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::featureFlow
const uint256 featureFlow
ripple::featureNegativeUNL
const uint256 featureNegativeUNL
ripple::bitsetIndexToFeature
uint256 bitsetIndexToFeature(size_t i)
Definition: Feature.cpp:374
ripple::FeatureBitset
Definition: Feature.h:113
ripple::Feature_test::testNoParams
void testNoParams()
Definition: Feature_test.cpp:101
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::featureOwnerPaysFee
const uint256 featureOwnerPaysFee
ripple::Feature_test::testNonAdmin
void testNonAdmin()
Definition: Feature_test.cpp:176
ripple::getRegisteredFeature
std::optional< uint256 > getRegisteredFeature(std::string const &name)
Definition: Feature.cpp:341
ripple::Feature_test::testInvalidFeature
void testInvalidFeature()
Definition: Feature_test.cpp:163
std::unique_ptr
STL class.
ripple::DefaultVote::no
@ no
ripple::featureToName
std::string featureToName(uint256 const &f)
Definition: Feature.cpp:380