rippled
Transaction_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/rdb/backend/RelationalDBInterfaceSqlite.h>
21 #include <ripple/protocol/ErrorCodes.h>
22 #include <ripple/protocol/jss.h>
23 #include <test/jtx.h>
24 #include <test/jtx/Env.h>
25 #include <test/jtx/envconfig.h>
26 
27 namespace ripple {
28 
29 class Transaction_test : public beast::unit_test::suite
30 {
31  void
33  {
34  testcase("Test Range Request");
35 
36  using namespace test::jtx;
37  using std::to_string;
38 
39  const char* COMMAND = jss::tx.c_str();
40  const char* BINARY = jss::binary.c_str();
41  const char* NOT_FOUND = RPC::get_error_info(rpcTXN_NOT_FOUND).token;
43  const char* EXCESSIVE =
45 
46  Env env(*this);
47  auto const alice = Account("alice");
48  env.fund(XRP(1000), alice);
49  env.close();
50 
53  auto const startLegSeq = env.current()->info().seq;
54  for (int i = 0; i < 750; ++i)
55  {
56  env(noop(alice));
57  txns.emplace_back(env.tx());
58  env.close();
59  metas.emplace_back(
60  env.closed()->txRead(env.tx()->getTransactionID()).second);
61  }
62  auto const endLegSeq = env.closed()->info().seq;
63 
64  // Find the existing transactions
65  for (size_t i = 0; i < txns.size(); ++i)
66  {
67  auto const& tx = txns[i];
68  auto const& meta = metas[i];
69  auto const result = env.rpc(
70  COMMAND,
71  to_string(tx->getTransactionID()),
72  BINARY,
73  to_string(startLegSeq),
74  to_string(endLegSeq));
75 
76  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
77  BEAST_EXPECT(
78  result[jss::result][jss::tx] ==
79  strHex(tx->getSerializer().getData()));
80  BEAST_EXPECT(
81  result[jss::result][jss::meta] ==
82  strHex(meta->getSerializer().getData()));
83  }
84 
85  auto const tx = env.jt(noop(alice), seq(env.seq(alice))).stx;
86  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
87  {
88  auto const result = env.rpc(
89  COMMAND,
90  to_string(tx->getTransactionID()),
91  BINARY,
92  to_string(startLegSeq),
93  to_string(endLegSeq + deltaEndSeq));
94 
95  BEAST_EXPECT(
96  result[jss::result][jss::status] == jss::error &&
97  result[jss::result][jss::error] == NOT_FOUND);
98 
99  if (deltaEndSeq)
100  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
101  else
102  BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
103  }
104 
105  // Find transactions outside of provided range.
106  for (auto&& tx : txns)
107  {
108  auto const result = env.rpc(
109  COMMAND,
110  to_string(tx->getTransactionID()),
111  BINARY,
112  to_string(endLegSeq + 1),
113  to_string(endLegSeq + 100));
114 
115  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
116  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
117  }
118 
119  const auto deletedLedger = (startLegSeq + endLegSeq) / 2;
120  {
121  // Remove one of the ledgers from the database directly
122  dynamic_cast<RelationalDBInterfaceSqlite*>(
123  &env.app().getRelationalDBInterface())
124  ->deleteTransactionByLedgerSeq(deletedLedger);
125  }
126 
127  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
128  {
129  auto const result = env.rpc(
130  COMMAND,
131  to_string(tx->getTransactionID()),
132  BINARY,
133  to_string(startLegSeq),
134  to_string(endLegSeq + deltaEndSeq));
135 
136  BEAST_EXPECT(
137  result[jss::result][jss::status] == jss::error &&
138  result[jss::result][jss::error] == NOT_FOUND);
139  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
140  }
141 
142  // Provide range without providing the `binary`
143  // field. (Tests parameter parsing)
144  {
145  auto const result = env.rpc(
146  COMMAND,
147  to_string(tx->getTransactionID()),
148  to_string(startLegSeq),
149  to_string(endLegSeq));
150 
151  BEAST_EXPECT(
152  result[jss::result][jss::status] == jss::error &&
153  result[jss::result][jss::error] == NOT_FOUND);
154 
155  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
156  }
157 
158  // Provide range without providing the `binary`
159  // field. (Tests parameter parsing)
160  {
161  auto const result = env.rpc(
162  COMMAND,
163  to_string(tx->getTransactionID()),
164  to_string(startLegSeq),
165  to_string(deletedLedger - 1));
166 
167  BEAST_EXPECT(
168  result[jss::result][jss::status] == jss::error &&
169  result[jss::result][jss::error] == NOT_FOUND);
170 
171  BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
172  }
173 
174  // Provide range without providing the `binary`
175  // field. (Tests parameter parsing)
176  {
177  auto const result = env.rpc(
178  COMMAND,
179  to_string(txns[0]->getTransactionID()),
180  to_string(startLegSeq),
181  to_string(deletedLedger - 1));
182 
183  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
184  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
185  }
186 
187  // Provide an invalid range: (min > max)
188  {
189  auto const result = env.rpc(
190  COMMAND,
191  to_string(tx->getTransactionID()),
192  BINARY,
193  to_string(deletedLedger - 1),
194  to_string(startLegSeq));
195 
196  BEAST_EXPECT(
197  result[jss::result][jss::status] == jss::error &&
198  result[jss::result][jss::error] == INVALID);
199 
200  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
201  }
202 
203  // Provide an invalid range: (min < 0)
204  {
205  auto const result = env.rpc(
206  COMMAND,
207  to_string(tx->getTransactionID()),
208  BINARY,
209  to_string(-1),
210  to_string(deletedLedger - 1));
211 
212  BEAST_EXPECT(
213  result[jss::result][jss::status] == jss::error &&
214  result[jss::result][jss::error] == INVALID);
215 
216  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
217  }
218 
219  // Provide an invalid range: (min < 0, max < 0)
220  {
221  auto const result = env.rpc(
222  COMMAND,
223  to_string(tx->getTransactionID()),
224  BINARY,
225  to_string(-20),
226  to_string(-10));
227 
228  BEAST_EXPECT(
229  result[jss::result][jss::status] == jss::error &&
230  result[jss::result][jss::error] == INVALID);
231 
232  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
233  }
234 
235  // Provide an invalid range: (only one value)
236  {
237  auto const result = env.rpc(
238  COMMAND,
239  to_string(tx->getTransactionID()),
240  BINARY,
241  to_string(20));
242 
243  BEAST_EXPECT(
244  result[jss::result][jss::status] == jss::error &&
245  result[jss::result][jss::error] == INVALID);
246 
247  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
248  }
249 
250  // Provide an invalid range: (only one value)
251  {
252  auto const result = env.rpc(
253  COMMAND, to_string(tx->getTransactionID()), to_string(20));
254 
255  // Since we only provided one value for the range,
256  // the interface parses it as a false binary flag,
257  // as single-value ranges are not accepted. Since
258  // the error this causes differs depending on the platform
259  // we don't call out a specific error here.
260  BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
261 
262  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
263  }
264 
265  // Provide an invalid range: (max - min > 1000)
266  {
267  auto const result = env.rpc(
268  COMMAND,
269  to_string(tx->getTransactionID()),
270  BINARY,
271  to_string(startLegSeq),
272  to_string(startLegSeq + 1001));
273 
274  BEAST_EXPECT(
275  result[jss::result][jss::status] == jss::error &&
276  result[jss::result][jss::error] == EXCESSIVE);
277 
278  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
279  }
280  }
281 
282 public:
283  void
284  run() override
285  {
287  }
288 };
289 
290 BEAST_DEFINE_TESTSUITE(Transaction, rpc, ripple);
291 
292 } // namespace ripple
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
ripple::RPC::get_error_info
ErrorInfo const & get_error_info(error_code_i code)
Returns an ErrorInfo that reflects the error code.
Definition: ErrorCodes.cpp:201
std::vector
STL class.
std::vector::size
T size(T... args)
ripple::rpcEXCESSIVE_LGR_RANGE
@ rpcEXCESSIVE_LGR_RANGE
Definition: ErrorCodes.h:135
ripple::Transaction_test::run
void run() override
Definition: Transaction_test.cpp:284
ripple::Transaction_test
Definition: Transaction_test.cpp:29
ripple::RelationalDBInterfaceSqlite
Definition: RelationalDBInterfaceSqlite.h:27
std::to_string
T to_string(T... args)
ripple::rpcTXN_NOT_FOUND
@ rpcTXN_NOT_FOUND
Definition: ErrorCodes.h:80
std::vector::emplace_back
T emplace_back(T... args)
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::INVALID
@ INVALID
Definition: Transaction.h:47
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:39
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:45
ripple::rpcINVALID_LGR_RANGE
@ rpcINVALID_LGR_RANGE
Definition: ErrorCodes.h:136
ripple::RPC::ErrorInfo::token
Json::StaticString token
Definition: ErrorCodes.h:181
ripple::Transaction_test::testRangeRequest
void testRangeRequest()
Definition: Transaction_test.cpp:32