rippled
Loading...
Searching...
No Matches
TransactionEntry_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/Env.h>
3
4#include <xrpl/json/json_reader.h>
5#include <xrpl/json/json_value.h>
6#include <xrpl/protocol/jss.h>
7
8#include <functional>
9
10namespace xrpl {
11
13{
14 void
16 {
17 testcase("Invalid request params");
18 using namespace test::jtx;
19 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
20 cfg->FEES.reference_fee = 10;
21 return cfg;
22 })};
23
24 {
25 // no params
26 auto const result = env.client().invoke("transaction_entry", {})[jss::result];
27 BEAST_EXPECT(result[jss::error] == "fieldNotFoundTransaction");
28 BEAST_EXPECT(result[jss::status] == "error");
29 }
30
31 {
33 params[jss::ledger] = 20;
34 auto const result = env.client().invoke("transaction_entry", params)[jss::result];
35 BEAST_EXPECT(result[jss::error] == "lgrNotFound");
36 BEAST_EXPECT(result[jss::status] == "error");
37 }
38
39 {
41 params[jss::ledger] = "current";
42 params[jss::tx_hash] = "DEADBEEF";
43 auto const result = env.client().invoke("transaction_entry", params)[jss::result];
44 BEAST_EXPECT(result[jss::error] == "notYetImplemented");
45 BEAST_EXPECT(result[jss::status] == "error");
46 }
47
48 {
50 params[jss::ledger] = "closed";
51 params[jss::tx_hash] = "DEADBEEF";
52 auto const result = env.client().invoke("transaction_entry", params)[jss::result];
53 BEAST_EXPECT(!result[jss::ledger_hash].asString().empty());
54 BEAST_EXPECT(result[jss::error] == "malformedRequest");
55 BEAST_EXPECT(result[jss::status] == "error");
56 }
57
58 std::string const txHash{
59 "E2FE8D4AF3FCC3944DDF6CD8CDDC5E3F0AD50863EF8919AFEF10CB6408CD4D05"};
60
61 // Command line format
62 {
63 // No arguments
64 Json::Value const result{env.rpc("transaction_entry")};
65 BEAST_EXPECT(result[jss::ledger_hash].asString().empty());
66 BEAST_EXPECT(result[jss::error] == "badSyntax");
67 BEAST_EXPECT(result[jss::status] == "error");
68 }
69
70 {
71 // One argument
72 Json::Value const result{env.rpc("transaction_entry", txHash)};
73 BEAST_EXPECT(result[jss::error] == "badSyntax");
74 BEAST_EXPECT(result[jss::status] == "error");
75 }
76
77 {
78 // First argument with too few characters
79 Json::Value const result{env.rpc("transaction_entry", txHash.substr(1), "closed")};
80 BEAST_EXPECT(result[jss::error] == "invalidParams");
81 BEAST_EXPECT(result[jss::status] == "error");
82 }
83
84 {
85 // First argument with too many characters
86 Json::Value const result{env.rpc("transaction_entry", txHash + "A", "closed")};
87 BEAST_EXPECT(result[jss::error] == "invalidParams");
88 BEAST_EXPECT(result[jss::status] == "error");
89 }
90
91 {
92 // Second argument not valid
93 Json::Value const result{env.rpc("transaction_entry", txHash, "closer")};
94 BEAST_EXPECT(result[jss::error] == "invalidParams");
95 BEAST_EXPECT(result[jss::status] == "error");
96 }
97
98 {
99 // Ledger index of 0 is not valid
100 Json::Value const result{env.rpc("transaction_entry", txHash, "0")};
101 BEAST_EXPECT(result[jss::error] == "invalidParams");
102 BEAST_EXPECT(result[jss::status] == "error");
103 }
104
105 {
106 // Three arguments
107 Json::Value const result{env.rpc("transaction_entry", txHash, "closed", "extra")};
108 BEAST_EXPECT(result[jss::error] == "badSyntax");
109 BEAST_EXPECT(result[jss::status] == "error");
110 }
111
112 {
113 // Valid structure, but transaction not found.
114 Json::Value const result{env.rpc("transaction_entry", txHash, "closed")};
115 BEAST_EXPECT(!result[jss::result][jss::ledger_hash].asString().empty());
116 BEAST_EXPECT(result[jss::result][jss::error] == "transactionNotFound");
117 BEAST_EXPECT(result[jss::result][jss::status] == "error");
118 }
119 }
120
121 void
122 testRequest(unsigned apiVersion)
123 {
124 testcase("Basic request API version " + std::to_string(apiVersion));
125 using namespace test::jtx;
126 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
127 cfg->FEES.reference_fee = 10;
128 return cfg;
129 })};
130
131 auto check_tx = [this, &env, apiVersion](
132 int index,
133 std::string const txhash,
134 std::string const expected_json = "",
135 std::string const expected_ledger_hash = "",
136 std::string const close_time_iso = "") {
137 // first request using ledger_index to lookup
138 Json::Value const resIndex{[&env, index, &txhash, apiVersion]() {
140 params[jss::ledger_index] = index;
141 params[jss::tx_hash] = txhash;
142 params[jss::api_version] = apiVersion;
143 return env.client().invoke("transaction_entry", params)[jss::result];
144 }()};
145
146 if (!BEAST_EXPECT(resIndex.isMember(jss::tx_json)))
147 return;
148
149 BEAST_EXPECT(resIndex[jss::validated] == true);
150 BEAST_EXPECT(resIndex[jss::ledger_index] == index);
151 BEAST_EXPECT(resIndex[jss::ledger_hash] == expected_ledger_hash);
152 if (apiVersion > 1)
153 {
154 BEAST_EXPECT(resIndex[jss::hash] == txhash);
155 BEAST_EXPECT(!resIndex[jss::tx_json].isMember(jss::hash));
156 BEAST_EXPECT(!resIndex[jss::tx_json].isMember(jss::Amount));
157
158 if (BEAST_EXPECT(!close_time_iso.empty()))
159 BEAST_EXPECT(resIndex[jss::close_time_iso] == close_time_iso);
160 }
161 else
162 {
163 BEAST_EXPECT(resIndex[jss::tx_json][jss::hash] == txhash);
164 BEAST_EXPECT(!resIndex.isMember(jss::hash));
165 BEAST_EXPECT(!resIndex.isMember(jss::close_time_iso));
166 }
167
168 if (!expected_json.empty())
169 {
170 Json::Value expected;
171 Json::Reader().parse(expected_json, expected);
172 if (RPC::contains_error(expected))
173 Throw<std::runtime_error>("Internal JSONRPC_test error. Bad test JSON.");
174
175 for (auto memberIt = expected.begin(); memberIt != expected.end(); memberIt++)
176 {
177 auto const name = memberIt.memberName();
178 if (BEAST_EXPECT(resIndex[jss::tx_json].isMember(name)))
179 {
180 auto const received = resIndex[jss::tx_json][name];
181 BEAST_EXPECTS(
182 received == *memberIt,
183 txhash + " contains \n\"" + name + "\": " //
184 + to_string(received) //
185 + " but expected " //
186 + to_string(expected));
187 }
188 }
189 }
190
191 // second request using ledger_hash to lookup and verify
192 // both responses match
193 {
195 params[jss::ledger_hash] = resIndex[jss::ledger_hash];
196 params[jss::tx_hash] = txhash;
197 params[jss::api_version] = apiVersion;
198 Json::Value const resHash =
199 env.client().invoke("transaction_entry", params)[jss::result];
200 BEAST_EXPECT(resHash == resIndex);
201 }
202
203 // Use the command line form with the index.
204 Json::Value const clIndex{
205 env.rpc(apiVersion, "transaction_entry", txhash, std::to_string(index))};
206 BEAST_EXPECT(clIndex["result"] == resIndex);
207
208 // Use the command line form with the ledger_hash.
209 Json::Value const clHash{env.rpc(
210 apiVersion, "transaction_entry", txhash, resIndex[jss::ledger_hash].asString())};
211 BEAST_EXPECT(clHash["result"] == resIndex);
212 };
213
214 Account const A1{"A1"};
215 Account const A2{"A2"};
216
217 env.fund(XRP(10000), A1);
218 auto fund_1_tx = to_string(env.tx()->getTransactionID());
219 BEAST_EXPECT(
220 fund_1_tx == "F4E9DF90D829A9E8B423FF68C34413E240D8D8BB0EFD080DF08114ED398E2506");
221
222 env.fund(XRP(10000), A2);
223 auto fund_2_tx = to_string(env.tx()->getTransactionID());
224 BEAST_EXPECT(
225 fund_2_tx == "6853CD8226A05068C951CB1F54889FF4E40C5B440DC1C5BA38F114C4E0B1E705");
226
227 env.close();
228
229 // these are actually AccountSet txs because fund does two txs and
230 // env.tx only reports the last one
231 check_tx(
232 env.closed()->seq(),
233 fund_1_tx,
234 R"({
235 "Account" : "r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf",
236 "Fee" : "10",
237 "Sequence" : 3,
238 "SetFlag" : 8,
239 "SigningPubKey" : "0324CAAFA2212D2AEAB9D42D481535614AED486293E1FB1380FF070C3DD7FB4264",
240 "TransactionType" : "AccountSet",
241 "TxnSignature" : "3044022007B35E3B99460534FF6BC3A66FBBA03591C355CC38E38588968E87CCD01BE229022071A443026DE45041B55ABB1CC76812A87EA701E475BBB7E165513B4B242D3474",
242})",
243 "ADB727BCC74B29421BB01B847740B179B8A0ED3248D76A89ED2E39B02C427784",
244 "2000-01-01T00:00:10Z");
245 check_tx(
246 env.closed()->seq(),
247 fund_2_tx,
248 R"({
249 "Account" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
250 "Fee" : "10",
251 "Sequence" : 3,
252 "SetFlag" : 8,
253 "SigningPubKey" : "03CFF28E067A2CCE6CC5A598C0B845CBD3F30A7863BE9C0DD55F4960EFABCCF4D0",
254 "TransactionType" : "AccountSet",
255 "TxnSignature" : "3045022100C8857FC0759A2AC0D2F320684691A66EAD252EAED9EF88C79791BC58BFCC9D860220421722286487DD0ED6BBA626CE6FCBDD14289F7F4726870C3465A4054C2702D7",
256})",
257 "ADB727BCC74B29421BB01B847740B179B8A0ED3248D76A89ED2E39B02C427784",
258 "2000-01-01T00:00:10Z");
259
260 env.trust(A2["USD"](1000), A1);
261 // the trust tx is actually a payment since the trust method
262 // refunds fees with a payment after TrustSet..so just ignore the type
263 // in the check below
264 auto trust_tx = to_string(env.tx()->getTransactionID());
265 BEAST_EXPECT(
266 trust_tx == "C992D97D88FF444A1AB0C06B27557EC54B7F7DA28254778E60238BEA88E0C101");
267
268 env(pay(A2, A1, A2["USD"](5)));
269 auto pay_tx = to_string(env.tx()->getTransactionID());
270 env.close();
271 BEAST_EXPECT(pay_tx == "988046D484ACE9F5F6A8C792D89C6EA2DB307B5DDA9864AEBA88E6782ABD0865");
272
273 check_tx(
274 env.closed()->seq(),
275 trust_tx,
276 R"({
277 "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
278 "DeliverMax" : "10",
279 "Destination" : "r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf",
280 "Fee" : "10",
281 "Flags" : 2147483648,
282 "Sequence" : 3,
283 "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
284 "TransactionType" : "Payment",
285 "TxnSignature" : "3044022033D9EBF7F02950AF2F6B13C07AEE641C8FEBDD540A338FCB9027A965A4AED35B02206E4E227DCC226A3456C0FEF953449D21645A24EB63CA0BB7C5B62470147FD1D1",
286})",
287 "3A6E375BFDFF029A571AFBB3BC46C4F52963FAF043B406D0E59A7194C1A8F98E",
288 "2000-01-01T00:00:20Z");
289
290 check_tx(
291 env.closed()->seq(),
292 pay_tx,
293 R"({
294 "Account" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
295 "DeliverMax" :
296 {
297 "currency" : "USD",
298 "issuer" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
299 "value" : "5"
300 },
301 "Destination" : "r4nmQNH4Fhjfh6cHDbvVSsBv7KySbj4cBf",
302 "Fee" : "10",
303 "Flags" : 2147483648,
304 "Sequence" : 4,
305 "SigningPubKey" : "03CFF28E067A2CCE6CC5A598C0B845CBD3F30A7863BE9C0DD55F4960EFABCCF4D0",
306 "TransactionType" : "Payment",
307 "TxnSignature" : "30450221008A722B7F16EDB2348886E88ED4EC682AE9973CC1EE0FF37C93BB2CEC821D3EDF022059E464472031BA5E0D88A93E944B6A8B8DB3E1D5E5D1399A805F615789DB0BED",
308})",
309 "3A6E375BFDFF029A571AFBB3BC46C4F52963FAF043B406D0E59A7194C1A8F98E",
310 "2000-01-01T00:00:20Z");
311
312 env(offer(A2, XRP(100), A2["USD"](1)));
313 auto offer_tx = to_string(env.tx()->getTransactionID());
314 BEAST_EXPECT(
315 offer_tx == "5FCC1A27A7664F82A0CC4BE5766FBBB7C560D52B93AA7B550CD33B27AEC7EFFB");
316
317 env.close();
318 check_tx(
319 env.closed()->seq(),
320 offer_tx,
321 R"({
322 "Account" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
323 "Fee" : "10",
324 "Sequence" : 5,
325 "SigningPubKey" : "03CFF28E067A2CCE6CC5A598C0B845CBD3F30A7863BE9C0DD55F4960EFABCCF4D0",
326 "TakerGets" :
327 {
328 "currency" : "USD",
329 "issuer" : "rGpeQzUWFu4fMhJHZ1Via5aqFC3A5twZUD",
330 "value" : "1"
331 },
332 "TakerPays" : "100000000",
333 "TransactionType" : "OfferCreate",
334 "TxnSignature" : "304502210093FC93ACB77B4E3DE3315441BD010096734859080C1797AB735EB47EBD541BD102205020BB1A7C3B4141279EE4C287C13671E2450EA78914EFD0C6DB2A18344CD4F2",
335})",
336 "73D6C8E66E0DC22F3E6F7D39BF795A6831BEB412823A986C7CC19470C93557C0",
337 "2000-01-01T00:00:30Z");
338 }
339
340public:
341 void
342 run() override
343 {
344 testBadInput();
346 }
347};
348
349BEAST_DEFINE_TESTSUITE(TransactionEntry, rpc, xrpl);
350
351} // namespace xrpl
T bind_front(T... args)
Unserialize a JSON document into a Value.
Definition json_reader.h:17
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:130
const_iterator begin() const
const_iterator end() const
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
void testRequest(unsigned apiVersion)
void run() override
Runs the suite.
T empty(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
void forAllApiVersions(Fn const &fn, Args &&... args)
Definition ApiVersion.h:149
T to_string(T... args)