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/SQLiteDatabase.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<SQLiteDatabase*>(&env.app().getRelationalDatabase())
123  ->deleteTransactionByLedgerSeq(deletedLedger);
124  }
125 
126  for (int deltaEndSeq = 0; deltaEndSeq < 2; ++deltaEndSeq)
127  {
128  auto const result = env.rpc(
129  COMMAND,
130  to_string(tx->getTransactionID()),
131  BINARY,
132  to_string(startLegSeq),
133  to_string(endLegSeq + deltaEndSeq));
134 
135  BEAST_EXPECT(
136  result[jss::result][jss::status] == jss::error &&
137  result[jss::result][jss::error] == NOT_FOUND);
138  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
139  }
140 
141  // Provide range without providing the `binary`
142  // field. (Tests parameter parsing)
143  {
144  auto const result = env.rpc(
145  COMMAND,
146  to_string(tx->getTransactionID()),
147  to_string(startLegSeq),
148  to_string(endLegSeq));
149 
150  BEAST_EXPECT(
151  result[jss::result][jss::status] == jss::error &&
152  result[jss::result][jss::error] == NOT_FOUND);
153 
154  BEAST_EXPECT(!result[jss::result][jss::searched_all].asBool());
155  }
156 
157  // Provide range without providing the `binary`
158  // field. (Tests parameter parsing)
159  {
160  auto const result = env.rpc(
161  COMMAND,
162  to_string(tx->getTransactionID()),
163  to_string(startLegSeq),
164  to_string(deletedLedger - 1));
165 
166  BEAST_EXPECT(
167  result[jss::result][jss::status] == jss::error &&
168  result[jss::result][jss::error] == NOT_FOUND);
169 
170  BEAST_EXPECT(result[jss::result][jss::searched_all].asBool());
171  }
172 
173  // Provide range without providing the `binary`
174  // field. (Tests parameter parsing)
175  {
176  auto const result = env.rpc(
177  COMMAND,
178  to_string(txns[0]->getTransactionID()),
179  to_string(startLegSeq),
180  to_string(deletedLedger - 1));
181 
182  BEAST_EXPECT(result[jss::result][jss::status] == jss::success);
183  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
184  }
185 
186  // Provide an invalid range: (min > max)
187  {
188  auto const result = env.rpc(
189  COMMAND,
190  to_string(tx->getTransactionID()),
191  BINARY,
192  to_string(deletedLedger - 1),
193  to_string(startLegSeq));
194 
195  BEAST_EXPECT(
196  result[jss::result][jss::status] == jss::error &&
197  result[jss::result][jss::error] == INVALID);
198 
199  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
200  }
201 
202  // Provide an invalid range: (min < 0)
203  {
204  auto const result = env.rpc(
205  COMMAND,
206  to_string(tx->getTransactionID()),
207  BINARY,
208  to_string(-1),
209  to_string(deletedLedger - 1));
210 
211  BEAST_EXPECT(
212  result[jss::result][jss::status] == jss::error &&
213  result[jss::result][jss::error] == INVALID);
214 
215  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
216  }
217 
218  // Provide an invalid range: (min < 0, max < 0)
219  {
220  auto const result = env.rpc(
221  COMMAND,
222  to_string(tx->getTransactionID()),
223  BINARY,
224  to_string(-20),
225  to_string(-10));
226 
227  BEAST_EXPECT(
228  result[jss::result][jss::status] == jss::error &&
229  result[jss::result][jss::error] == INVALID);
230 
231  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
232  }
233 
234  // Provide an invalid range: (only one value)
235  {
236  auto const result = env.rpc(
237  COMMAND,
238  to_string(tx->getTransactionID()),
239  BINARY,
240  to_string(20));
241 
242  BEAST_EXPECT(
243  result[jss::result][jss::status] == jss::error &&
244  result[jss::result][jss::error] == INVALID);
245 
246  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
247  }
248 
249  // Provide an invalid range: (only one value)
250  {
251  auto const result = env.rpc(
252  COMMAND, to_string(tx->getTransactionID()), to_string(20));
253 
254  // Since we only provided one value for the range,
255  // the interface parses it as a false binary flag,
256  // as single-value ranges are not accepted. Since
257  // the error this causes differs depending on the platform
258  // we don't call out a specific error here.
259  BEAST_EXPECT(result[jss::result][jss::status] == jss::error);
260 
261  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
262  }
263 
264  // Provide an invalid range: (max - min > 1000)
265  {
266  auto const result = env.rpc(
267  COMMAND,
268  to_string(tx->getTransactionID()),
269  BINARY,
270  to_string(startLegSeq),
271  to_string(startLegSeq + 1001));
272 
273  BEAST_EXPECT(
274  result[jss::result][jss::status] == jss::error &&
275  result[jss::result][jss::error] == EXCESSIVE);
276 
277  BEAST_EXPECT(!result[jss::result].isMember(jss::searched_all));
278  }
279  }
280 
281 public:
282  void
283  run() override
284  {
286  }
287 };
288 
289 BEAST_DEFINE_TESTSUITE(Transaction, rpc, ripple);
290 
291 } // namespace ripple
ripple::SQLiteDatabase
Definition: SQLiteDatabase.h:27
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:170
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:283
ripple::Transaction_test
Definition: Transaction_test.cpp:29
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:41
ripple::strHex
std::string strHex(FwdIt begin, FwdIt end)
Definition: strHex.h:30
ripple::rpcINVALID_LGR_RANGE
@ rpcINVALID_LGR_RANGE
Definition: ErrorCodes.h:136
ripple::RPC::ErrorInfo::token
Json::StaticString token
Definition: ErrorCodes.h:199
ripple::Transaction_test::testRangeRequest
void testRangeRequest()
Definition: Transaction_test.cpp:32