rippled
Loading...
Searching...
No Matches
JSONRPC_test.cpp
1#include <test/jtx.h>
2#include <test/jtx/envconfig.h>
3
4#include <xrpld/app/misc/LoadFeeTrack.h>
5#include <xrpld/app/misc/TxQ.h>
6#include <xrpld/core/ConfigSections.h>
7#include <xrpld/rpc/detail/TransactionSign.h>
8
9#include <xrpl/basics/contract.h>
10#include <xrpl/beast/unit_test.h>
11#include <xrpl/json/json_reader.h>
12#include <xrpl/protocol/ErrorCodes.h>
13
14namespace xrpl {
15
16namespace RPC {
17
19{
20 char const* const description;
21 int const line;
22 char const* const json;
23 // The JSON is applied to four different interfaces:
24 // 1. sign,
25 // 2. submit,
26 // 3. sign_for, and
27 // 4. submit_multisigned.
28 // The JSON is not valid for all of these interfaces, but it should
29 // crash none of them, and should provide reliable error messages.
30 //
31 // The expMsg array contains the expected error string for the above cases.
33
34 constexpr TxnTestData(
35 char const* description_,
36 int line_,
37 char const* json_,
39 : description(description_), line(line_), json(json_), expMsg{expMsg_}
40 {
41 }
42
43 TxnTestData() = delete;
44 TxnTestData(TxnTestData const&) = delete;
47 operator=(TxnTestData const&) = delete;
50};
51
52static constexpr TxnTestData txnTestArray[] = {
53
54 {"Minimal payment, no Amount only DeliverMax",
55 __LINE__,
56 R"({
57 "command": "dummy_command",
58 "secret": "masterpassphrase",
59 "tx_json": {
60 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
61 "DeliverMax": "1000000000",
62 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
63 "TransactionType": "Payment"
64 }
65})",
66 {{"", "", "Missing field 'account'.", "Missing field 'tx_json.Sequence'."}}},
67
68 {"Pass in Fee with minimal payment, both Amount and DeliverMax.",
69 __LINE__,
70 R"({
71 "command": "dummy_command",
72 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
73 "secret": "masterpassphrase",
74 "tx_json": {
75 "Fee": 10,
76 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
77 "Amount": "1000000000",
78 "DeliverMax": "1000000000",
79 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
80 "TransactionType": "Payment"
81 }
82})",
83 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
84
85 {"Pass in Sequence, no Amount only DeliverMax",
86 __LINE__,
87 R"({
88 "command": "dummy_command",
89 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
90 "secret": "masterpassphrase",
91 "tx_json": {
92 "Sequence": 0,
93 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
94 "DeliverMax": "1000000000",
95 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
96 "TransactionType": "Payment"
97 }
98})",
99 {{"", "", "Missing field 'tx_json.Fee'.", "Missing field 'tx_json.SigningPubKey'."}}},
100
101 {"Pass in Sequence and Fee with minimal payment, both Amount and "
102 "DeliverMax.",
103 __LINE__,
104 R"({
105 "command": "dummy_command",
106 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
107 "secret": "masterpassphrase",
108 "tx_json": {
109 "Sequence": 0,
110 "Fee": 10,
111 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
112 "Amount": "1000000000",
113 "DeliverMax": "1000000000",
114 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
115 "TransactionType": "Payment"
116 }
117})",
118 {{"",
119 "",
120 "A Signer may not be the transaction's Account "
121 "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
122 "Missing field 'tx_json.SigningPubKey'."}}},
123
124 {"Add 'fee_mult_max' field.",
125 __LINE__,
126 R"({
127 "command": "dummy_command",
128 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
129 "secret": "masterpassphrase",
130 "fee_mult_max": 7,
131 "tx_json": {
132 "Sequence": 0,
133 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
134 "Amount": "1000000000",
135 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
136 "TransactionType": "Payment"
137 }
138})",
139 {{"", "", "Missing field 'tx_json.Fee'.", "Missing field 'tx_json.SigningPubKey'."}}},
140
141 {"Add 'fee_mult_max' and 'fee_div_max' field.",
142 __LINE__,
143 R"({
144 "command": "dummy_command",
145 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
146 "secret": "masterpassphrase",
147 "fee_mult_max": 7,
148 "fee_div_max": 4,
149 "tx_json": {
150 "Sequence": 0,
151 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
152 "Amount": "1000000000",
153 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
154 "TransactionType": "Payment"
155 }
156})",
157 {{"", "", "Missing field 'tx_json.Fee'.", "Missing field 'tx_json.SigningPubKey'."}}},
158
159 {"fee_mult_max is ignored if 'Fee' is present.",
160 __LINE__,
161 R"({
162 "command": "dummy_command",
163 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
164 "secret": "masterpassphrase",
165 "fee_mult_max": 0,
166 "tx_json": {
167 "Sequence": 0,
168 "Fee": 10,
169 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
170 "Amount": "1000000000",
171 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
172 "TransactionType": "Payment"
173 }
174})",
175 {{"",
176 "",
177 "A Signer may not be the transaction's Account "
178 "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
179 "Missing field 'tx_json.SigningPubKey'."}}},
180
181 {"fee_div_max is ignored if 'Fee' is present.",
182 __LINE__,
183 R"({
184 "command": "dummy_command",
185 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
186 "secret": "masterpassphrase",
187 "fee_mult_max": 100,
188 "fee_div_max": 1000,
189 "tx_json": {
190 "Sequence": 0,
191 "Fee": 10,
192 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
193 "Amount": "1000000000",
194 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
195 "TransactionType": "Payment"
196 }
197})",
198 {{"",
199 "",
200 "A Signer may not be the transaction's Account "
201 "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
202 "Missing field 'tx_json.SigningPubKey'."}}},
203
204 {"Invalid 'fee_mult_max' field.",
205 __LINE__,
206 R"({
207 "command": "dummy_command",
208 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
209 "secret": "masterpassphrase",
210 "fee_mult_max": "NotAFeeMultiplier",
211 "tx_json": {
212 "Sequence": 0,
213 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
214 "Amount": "1000000000",
215 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
216 "TransactionType": "Payment"
217 }
218})",
219 {{"Invalid field 'fee_mult_max', not a positive integer.",
220 "Invalid field 'fee_mult_max', not a positive integer.",
221 "Missing field 'tx_json.Fee'.",
222 "Missing field 'tx_json.SigningPubKey'."}}},
223
224 {"Invalid 'fee_div_max' field.",
225 __LINE__,
226 R"({
227 "command": "dummy_command",
228 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
229 "secret": "masterpassphrase",
230 "fee_mult_max": 5,
231 "fee_div_max": "NotAFeeMultiplier",
232 "tx_json": {
233 "Sequence": 0,
234 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
235 "Amount": "1000000000",
236 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
237 "TransactionType": "Payment"
238 }
239})",
240 {{"Invalid field 'fee_div_max', not a positive integer.",
241 "Invalid field 'fee_div_max', not a positive integer.",
242 "Missing field 'tx_json.Fee'.",
243 "Missing field 'tx_json.SigningPubKey'."}}},
244
245 {"Invalid value for 'fee_mult_max' field.",
246 __LINE__,
247 R"({
248 "command": "dummy_command",
249 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
250 "secret": "masterpassphrase",
251 "fee_mult_max": 0,
252 "tx_json": {
253 "Sequence": 0,
254 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
255 "Amount": "1000000000",
256 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
257 "TransactionType": "Payment"
258 }
259})",
260 {{"Fee of 10 exceeds the requested tx limit of 0",
261 "Fee of 10 exceeds the requested tx limit of 0",
262 "Missing field 'tx_json.Fee'.",
263 "Missing field 'tx_json.SigningPubKey'."}}},
264
265 {"Invalid value for 'fee_div_max' field.",
266 __LINE__,
267 R"({
268 "command": "dummy_command",
269 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
270 "secret": "masterpassphrase",
271 "fee_mult_max": 4,
272 "fee_div_max": 7,
273 "tx_json": {
274 "Sequence": 0,
275 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
276 "Amount": "1000000000",
277 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
278 "TransactionType": "Payment"
279 }
280})",
281 {{"Fee of 10 exceeds the requested tx limit of 5",
282 "Fee of 10 exceeds the requested tx limit of 5",
283 "Missing field 'tx_json.Fee'.",
284 "Missing field 'tx_json.SigningPubKey'."}}},
285
286 {"Invalid zero value for 'fee_div_max' field.",
287 __LINE__,
288 R"({
289 "command": "dummy_command",
290 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
291 "secret": "masterpassphrase",
292 "fee_mult_max": 4,
293 "fee_div_max": 0,
294 "tx_json": {
295 "Sequence": 0,
296 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
297 "Amount": "1000000000",
298 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
299 "TransactionType": "Payment"
300 }
301})",
302 {{"Invalid field 'fee_div_max', not a positive integer.",
303 "Invalid field 'fee_div_max', not a positive integer.",
304 "Missing field 'tx_json.Fee'.",
305 "Missing field 'tx_json.SigningPubKey'."}}},
306
307 {"Missing 'Amount'.",
308 __LINE__,
309 R"({
310 "command": "dummy_command",
311 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
312 "secret": "masterpassphrase",
313 "tx_json": {
314 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
315 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
316 "TransactionType": "Payment"
317 }
318})",
319 {{"Missing field 'tx_json.Amount'.",
320 "Missing field 'tx_json.Amount'.",
321 "Missing field 'tx_json.Sequence'.",
322 "Missing field 'tx_json.Sequence'."}}},
323
324 {"Invalid 'Amount'.",
325 __LINE__,
326 R"({
327 "command": "dummy_command",
328 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
329 "secret": "masterpassphrase",
330 "tx_json": {
331 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
332 "Amount": "NotAnAmount",
333 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
334 "TransactionType": "Payment"
335 }
336})",
337 {{"Invalid field 'tx_json.Amount'.",
338 "Invalid field 'tx_json.Amount'.",
339 "Missing field 'tx_json.Sequence'.",
340 "Missing field 'tx_json.Sequence'."}}},
341
342 {"Missing 'Destination'.",
343 __LINE__,
344 R"({
345 "command": "dummy_command",
346 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
347 "secret": "masterpassphrase",
348 "tx_json": {
349 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
350 "Amount": "1000000000",
351 "TransactionType": "Payment"
352 }
353})",
354 {{"Missing field 'tx_json.Destination'.",
355 "Missing field 'tx_json.Destination'.",
356 "Missing field 'tx_json.Sequence'.",
357 "Missing field 'tx_json.Sequence'."}}},
358
359 {"Invalid 'Destination'.",
360 __LINE__,
361 R"({
362 "command": "dummy_command",
363 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
364 "secret": "masterpassphrase",
365 "tx_json": {
366 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
367 "Amount": "1000000000",
368 "Destination": "NotADestination",
369 "TransactionType": "Payment"
370 }
371})",
372 {{"Invalid field 'tx_json.Destination'.",
373 "Invalid field 'tx_json.Destination'.",
374 "Missing field 'tx_json.Sequence'.",
375 "Missing field 'tx_json.Sequence'."}}},
376
377 {"Cannot create XRP to XRP paths.",
378 __LINE__,
379 R"({
380 "command": "dummy_command",
381 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
382 "secret": "masterpassphrase",
383 "build_path": 1,
384 "tx_json": {
385 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
386 "Amount": "1000000000",
387 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
388 "TransactionType": "Payment"
389 }
390})",
391 {{"Cannot build XRP to XRP paths.",
392 "Cannot build XRP to XRP paths.",
393 "Missing field 'tx_json.Sequence'.",
394 "Missing field 'tx_json.Sequence'."}}},
395
396 {"Successful 'build_path'.",
397 __LINE__,
398 R"({
399 "command": "dummy_command",
400 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
401 "secret": "masterpassphrase",
402 "build_path": 1,
403 "tx_json": {
404 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
405 "Amount": {
406 "value": "10",
407 "currency": "USD",
408 "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
409 },
410 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
411 "TransactionType": "Payment"
412 }
413})",
414 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
415
416 {"Not valid to include both 'Paths' and 'build_path'.",
417 __LINE__,
418 R"({
419 "command": "dummy_command",
420 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
421 "secret": "masterpassphrase",
422 "build_path": 1,
423 "tx_json": {
424 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
425 "Amount": {
426 "value": "10",
427 "currency": "USD",
428 "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
429 },
430 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
431 "Paths": "",
432 "TransactionType": "Payment"
433 }
434})",
435 {{"Cannot specify both 'tx_json.Paths' and 'build_path'",
436 "Cannot specify both 'tx_json.Paths' and 'build_path'",
437 "Missing field 'tx_json.Sequence'.",
438 "Missing field 'tx_json.Sequence'."}}},
439
440 {"Successful 'SendMax'.",
441 __LINE__,
442 R"({
443 "command": "dummy_command",
444 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
445 "secret": "masterpassphrase",
446 "build_path": 1,
447 "tx_json": {
448 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
449 "Amount": {
450 "value": "10",
451 "currency": "USD",
452 "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
453 },
454 "SendMax": {
455 "value": "5",
456 "currency": "USD",
457 "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
458 },
459 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
460 "TransactionType": "Payment"
461 }
462})",
463 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
464
465 {"'Amount' may not be XRP for pathfinding, but 'SendMax' may be XRP.",
466 __LINE__,
467 R"({
468 "command": "dummy_command",
469 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
470 "secret": "masterpassphrase",
471 "build_path": 1,
472 "tx_json": {
473 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
474 "Amount": {
475 "value": "10",
476 "currency": "USD",
477 "issuer": "rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4"
478 },
479 "SendMax": 10000,
480 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
481 "TransactionType": "Payment"
482 }
483})",
484 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
485
486 {"'secret' must be present.",
487 __LINE__,
488 R"({
489 "command": "dummy_command",
490 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
491 "tx_json": {
492 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
493 "Amount": "1000000000",
494 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
495 "TransactionType": "Payment"
496 }
497})",
498 {{"Missing field 'secret'.",
499 "Missing field 'secret'.",
500 "Missing field 'tx_json.Sequence'.",
501 "Missing field 'tx_json.Sequence'."}}},
502
503 {"'secret' must be non-empty.",
504 __LINE__,
505 R"({
506 "command": "dummy_command",
507 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
508 "secret": "",
509 "tx_json": {
510 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
511 "Amount": "1000000000",
512 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
513 "TransactionType": "Payment"
514 }
515})",
516 {{"Invalid field 'secret'.",
517 "Invalid field 'secret'.",
518 "Missing field 'tx_json.Sequence'.",
519 "Missing field 'tx_json.Sequence'."}}},
520
521 {"Use 'seed' instead of 'secret'.",
522 __LINE__,
523 R"({
524 "command": "dummy_command",
525 "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
526 "key_type": "ed25519",
527 "seed": "sh1yJfwoi98zCygwijUzuHmJDeVKd",
528 "tx_json": {
529 "Account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
530 "Amount": "1000000000",
531 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
532 "TransactionType": "Payment"
533 }
534})",
535 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
536
537 {"Malformed 'seed'.",
538 __LINE__,
539 R"({
540 "command": "dummy_command",
541 "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
542 "key_type": "ed25519",
543 "seed": "not a seed",
544 "tx_json": {
545 "Account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
546 "Amount": "1000000000",
547 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
548 "TransactionType": "Payment"
549 }
550})",
551 {{"Disallowed seed.",
552 "Disallowed seed.",
553 "Missing field 'tx_json.Sequence'.",
554 "Missing field 'tx_json.Sequence'."}}},
555
556 {"'tx_json' must be present.",
557 __LINE__,
558 R"({
559 "command": "dummy_command",
560 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
561 "secret": "masterpassphrase",
562 "rx_json": {
563 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
564 "Amount": "1000000000",
565 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
566 "TransactionType": "Payment"
567 }
568})",
569 {{"Missing field 'tx_json'.",
570 "Missing field 'tx_json'.",
571 "Missing field 'tx_json'.",
572 "Missing field 'tx_json'."}}},
573
574 {"'TransactionType' must be present.",
575 __LINE__,
576 R"({
577 "command": "dummy_command",
578 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
579 "secret": "masterpassphrase",
580 "tx_json": {
581 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
582 "Amount": "1000000000",
583 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
584 }
585})",
586 {{"Missing field 'tx_json.TransactionType'.",
587 "Missing field 'tx_json.TransactionType'.",
588 "Missing field 'tx_json.Sequence'.",
589 "Missing field 'tx_json.Sequence'."}}},
590
591 {"The 'TransactionType' must be a pre-established transaction type.",
592 __LINE__,
593 R"({
594 "command": "dummy_command",
595 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
596 "secret": "masterpassphrase",
597 "tx_json": {
598 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
599 "Amount": "1000000000",
600 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
601 "TransactionType": "tt"
602 }
603})",
604 {{"Field 'tx_json.TransactionType' has invalid data.",
605 "Field 'tx_json.TransactionType' has invalid data.",
606 "Missing field 'tx_json.Sequence'.",
607 "Missing field 'tx_json.Sequence'."}}},
608
609 {"The 'TransactionType' may be represented with an integer.",
610 __LINE__,
611 R"({
612 "command": "dummy_command",
613 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
614 "secret": "masterpassphrase",
615 "tx_json": {
616 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
617 "Amount": "1000000000",
618 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
619 "TransactionType": 0
620 }
621})",
622 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
623
624 {"'Account' must be present.",
625 __LINE__,
626 R"({
627 "command": "dummy_command",
628 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
629 "secret": "masterpassphrase",
630 "tx_json": {
631 "Amount": "1000000000",
632 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
633 "TransactionType": "Payment"
634 }
635})",
636 {{"Missing field 'tx_json.Account'.",
637 "Missing field 'tx_json.Account'.",
638 "Missing field 'tx_json.Sequence'.",
639 "Missing field 'tx_json.Sequence'."}}},
640
641 {"'Account' must be well formed.",
642 __LINE__,
643 R"({
644 "command": "dummy_command",
645 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
646 "secret": "masterpassphrase",
647 "tx_json": {
648 "Account": "NotAnAccount",
649 "Amount": "1000000000",
650 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
651 "TransactionType": "Payment"
652 }
653})",
654 {{"Invalid field 'tx_json.Account'.",
655 "Invalid field 'tx_json.Account'.",
656 "Missing field 'tx_json.Sequence'.",
657 "Missing field 'tx_json.Sequence'."}}},
658
659 {"The 'offline' tag may be added to the transaction.",
660 __LINE__,
661 R"({
662 "command": "dummy_command",
663 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
664 "secret": "masterpassphrase",
665 "offline": 0,
666 "tx_json": {
667 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
668 "Amount": "1000000000",
669 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
670 "TransactionType": "Payment"
671 }
672})",
673 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
674
675 {"If 'offline' is true then a 'Sequence' field must be supplied.",
676 __LINE__,
677 R"({
678 "command": "dummy_command",
679 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
680 "secret": "masterpassphrase",
681 "offline": 1,
682 "tx_json": {
683 "Fee": 10,
684 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
685 "Amount": "1000000000",
686 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
687 "TransactionType": "Payment"
688 }
689})",
690 {{"Missing field 'tx_json.Sequence'.",
691 "Missing field 'tx_json.Sequence'.",
692 "Missing field 'tx_json.Sequence'.",
693 "Missing field 'tx_json.Sequence'."}}},
694
695 {"If 'offline' is true then a 'Fee' field must be supplied.",
696 __LINE__,
697 R"({
698 "command": "dummy_command",
699 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
700 "secret": "masterpassphrase",
701 "offline": 1,
702 "tx_json": {
703 "Sequence": 0,
704 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
705 "Amount": "1000000000",
706 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
707 "TransactionType": "Payment"
708 }
709})",
710 {{"Missing field 'tx_json.Fee'.",
711 "Missing field 'tx_json.Fee'.",
712 "Missing field 'tx_json.Fee'.",
713 "Missing field 'tx_json.SigningPubKey'."}}},
714
715 {"Valid transaction if 'offline' is true.",
716 __LINE__,
717 R"({
718 "command": "dummy_command",
719 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
720 "secret": "masterpassphrase",
721 "offline": 1,
722 "tx_json": {
723 "Sequence": 0,
724 "Fee": 10,
725 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
726 "Amount": "1000000000",
727 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
728 "TransactionType": "Payment"
729 }
730})",
731 {{"",
732 "",
733 "A Signer may not be the transaction's Account "
734 "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh).",
735 "Missing field 'tx_json.SigningPubKey'."}}},
736
737 {"'offline' and 'build_path' are mutually exclusive.",
738 __LINE__,
739 R"({
740 "command": "dummy_command",
741 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
742 "secret": "masterpassphrase",
743 "offline": 1,
744 "build_path": 1,
745 "tx_json": {
746 "Sequence": 0,
747 "Fee": 10,
748 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
749 "Amount": "1000000000",
750 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
751 "TransactionType": "Payment"
752 }
753})",
754 {{"Field 'build_path' not allowed in this context.",
755 "Field 'build_path' not allowed in this context.",
756 "Field 'build_path' not allowed in this context.",
757 "Missing field 'tx_json.SigningPubKey'."}}},
758
759 {"A 'Flags' field may be specified.",
760 __LINE__,
761 R"({
762 "command": "dummy_command",
763 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
764 "secret": "masterpassphrase",
765 "tx_json": {
766 "Flags": 0,
767 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
768 "Amount": "1000000000",
769 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
770 "TransactionType": "Payment"
771 }
772})",
773 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
774
775 {"The 'Flags' field must be numeric.",
776 __LINE__,
777 R"({
778 "command": "dummy_command",
779 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
780 "secret": "masterpassphrase",
781 "tx_json": {
782 "Flags": "NotGoodFlags",
783 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
784 "Amount": "1000000000",
785 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
786 "TransactionType": "Payment"
787 }
788})",
789 {{"Field 'tx_json.Flags' has invalid data.",
790 "Field 'tx_json.Flags' has invalid data.",
791 "Missing field 'tx_json.Sequence'.",
792 "Missing field 'tx_json.Sequence'."}}},
793
794 {"It's okay to add a 'debug_signing' field.",
795 __LINE__,
796 R"({
797 "command": "dummy_command",
798 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
799 "secret": "masterpassphrase",
800 "debug_signing": 0,
801 "tx_json": {
802 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
803 "Amount": "1000000000",
804 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
805 "TransactionType": "Payment"
806 }
807})",
808 {{"", "", "Missing field 'tx_json.Sequence'.", "Missing field 'tx_json.Sequence'."}}},
809
810 {"Single-sign a multisigned transaction.",
811 __LINE__,
812 R"({
813 "command": "dummy_command",
814 "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
815 "secret": "a",
816 "tx_json": {
817 "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
818 "Amount" : "1000000000",
819 "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
820 "Fee" : "50",
821 "Sequence" : 0,
822 "Signers" : [
823 {
824 "Signer" : {
825 "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
826 "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
827 "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
828 }
829 }
830 ],
831 "SigningPubKey" : "",
832 "TransactionType" : "Payment"
833 }
834})",
835 {{"Already multisigned.", "Already multisigned.", "Secret does not match account.", ""}}},
836
837 {"Minimal sign_for.",
838 __LINE__,
839 R"({
840 "command": "dummy_command",
841 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
842 "secret": "masterpassphrase",
843 "tx_json": {
844 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
845 "Amount": "1000000000",
846 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
847 "Fee": 50,
848 "Sequence": 0,
849 "SigningPubKey": "",
850 "TransactionType": "Payment"
851 }
852})",
853 {{"Secret does not match account.", "Secret does not match account.", "", "Missing field 'tx_json.Signers'."}}},
854
855 {"Minimal offline sign_for.",
856 __LINE__,
857 R"({
858 "command": "dummy_command",
859 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
860 "secret": "masterpassphrase",
861 "offline": 1,
862 "tx_json": {
863 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
864 "Amount": "1000000000",
865 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
866 "Fee": 50,
867 "Sequence": 0,
868 "SigningPubKey": "",
869 "TransactionType": "Payment"
870 }
871})",
872 {{"", "", "", "Missing field 'tx_json.Signers'."}}},
873
874 {"Offline sign_for using 'seed' instead of 'secret'.",
875 __LINE__,
876 R"({
877 "command": "dummy_command",
878 "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
879 "key_type": "ed25519",
880 "seed": "sh1yJfwoi98zCygwijUzuHmJDeVKd",
881 "offline": 1,
882 "tx_json": {
883 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
884 "Amount": "1000000000",
885 "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
886 "Fee": 50,
887 "Sequence": 0,
888 "SigningPubKey": "",
889 "TransactionType": "Payment"
890 }
891})",
892 {{"", "", "", "Missing field 'tx_json.Signers'."}}},
893
894 {"Malformed seed in sign_for.",
895 __LINE__,
896 R"({
897 "command": "dummy_command",
898 "account": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
899 "key_type": "ed25519",
900 "seed": "sh1yJfwoi98zCygwjUzuHmJDeVKd",
901 "offline": 1,
902 "tx_json": {
903 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
904 "Amount": "1000000000",
905 "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
906 "Fee": 50,
907 "Sequence": 0,
908 "SigningPubKey": "",
909 "TransactionType": "Payment"
910 }
911})",
912 {{"Disallowed seed.", "Disallowed seed.", "Disallowed seed.", "Missing field 'tx_json.Signers'."}}},
913
914 {"Missing 'Account' in sign_for.",
915 __LINE__,
916 R"({
917 "command": "dummy_command",
918 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
919 "secret": "masterpassphrase",
920 "tx_json": {
921 "Amount": "1000000000",
922 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
923 "Fee": 50,
924 "Sequence": 0,
925 "SigningPubKey": "",
926 "TransactionType": "Payment"
927 }
928})",
929 {{"Missing field 'tx_json.Account'.",
930 "Missing field 'tx_json.Account'.",
931 "Missing field 'tx_json.Account'.",
932 "Missing field 'tx_json.Account'."}}},
933
934 {"Missing 'Amount' in sign_for.",
935 __LINE__,
936 R"({
937 "command": "dummy_command",
938 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
939 "secret": "masterpassphrase",
940 "tx_json": {
941 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
942 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
943 "Fee": 50,
944 "Sequence": 0,
945 "SigningPubKey": "",
946 "TransactionType": "Payment"
947 }
948})",
949 {{"Missing field 'tx_json.Amount'.",
950 "Missing field 'tx_json.Amount'.",
951 "Missing field 'tx_json.Amount'.",
952 "Missing field 'tx_json.Amount'."}}},
953
954 {"Missing 'Destination' in sign_for.",
955 __LINE__,
956 R"({
957 "command": "dummy_command",
958 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
959 "secret": "masterpassphrase",
960 "tx_json": {
961 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
962 "Amount": "1000000000",
963 "Fee": 50,
964 "Sequence": 0,
965 "SigningPubKey": "",
966 "TransactionType": "Payment"
967 }
968})",
969 {{"Missing field 'tx_json.Destination'.",
970 "Missing field 'tx_json.Destination'.",
971 "Missing field 'tx_json.Destination'.",
972 "Missing field 'tx_json.Destination'."}}},
973
974 {"Missing 'Destination' in sign_for, use DeliverMax",
975 __LINE__,
976 R"({
977 "command": "dummy_command",
978 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
979 "secret": "masterpassphrase",
980 "tx_json": {
981 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
982 "DeliverMax": "1000000000",
983 "Fee": 50,
984 "Sequence": 0,
985 "SigningPubKey": "",
986 "TransactionType": "Payment"
987 }
988})",
989 {{"Missing field 'tx_json.Destination'.",
990 "Missing field 'tx_json.Destination'.",
991 "Missing field 'tx_json.Destination'.",
992 "Missing field 'tx_json.Destination'."}}},
993
994 {"Missing 'Fee' in sign_for.",
995 __LINE__,
996 R"({
997 "command": "dummy_command",
998 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
999 "secret": "masterpassphrase",
1000 "tx_json": {
1001 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1002 "Amount": "1000000000",
1003 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1004 "Sequence": 0,
1005 "SigningPubKey": "",
1006 "TransactionType": "Payment"
1007 }
1008})",
1009 {{"Secret does not match account.",
1010 "Secret does not match account.",
1011 "Missing field 'tx_json.Fee'.",
1012 "Missing field 'tx_json.Fee'."}}},
1013
1014 {"Missing 'Sequence' in sign_for.",
1015 __LINE__,
1016 R"({
1017 "command": "dummy_command",
1018 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1019 "secret": "masterpassphrase",
1020 "tx_json": {
1021 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1022 "Amount": "1000000000",
1023 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1024 "Fee": 50,
1025 "SigningPubKey": "",
1026 "TransactionType": "Payment"
1027 }
1028})",
1029 {{"Secret does not match account.",
1030 "Secret does not match account.",
1031 "Missing field 'tx_json.Sequence'.",
1032 "Missing field 'tx_json.Sequence'."}}},
1033
1034 {"Missing 'SigningPubKey' in sign_for is automatically filled in.",
1035 __LINE__,
1036 R"({
1037 "command": "dummy_command",
1038 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1039 "secret": "masterpassphrase",
1040 "tx_json": {
1041 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1042 "Amount": "1000000000",
1043 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1044 "Fee": 50,
1045 "Sequence": 0,
1046 "TransactionType": "Payment"
1047 }
1048})",
1049 {{"Secret does not match account.",
1050 "Secret does not match account.",
1051 "",
1052 "Missing field 'tx_json.SigningPubKey'."}}},
1053
1054 {"In sign_for, an account may not sign for itself.",
1055 __LINE__,
1056 R"({
1057 "command": "dummy_command",
1058 "account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1059 "secret": "a",
1060 "tx_json": {
1061 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1062 "Amount": "1000000000",
1063 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1064 "Fee": 50,
1065 "Sequence": 0,
1066 "TransactionType": "Payment"
1067 }
1068})",
1069 {{"",
1070 "",
1071 "A Signer may not be the transaction's Account "
1072 "(rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA).",
1073 "Missing field 'tx_json.SigningPubKey'."}}},
1074
1075 {"Cannot put duplicate accounts in Signers array",
1076 __LINE__,
1077 R"({
1078 "command": "dummy_command",
1079 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1080 "secret": "masterpassphrase",
1081 "tx_json": {
1082 "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1083 "Amount" : "1000000000",
1084 "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1085 "Fee" : "50",
1086 "Sequence" : 0,
1087 "Signers" : [
1088 {
1089 "Signer" : {
1090 "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1091 "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1092 "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1093 }
1094 }
1095 ],
1096 "SigningPubKey" : "",
1097 "TransactionType" : "Payment"
1098 }
1099})",
1100 {{"Already multisigned.",
1101 "Already multisigned.",
1102 "Duplicate Signers:Signer:Account entries "
1103 "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh) are not allowed.",
1104 ""}}},
1105
1106 {"Correctly append to pre-established Signers array",
1107 __LINE__,
1108 R"({
1109 "command": "dummy_command",
1110 "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1111 "secret": "c",
1112 "tx_json": {
1113 "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1114 "Amount" : "1000000000",
1115 "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1116 "Fee" : "50",
1117 "Sequence" : 0,
1118 "Signers" : [
1119 {
1120 "Signer" : {
1121 "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1122 "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1123 "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1124 }
1125 }
1126 ],
1127 "SigningPubKey" : "",
1128 "TransactionType" : "Payment"
1129 }
1130})",
1131 {{"Already multisigned.", "Already multisigned.", "", ""}}},
1132
1133 {"Append to pre-established Signers array with bad signature",
1134 __LINE__,
1135 R"({
1136 "command": "dummy_command",
1137 "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1138 "secret": "c",
1139 "tx_json": {
1140 "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1141 "Amount" : "1000000000",
1142 "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1143 "Fee" : "50",
1144 "Sequence" : 0,
1145 "Signers" : [
1146 {
1147 "Signer" : {
1148 "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1149 "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1150 "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ACB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1151 }
1152 }
1153 ],
1154 "SigningPubKey" : "",
1155 "TransactionType" : "Payment"
1156 }
1157})",
1158 {{"Already multisigned.", "Already multisigned.", "Invalid signature.", "Invalid signature."}}},
1159
1160 {"Non-empty 'SigningPubKey' in sign_for.",
1161 __LINE__,
1162 R"({
1163 "command": "dummy_command",
1164 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1165 "secret": "masterpassphrase",
1166 "tx_json": {
1167 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1168 "Amount": "1000000000",
1169 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1170 "Fee": 50,
1171 "Sequence": 0,
1172 "SigningPubKey": "1",
1173 "TransactionType": "Payment"
1174 }
1175})",
1176 {{"Secret does not match account.",
1177 "Secret does not match account.",
1178 "When multi-signing 'tx_json.SigningPubKey' must be empty.",
1179 "When multi-signing 'tx_json.SigningPubKey' must be empty."}}},
1180
1181 {"Missing 'TransactionType' in sign_for.",
1182 __LINE__,
1183 R"({
1184 "command": "dummy_command",
1185 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1186 "secret": "masterpassphrase",
1187 "tx_json": {
1188 "Account": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1189 "Amount": "1000000000",
1190 "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1191 "Fee": 50,
1192 "Sequence": 0,
1193 "SigningPubKey": "",
1194 }
1195})",
1196 {{"Missing field 'tx_json.TransactionType'.",
1197 "Missing field 'tx_json.TransactionType'.",
1198 "Missing field 'tx_json.TransactionType'.",
1199 "Missing field 'tx_json.TransactionType'."}}},
1200
1201 {"TxnSignature in sign_for.",
1202 __LINE__,
1203 R"({
1204 "command": "dummy_command",
1205 "account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1206 "secret": "c",
1207 "tx_json": {
1208 "Account" : "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1209 "Amount" : "1000000000",
1210 "Destination" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1211 "Fee" : "50",
1212 "Sequence" : 0,
1213 "Signers" : [
1214 {
1215 "Signer" : {
1216 "Account" : "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1217 "SigningPubKey" : "0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020",
1218 "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998"
1219 }
1220 }
1221 ],
1222 "SigningPubKey" : "",
1223 "TxnSignature" : "304502210080EB23E78A841DDC5E3A4F10DE6EAF052207D6B519BF8954467ADB221B3F349002202CA458E8D4E4DE7176D27A91628545E7B295A5DFC8ADF0B5CD3E279B6FA02998",
1224 "TransactionType" : "Payment"
1225 }
1226})",
1227 {{"Already multisigned.",
1228 "Already multisigned.",
1229 "Already single-signed.",
1230 "Signing of transaction is malformed."}}},
1231
1232 {"Invalid field 'tx_json': string instead of object",
1233 __LINE__,
1234 R"({
1235 "command": "dummy_command",
1236 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1237 "secret": "masterpassphrase",
1238 "tx_json": ""
1239})",
1240 {{"Invalid field 'tx_json', not object.",
1241 "Invalid field 'tx_json', not object.",
1242 "Invalid field 'tx_json', not object.",
1243 "Invalid field 'tx_json', not object."}}},
1244
1245 {"Invalid field 'tx_json': integer instead of object",
1246 __LINE__,
1247 R"({
1248 "command": "dummy_command",
1249 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1250 "secret": "masterpassphrase",
1251 "tx_json": 20160331
1252})",
1253 {{"Invalid field 'tx_json', not object.",
1254 "Invalid field 'tx_json', not object.",
1255 "Invalid field 'tx_json', not object.",
1256 "Invalid field 'tx_json', not object."}}},
1257
1258 {"Invalid field 'tx_json': array instead of object",
1259 __LINE__,
1260 R"({
1261 "command": "dummy_command",
1262 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1263 "secret": "masterpassphrase",
1264 "tx_json": [ "hello", "world" ]
1265})",
1266 {{"Invalid field 'tx_json', not object.",
1267 "Invalid field 'tx_json', not object.",
1268 "Invalid field 'tx_json', not object.",
1269 "Invalid field 'tx_json', not object."}}},
1270
1271 {"Pass in Fee with minimal payment, both Amount and DeliverMax.",
1272 __LINE__,
1273 R"({
1274 "command": "dummy_command",
1275 "account": "r9zN9x52FiCFAcicCLMQKbj1nxYhxJbbSy",
1276 "secret": "ssgN6zTvtM1q9XV8DvJpWm8LBYWiY",
1277 "tx_json": {
1278 "Fee": 10,
1279 "Account": "r9zN9x52FiCFAcicCLMQKbj1nxYhxJbbSy",
1280 "Amount": "1000000000",
1281 "DeliverMax": "1000000000",
1282 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1283 "TransactionType": "Payment"
1284 }
1285})",
1286 {{"Source account not found.",
1287 "Source account not found.",
1288 "Missing field 'tx_json.Sequence'.",
1289 "Missing field 'tx_json.Sequence'."}}},
1290
1291 {"Minimal submit_multisigned.",
1292 __LINE__,
1293 R"({
1294 "command": "submit_multisigned",
1295 "tx_json": {
1296 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1297 "Amount": "1000000000",
1298 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1299 "Fee": 50,
1300 "Sequence": 0,
1301 "Signers" : [
1302 {
1303 "Signer" : {
1304 "Account" : "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1305 "SigningPubKey" : "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8",
1306 "TxnSignature" : "3045022100909D01399AFFAD1E30D250CE61F93975B7F61E47B5244D78C3E86D9806535D95022012E389E0ACB016334052B7FE07FA6CEFDC8BE82CB410FA841D5049641C89DC8F"
1307 }
1308 }
1309 ],
1310 "SigningPubKey": "",
1311 "TransactionType": "Payment"
1312 }
1313})",
1314 {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", ""}}},
1315
1316 {"Minimal submit_multisigned with bad signature.",
1317 __LINE__,
1318 R"({
1319 "command": "submit_multisigned",
1320 "tx_json": {
1321 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1322 "Amount": "1000000000",
1323 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1324 "Fee": 50,
1325 "Sequence": 0,
1326 "Signers": [
1327 {
1328 "Signer": {
1329 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1330 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1331 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1332 }
1333 }
1334 ],
1335 "SigningPubKey": "",
1336 "TransactionType": "Payment"
1337 }
1338})",
1339 {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", "Invalid signature."}}},
1340
1341 {"Missing tx_json in submit_multisigned.",
1342 __LINE__,
1343 R"({
1344 "command": "submit_multisigned",
1345 "Signers": [
1346 {
1347 "Signer": {
1348 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1349 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1350 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1351 }
1352 }
1353 ]
1354})",
1355 {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", "Missing field 'tx_json'."}}},
1356
1357 {"Missing sequence in submit_multisigned.",
1358 __LINE__,
1359 R"({
1360 "command": "submit_multisigned",
1361 "tx_json": {
1362 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1363 "Amount": "1000000000",
1364 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1365 "Fee": 50,
1366 "Signers": [
1367 {
1368 "Signer": {
1369 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1370 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1371 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1372 }
1373 }
1374 ],
1375 "SigningPubKey": "",
1376 "TransactionType": "Payment"
1377 }
1378})",
1379 {{"Missing field 'secret'.",
1380 "Missing field 'secret'.",
1381 "Missing field 'account'.",
1382 "Missing field 'tx_json.Sequence'."}}},
1383
1384 {"Missing SigningPubKey in submit_multisigned.",
1385 __LINE__,
1386 R"({
1387 "command": "submit_multisigned",
1388 "tx_json": {
1389 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1390 "Amount": "1000000000",
1391 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1392 "Fee": 50,
1393 "Signers": [
1394 {
1395 "Signer": {
1396 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1397 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1398 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1399 }
1400 }
1401 ],
1402 "Sequence": 0,
1403 "TransactionType": "Payment"
1404 }
1405})",
1406 {{"Missing field 'secret'.",
1407 "Missing field 'secret'.",
1408 "Missing field 'account'.",
1409 "Missing field 'tx_json.SigningPubKey'."}}},
1410
1411 {"Non-empty SigningPubKey in submit_multisigned.",
1412 __LINE__,
1413 R"({
1414 "command": "submit_multisigned",
1415 "tx_json": {
1416 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1417 "Amount": "1000000000",
1418 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1419 "Fee": 50,
1420 "Sequence": 0,
1421 "Signers": [
1422 {
1423 "Signer": {
1424 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1425 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1426 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1427 }
1428 }
1429 ],
1430 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8",
1431 "TransactionType": "Payment"
1432 }
1433})",
1434 {{"Missing field 'secret'.",
1435 "Missing field 'secret'.",
1436 "Missing field 'account'.",
1437 "When multi-signing 'tx_json.SigningPubKey' must be empty."}}},
1438
1439 {"Missing TransactionType in submit_multisigned.",
1440 __LINE__,
1441 R"({
1442 "command": "submit_multisigned",
1443 "tx_json": {
1444 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1445 "Amount": "1000000000",
1446 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1447 "Fee": 50,
1448 "Signers": [
1449 {
1450 "Signer": {
1451 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1452 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1453 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1454 }
1455 }
1456 ],
1457 "Sequence": 0,
1458 "SigningPubKey": "",
1459 }
1460})",
1461 {{"Missing field 'secret'.",
1462 "Missing field 'secret'.",
1463 "Missing field 'account'.",
1464 "Missing field 'tx_json.TransactionType'."}}},
1465
1466 {"Missing Account in submit_multisigned.",
1467 __LINE__,
1468 R"({
1469 "command": "submit_multisigned",
1470 "tx_json": {
1471 "Amount": "1000000000",
1472 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1473 "Fee": 50,
1474 "Sequence": 0,
1475 "Signers": [
1476 {
1477 "Signer": {
1478 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1479 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1480 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1481 }
1482 }
1483 ],
1484 "SigningPubKey": "",
1485 "TransactionType": "Payment"
1486 }
1487})",
1488 {{"Missing field 'secret'.",
1489 "Missing field 'secret'.",
1490 "Missing field 'account'.",
1491 "Missing field 'tx_json.Account'."}}},
1492
1493 {"Malformed Account in submit_multisigned.",
1494 __LINE__,
1495 R"({
1496 "command": "submit_multisigned",
1497 "tx_json": {
1498 "Account": "NotAnAccount",
1499 "Amount": "1000000000",
1500 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1501 "Fee": 50,
1502 "Sequence": 0,
1503 "Signers": [
1504 {
1505 "Signer": {
1506 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1507 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1508 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1509 }
1510 }
1511 ],
1512 "SigningPubKey": "",
1513 "TransactionType": "Payment"
1514 }
1515})",
1516 {{"Missing field 'secret'.",
1517 "Missing field 'secret'.",
1518 "Missing field 'account'.",
1519 "Invalid field 'tx_json.Account'."}}},
1520
1521 {"Account not in ledger in submit_multisigned.",
1522 __LINE__,
1523 R"({
1524 "command": "submit_multisigned",
1525 "tx_json": {
1526 "Account": "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd",
1527 "Amount": "1000000000",
1528 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1529 "Fee": 50,
1530 "Sequence": 0,
1531 "Signers": [
1532 {
1533 "Signer": {
1534 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1535 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1536 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1537 }
1538 }
1539 ],
1540 "SigningPubKey": "",
1541 "TransactionType": "Payment"
1542 }
1543})",
1544 {{"Missing field 'secret'.", "Missing field 'secret'.", "Missing field 'account'.", "Source account not found."}}},
1545
1546 {"Missing Fee in submit_multisigned.",
1547 __LINE__,
1548 R"({
1549 "command": "submit_multisigned",
1550 "tx_json": {
1551 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1552 "Amount": "1000000000",
1553 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1554 "Sequence": 0,
1555 "Signers": [
1556 {
1557 "Signer": {
1558 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1559 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1560 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1561 }
1562 }
1563 ],
1564 "SigningPubKey": "",
1565 "TransactionType": "Payment"
1566 }
1567})",
1568 {{"Missing field 'secret'.",
1569 "Missing field 'secret'.",
1570 "Missing field 'account'.",
1571 "Missing field 'tx_json.Fee'."}}},
1572
1573 {"Non-numeric Fee in submit_multisigned.",
1574 __LINE__,
1575 R"({
1576 "command": "submit_multisigned",
1577 "tx_json": {
1578 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1579 "Amount": "1000000000",
1580 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1581 "Fee": 50.1,
1582 "Sequence": 0,
1583 "Signers": [
1584 {
1585 "Signer": {
1586 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1587 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1588 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1589 }
1590 }
1591 ],
1592 "SigningPubKey": "",
1593 "TransactionType": "Payment"
1594 }
1595})",
1596 {{"Missing field 'secret'.",
1597 "Missing field 'secret'.",
1598 "Missing field 'account'.",
1599 "Field 'tx_json.Fee' has invalid data."}}},
1600
1601 {"Missing Amount in submit_multisigned Payment.",
1602 __LINE__,
1603 R"({
1604 "command": "submit_multisigned",
1605 "tx_json": {
1606 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1607 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1608 "Fee": 50000000,
1609 "Sequence": 0,
1610 "Signers": [
1611 {
1612 "Signer": {
1613 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1614 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1615 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1616 }
1617 }
1618 ],
1619 "SigningPubKey": "",
1620 "TransactionType": "Payment"
1621 }
1622})",
1623 {{"Missing field 'secret'.",
1624 "Missing field 'secret'.",
1625 "Missing field 'account'.",
1626 "Missing field 'tx_json.Amount'."}}},
1627
1628 {"Invalid Amount in submit_multisigned Payment.",
1629 __LINE__,
1630 R"({
1631 "command": "submit_multisigned",
1632 "tx_json": {
1633 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1634 "Amount": "NotANumber",
1635 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1636 "Fee": 50,
1637 "Sequence": 0,
1638 "Signers": [
1639 {
1640 "Signer": {
1641 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1642 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1643 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1644 }
1645 }
1646 ],
1647 "SigningPubKey": "",
1648 "TransactionType": "Payment"
1649 }
1650})",
1651 {{"Missing field 'secret'.",
1652 "Missing field 'secret'.",
1653 "Missing field 'account'.",
1654 "Invalid field 'tx_json.Amount'."}}},
1655
1656 {"Invalid DeliverMax in submit_multisigned Payment.",
1657 __LINE__,
1658 R"({
1659 "command": "submit_multisigned",
1660 "tx_json": {
1661 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1662 "DeliverMax": "NotANumber",
1663 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1664 "Fee": 50,
1665 "Sequence": 0,
1666 "Signers": [
1667 {
1668 "Signer": {
1669 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1670 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1671 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1672 }
1673 }
1674 ],
1675 "SigningPubKey": "",
1676 "TransactionType": "Payment"
1677 }
1678})",
1679 {{"Missing field 'secret'.",
1680 "Missing field 'secret'.",
1681 "Missing field 'account'.",
1682 "Invalid field 'tx_json.Amount'."}}},
1683
1684 {"No build_path in submit_multisigned.",
1685 __LINE__,
1686 R"({
1687 "command": "submit_multisigned",
1688 "build_path": 1,
1689 "tx_json": {
1690 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1691 "Amount": "1000000000",
1692 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1693 "Fee": 50,
1694 "Sequence": 0,
1695 "Signers": [
1696 {
1697 "Signer": {
1698 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1699 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1700 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1701 }
1702 }
1703 ],
1704 "SigningPubKey": "",
1705 "TransactionType": "Payment"
1706 }
1707})",
1708 {{"Missing field 'secret'.",
1709 "Missing field 'secret'.",
1710 "Missing field 'account'.",
1711 "Field 'build_path' not allowed in this context."}}},
1712
1713 {"Missing Destination in submit_multisigned Payment.",
1714 __LINE__,
1715 R"({
1716 "command": "submit_multisigned",
1717 "tx_json": {
1718 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1719 "Amount": "1000000000",
1720 "Fee": 50,
1721 "Sequence": 0,
1722 "Signers": [
1723 {
1724 "Signer": {
1725 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1726 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1727 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1728 }
1729 }
1730 ],
1731 "SigningPubKey": "",
1732 "TransactionType": "Payment"
1733 }
1734})",
1735 {{"Missing field 'secret'.",
1736 "Missing field 'secret'.",
1737 "Missing field 'account'.",
1738 "Missing field 'tx_json.Destination'."}}},
1739
1740 {"Malformed Destination in submit_multisigned Payment.",
1741 __LINE__,
1742 R"({
1743 "command": "submit_multisigned",
1744 "tx_json": {
1745 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1746 "Amount": "1000000000",
1747 "Destination": "NotADestination",
1748 "Fee": 50,
1749 "Sequence": 0,
1750 "Signers": [
1751 {
1752 "Signer": {
1753 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1754 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1755 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1756 }
1757 }
1758 ],
1759 "SigningPubKey": "",
1760 "TransactionType": "Payment"
1761 }
1762})",
1763 {{"Missing field 'secret'.",
1764 "Missing field 'secret'.",
1765 "Missing field 'account'.",
1766 "Invalid field 'tx_json.Destination'."}}},
1767
1768 {"Missing Signers field in submit_multisigned.",
1769 __LINE__,
1770 R"({
1771 "command": "submit_multisigned",
1772 "tx_json": {
1773 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1774 "Amount": "1000000000",
1775 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1776 "Fee": 50,
1777 "Sequence": 0,
1778 "SigningPubKey": "",
1779 "TransactionType": "Payment"
1780 }
1781})",
1782 {{"Missing field 'secret'.",
1783 "Missing field 'secret'.",
1784 "Missing field 'account'.",
1785 "Missing field 'tx_json.Signers'."}}},
1786
1787 {"Signers not an array in submit_multisigned.",
1788 __LINE__,
1789 R"({
1790 "command": "submit_multisigned",
1791 "tx_json": {
1792 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1793 "Amount": "1000000000",
1794 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1795 "Fee": 50,
1796 "Sequence": 0,
1797 "Signers": {
1798 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1799 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1800 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1801 },
1802 "SigningPubKey": "",
1803 "TransactionType": "Payment"
1804 }
1805})",
1806 {{"Missing field 'secret'.",
1807 "Missing field 'secret'.",
1808 "Missing field 'account'.",
1809 "Field 'tx_json.Signers' is not a JSON array."}}},
1810
1811 {"Empty Signers array in submit_multisigned.",
1812 __LINE__,
1813 R"({
1814 "command": "submit_multisigned",
1815 "tx_json": {
1816 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1817 "Amount": "1000000000",
1818 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1819 "Fee": 50,
1820 "Sequence": 0,
1821 "Signers": [
1822 ],
1823 "SigningPubKey": "",
1824 "TransactionType": "Payment"
1825 }
1826})",
1827 {{"Missing field 'secret'.",
1828 "Missing field 'secret'.",
1829 "Missing field 'account'.",
1830 "tx_json.Signers array may not be empty."}}},
1831
1832 {"Duplicate Signer in submit_multisigned.",
1833 __LINE__,
1834 R"({
1835 "command": "submit_multisigned",
1836 "tx_json": {
1837 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1838 "Amount": "1000000000",
1839 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1840 "Fee": 50,
1841 "Sequence": 0,
1842 "Signers": [
1843 {
1844 "Signer": {
1845 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1846 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1847 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1848 }
1849 },
1850 {
1851 "Signer": {
1852 "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
1853 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1854 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1855 }
1856 }
1857 ],
1858 "SigningPubKey": "",
1859 "TransactionType": "Payment"
1860 }
1861})",
1862 {{"Missing field 'secret'.",
1863 "Missing field 'secret'.",
1864 "Missing field 'account'.",
1865 "Duplicate Signers:Signer:Account entries "
1866 "(rPcNzota6B8YBokhYtcTNqQVCngtbnWfux) are not allowed."}}},
1867
1868 {"Signer is tx_json Account in submit_multisigned.",
1869 __LINE__,
1870 R"({
1871 "command": "submit_multisigned",
1872 "tx_json": {
1873 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1874 "Amount": "1000000000",
1875 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1876 "Fee": 50,
1877 "Sequence": 0,
1878 "Signers": [
1879 {
1880 "Signer": {
1881 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1882 "TxnSignature": "3045022100F9ED357606932697A4FAB2BE7F222C21DD93CA4CFDD90357AADD07465E8457D6022038173193E3DFFFB5D78DD738CC0905395F885DA65B98FDB9793901FE3FD26ECE",
1883 "SigningPubKey": "02FE36A690D6973D55F88553F5D2C4202DE75F2CF8A6D0E17C70AC223F044501F8"
1884 }
1885 }
1886 ],
1887 "SigningPubKey": "",
1888 "TransactionType": "Payment"
1889 }
1890})",
1891 {{"Missing field 'secret'.",
1892 "Missing field 'secret'.",
1893 "Missing field 'account'.",
1894 "A Signer may not be the transaction's Account "
1895 "(rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh)."}}},
1896
1897 {"Empty Signers array in submit_multisigned, use DeliverMax",
1898 __LINE__,
1899 R"({
1900 "command": "submit_multisigned",
1901 "tx_json": {
1902 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1903 "DeliverMax": "10000000",
1904 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1905 "Fee": 50,
1906 "Sequence": 0,
1907 "Signers": [
1908 ],
1909 "SigningPubKey": "",
1910 "TransactionType": "Payment"
1911 }
1912})",
1913 {{"Missing field 'secret'.",
1914 "Missing field 'secret'.",
1915 "Missing field 'account'.",
1916 "tx_json.Signers array may not be empty."}}},
1917
1918 {"Empty Signers array in submit_multisigned, use DeliverMax and Amount",
1919 __LINE__,
1920 R"({
1921 "command": "submit_multisigned",
1922 "tx_json": {
1923 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1924 "Amount": "10000000",
1925 "DeliverMax": "10000000",
1926 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1927 "Fee": 50,
1928 "Sequence": 0,
1929 "Signers": [
1930 ],
1931 "SigningPubKey": "",
1932 "TransactionType": "Payment"
1933 }
1934})",
1935 {{"Missing field 'secret'.",
1936 "Missing field 'secret'.",
1937 "Missing field 'account'.",
1938 "tx_json.Signers array may not be empty."}}},
1939
1940 {"Payment cannot specify different DeliverMax and Amount.",
1941 __LINE__,
1942 R"({
1943 "command": "dummy_command",
1944 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1945 "secret": "masterpassphrase",
1946 "debug_signing": 0,
1947 "tx_json": {
1948 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1949 "Amount": "1000000000",
1950 "DeliverMax": "1000000020",
1951 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1952 "Fee": 50,
1953 "Sequence": 0,
1954 "SigningPubKey": "",
1955 "TransactionType": "Payment"
1956 }
1957})",
1958 {{"Cannot specify differing 'Amount' and 'DeliverMax'",
1959 "Cannot specify differing 'Amount' and 'DeliverMax'",
1960 "Cannot specify differing 'Amount' and 'DeliverMax'",
1961 "Cannot specify differing 'Amount' and 'DeliverMax'"}}},
1962 {"Payment cannot specify bad DomainID.",
1963 __LINE__,
1964 R"({
1965 "command": "dummy_command",
1966 "account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1967 "secret": "masterpassphrase",
1968 "debug_signing": 0,
1969 "tx_json": {
1970 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1971 "Amount": "1000000000",
1972 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1973 "Fee": 50,
1974 "Sequence": 0,
1975 "SigningPubKey": "",
1976 "TransactionType": "Payment",
1977 "DomainID": "invalid",
1978 }
1979})",
1980 {{"Unable to parse 'DomainID'.",
1981 "Unable to parse 'DomainID'.",
1982 "Unable to parse 'DomainID'.",
1983 "Unable to parse 'DomainID'."}}},
1984
1985 {"Minimal delegated transaction.",
1986 __LINE__,
1987 R"({
1988 "command": "dummy_command",
1989 "secret": "a",
1990 "tx_json": {
1991 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
1992 "Amount": "1000000000",
1993 "Destination": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA",
1994 "TransactionType": "Payment",
1995 "Delegate": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA"
1996 }
1997})",
1998 {{"", "", "Missing field 'account'.", "Missing field 'tx_json.Sequence'."}}},
1999
2000 {"Delegate not well formed.",
2001 __LINE__,
2002 R"({
2003 "command": "dummy_command",
2004 "secret": "a",
2005 "tx_json": {
2006 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
2007 "Amount": "1000000000",
2008 "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
2009 "TransactionType": "Payment",
2010 "Delegate": "NotAnAccount"
2011 }
2012})",
2013 {{"Invalid field 'tx_json.Delegate'.",
2014 "Invalid field 'tx_json.Delegate'.",
2015 "Missing field 'account'.",
2016 "Missing field 'tx_json.Sequence'."}}},
2017
2018 {"Delegate not in ledger.",
2019 __LINE__,
2020 R"({
2021 "command": "dummy_command",
2022 "secret": "a",
2023 "tx_json": {
2024 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
2025 "Amount": "1000000000",
2026 "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
2027 "TransactionType": "Payment",
2028 "Delegate": "rDg53Haik2475DJx8bjMDSDPj4VX7htaMd"
2029 }
2030})",
2031 {{"Delegate account not found.",
2032 "Delegate account not found.",
2033 "Missing field 'account'.",
2034 "Missing field 'tx_json.Sequence'."}}},
2035
2036 {"Delegate and secret not match.",
2037 __LINE__,
2038 R"({
2039 "command": "dummy_command",
2040 "secret": "aa",
2041 "tx_json": {
2042 "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
2043 "Amount": "1000000000",
2044 "Destination": "rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi",
2045 "TransactionType": "Payment",
2046 "Delegate": "rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA"
2047 }
2048})",
2049 {{"Secret does not match account.",
2050 "Secret does not match account.",
2051 "Missing field 'account'.",
2052 "Missing field 'tx_json.Sequence'."}}},
2053
2054};
2055
2057{
2058public:
2059 void
2061 {
2062 testcase("bad RPC command");
2063 test::jtx::Env env(*this);
2064 Json::Value const result{env.rpc("bad_command", R"({"MakingThisUp": 0})")};
2065
2066 BEAST_EXPECT(result[jss::result][jss::error] == "unknownCmd");
2067 BEAST_EXPECT(result[jss::result][jss::request][jss::command] == "bad_command");
2068 }
2069
2070 void
2072 {
2073 testcase("autofill fails");
2074 using namespace test::jtx;
2075
2076 // test batch raw transactions max size
2077 {
2078 Env env(*this);
2079 auto ledger = env.current();
2080 auto const& feeTrack = env.app().getFeeTrack();
2081 Json::Value req;
2082 Account const alice("alice");
2083 Account const bob("bob");
2084 env.fund(XRP(100000), alice);
2085 env.close();
2086
2087 auto const batchFee = batch::calcBatchFee(env, 0, 2);
2088 auto const seq = env.seq(alice);
2089 auto jt = env.jtnofill(
2090 batch::outer(alice, env.seq(alice), batchFee, tfAllOrNothing),
2091 batch::inner(pay(alice, bob, XRP(1)), seq + 1),
2092 batch::inner(pay(alice, bob, XRP(2)), seq + 2),
2093 batch::inner(pay(alice, bob, XRP(3)), seq + 3),
2094 batch::inner(pay(alice, bob, XRP(4)), seq + 4),
2095 batch::inner(pay(alice, bob, XRP(5)), seq + 5),
2096 batch::inner(pay(alice, bob, XRP(6)), seq + 6),
2097 batch::inner(pay(alice, bob, XRP(7)), seq + 7),
2098 batch::inner(pay(alice, bob, XRP(8)), seq + 8),
2099 batch::inner(pay(alice, bob, XRP(9)), seq + 9));
2100
2101 jt.jv.removeMember(jss::Fee);
2102 jt.jv.removeMember(jss::TxnSignature);
2103 req[jss::tx_json] = jt.jv;
2104 Json::Value result =
2105 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2106 BEAST_EXPECT(result.size() == 0);
2107 BEAST_EXPECT(
2108 req[jss::tx_json].isMember(jss::Fee) &&
2109 req[jss::tx_json][jss::Fee] == env.current()->fees().base.jsonClipped());
2110 }
2111
2112 // test signers max size
2113 {
2114 Env env(*this);
2115 auto ledger = env.current();
2116 auto const& feeTrack = env.app().getFeeTrack();
2117 Json::Value req;
2118 Account const alice("alice");
2119 Account const bob("bob");
2120 env.fund(XRP(100000), alice, bob);
2121 env.close();
2122
2123 auto jt = env.jtnofill(
2124 noop(alice),
2125 msig(
2126 alice,
2127 alice,
2128 alice,
2129 alice,
2130 alice,
2131 alice,
2132 alice,
2133 alice,
2134 alice,
2135 alice,
2136 alice,
2137 alice,
2138 alice,
2139 alice,
2140 alice,
2141 alice,
2142 alice,
2143 alice,
2144 alice,
2145 alice,
2146 alice,
2147 alice,
2148 alice,
2149 alice,
2150 alice,
2151 alice,
2152 alice,
2153 alice,
2154 alice,
2155 alice,
2156 alice,
2157 alice,
2158 alice,
2159 alice,
2160 alice,
2161 alice,
2162 alice,
2163 alice,
2164 alice,
2165 alice));
2166
2167 req[jss::tx_json] = jt.jv;
2168 Json::Value result =
2169 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2170 BEAST_EXPECT(result.size() == 0);
2171 BEAST_EXPECT(
2172 req[jss::tx_json].isMember(jss::Fee) &&
2173 req[jss::tx_json][jss::Fee] == env.current()->fees().base.jsonClipped());
2174 }
2175 }
2176
2177 void
2179 {
2180 testcase("autofill fees");
2181 test::jtx::Env env(*this);
2182 auto const baseFee = static_cast<int>(env.current()->fees().base.drops());
2183 auto ledger = env.current();
2184 auto const& feeTrack = env.app().getFeeTrack();
2185
2186 {
2187 Json::Value req;
2188 Json::Reader().parse("{ \"fee_mult_max\" : 1, \"tx_json\" : { } } ", req);
2189 Json::Value result =
2190 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2191
2192 BEAST_EXPECT(!RPC::contains_error(result));
2193 BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == baseFee);
2194 }
2195
2196 {
2197 Json::Value req;
2199 "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 2, "
2200 "\"tx_json\" : { } } ",
2201 req);
2202 Json::Value result =
2203 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2204
2205 BEAST_EXPECT(!RPC::contains_error(result));
2206 BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == baseFee);
2207 }
2208
2209 {
2210 Json::Value req;
2211 Json::Reader().parse("{ \"fee_mult_max\" : 0, \"tx_json\" : { } } ", req);
2212 Json::Value result =
2213 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2214
2215 BEAST_EXPECT(RPC::contains_error(result));
2216 BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2217 }
2218
2219 {
2220 // 3/6 = 1/2, but use the bigger number make sure
2221 // we're dividing.
2222 Json::Value req;
2224 "{ \"fee_mult_max\" : 3, \"fee_div_max\" : 6, "
2225 "\"tx_json\" : { } } ",
2226 req);
2227 Json::Value result =
2228 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2229
2230 BEAST_EXPECT(RPC::contains_error(result));
2231 BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2232 }
2233
2234 {
2235 Json::Value req;
2237 "{ \"fee_mult_max\" : 0, \"fee_div_max\" : 2, "
2238 "\"tx_json\" : { } } ",
2239 req);
2240 Json::Value result =
2241 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2242
2243 BEAST_EXPECT(RPC::contains_error(result));
2244 BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2245 }
2246
2247 {
2248 Json::Value req;
2250 "{ \"fee_mult_max\" : 10, \"fee_div_max\" : 0, "
2251 "\"tx_json\" : { } } ",
2252 req);
2253 Json::Value result =
2254 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2255
2256 BEAST_EXPECT(RPC::contains_error(result));
2257 BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2258 }
2259
2260 {
2261 // transaction with a higher base fee
2262 Json::Value req;
2263 test::jtx::Account const alice("alice");
2264 req[jss::tx_json] = test::jtx::acctdelete(env.master.human(), alice.human());
2265 Json::Value result =
2266 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrack, env.app().getTxQ(), env.app());
2267
2268 BEAST_EXPECT(result.size() == 0);
2269 BEAST_EXPECT(
2270 req[jss::tx_json].isMember(jss::Fee) &&
2271 req[jss::tx_json][jss::Fee] == env.current()->fees().increment.jsonClipped());
2272 }
2273 }
2274
2275 void
2277 {
2278 testcase("autofill escalated fees");
2279 using namespace test::jtx;
2280 Env env{*this, envconfig([](std::unique_ptr<Config> cfg) {
2281 cfg->loadFromString("[" SECTION_SIGNING_SUPPORT "]\ntrue");
2282 cfg->section("transaction_queue").set("minimum_txn_in_ledger_standalone", "3");
2283 return cfg;
2284 })};
2285 LoadFeeTrack const& feeTrackOuter = env.app().getFeeTrack();
2286
2287 {
2288 // high mult, no tx
2289 Json::Value req;
2291 R"({
2292 "fee_mult_max" : 1000,
2293 "tx_json" : { }
2294 })",
2295 req);
2296 Json::Value result =
2297 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2298
2299 BEAST_EXPECT(!RPC::contains_error(result));
2300 BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 10);
2301 }
2302
2303 {
2304 // low mult, no tx
2305 Json::Value req;
2307 R"({
2308 "fee_mult_max" : 5,
2309 "tx_json" : { }
2310 })",
2311 req);
2312 Json::Value result =
2313 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2314
2315 BEAST_EXPECT(!RPC::contains_error(result));
2316 BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 10);
2317 }
2318
2319 // put 4 transactions into the open ledger
2320 for (auto i = 0; i < 4; ++i)
2321 {
2322 env(noop(env.master));
2323 }
2324
2325 {
2326 // high mult, 4 txs
2327 Json::Value req;
2329 R"({
2330 "fee_mult_max" : 1000,
2331 "tx_json" : { }
2332 })",
2333 req);
2334 Json::Value result =
2335 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2336
2337 BEAST_EXPECT(!RPC::contains_error(result));
2338 BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 8889);
2339 }
2340
2341 {
2342 // low mult, 4 tx
2343 Json::Value req;
2345 R"({
2346 "fee_mult_max" : 5,
2347 "tx_json" : { }
2348 })",
2349 req);
2350 Json::Value result =
2351 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2352
2353 BEAST_EXPECT(RPC::contains_error(result));
2354 BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2355 }
2356
2357 {
2358 // different low mult, 4 tx
2359 Json::Value req;
2361 R"({
2362 "fee_mult_max" : 1000,
2363 "fee_div_max" : 3,
2364 "tx_json" : { }
2365 })",
2366 req);
2367 Json::Value result =
2368 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2369
2370 BEAST_EXPECT(RPC::contains_error(result));
2371 BEAST_EXPECT(!req[jss::tx_json].isMember(jss::Fee));
2372 }
2373
2374 {
2375 // high mult, 4 tx
2376 Json::Value req;
2378 R"({
2379 "fee_mult_max" : 8000,
2380 "fee_div_max" : 3,
2381 "tx_json" : { }
2382 })",
2383 req);
2384 Json::Value result =
2385 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2386
2387 BEAST_EXPECT(!RPC::contains_error(result));
2388 BEAST_EXPECT(req[jss::tx_json].isMember(jss::Fee) && req[jss::tx_json][jss::Fee] == 8889);
2389 }
2390
2391 {
2392 // negative mult
2393 Json::Value req;
2395 R"({
2396 "fee_mult_max" : -5,
2397 "tx_json" : { }
2398 })",
2399 req);
2400 Json::Value result =
2401 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2402
2403 BEAST_EXPECT(RPC::contains_error(result));
2404 }
2405
2406 {
2407 // negative div
2408 Json::Value req;
2410 R"({
2411 "fee_div_max" : -2,
2412 "tx_json" : { }
2413 })",
2414 req);
2415 Json::Value result =
2416 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2417
2418 BEAST_EXPECT(RPC::contains_error(result));
2419 }
2420
2421 {
2422 // negative mult & div
2423 Json::Value req;
2425 R"({
2426 "fee_mult_max" : -2,
2427 "fee_div_max" : -3,
2428 "tx_json" : { }
2429 })",
2430 req);
2431 Json::Value result =
2432 checkFee(req, Role::ADMIN, true, env.app().config(), feeTrackOuter, env.app().getTxQ(), env.app());
2433
2434 BEAST_EXPECT(RPC::contains_error(result));
2435 }
2436
2437 env.close();
2438
2439 {
2440 // Call "sign" with nothing in the open ledger
2441 Json::Value toSign;
2442 toSign[jss::tx_json] = noop(env.master);
2443 toSign[jss::secret] = "masterpassphrase";
2444 auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2445 auto result = rpcResult[jss::result];
2446
2447 BEAST_EXPECT(!RPC::contains_error(result));
2448 BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "10");
2449 BEAST_EXPECT(
2450 result[jss::tx_json].isMember(jss::Sequence) &&
2451 result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue));
2452 }
2453
2454 {
2455 // Call "sign" with enough transactions in the open ledger
2456 // to escalate the fee.
2457 for (;;)
2458 {
2459 auto metrics = env.app().getTxQ().getMetrics(*env.current());
2460 if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
2461 break;
2462 env(noop(env.master));
2463 }
2464
2465 Json::Value toSign;
2466 toSign[jss::tx_json] = noop(env.master);
2467 toSign[jss::secret] = "masterpassphrase";
2468 toSign[jss::fee_mult_max] = 900;
2469 auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2470 auto result = rpcResult[jss::result];
2471
2472 BEAST_EXPECT(!RPC::contains_error(result));
2473 BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "7813");
2474 BEAST_EXPECT(
2475 result[jss::tx_json].isMember(jss::Sequence) &&
2476 result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue));
2477
2478 env.close();
2479 }
2480
2481 {
2482 // Call "sign" with higher server load
2483 {
2484 auto& feeTrack = env.app().getFeeTrack();
2485 BEAST_EXPECT(feeTrack.getLoadFactor() == 256);
2486 for (int i = 0; i < 8; ++i)
2487 feeTrack.raiseLocalFee();
2488 BEAST_EXPECT(feeTrack.getLoadFactor() == 1220);
2489 }
2490
2491 Json::Value toSign;
2492 toSign[jss::tx_json] = noop(env.master);
2493 toSign[jss::secret] = "masterpassphrase";
2494 auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2495 auto result = rpcResult[jss::result];
2496
2497 BEAST_EXPECT(!RPC::contains_error(result));
2498 BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "47");
2499 BEAST_EXPECT(
2500 result[jss::tx_json].isMember(jss::Sequence) &&
2501 result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue));
2502 }
2503
2504 {
2505 // Call "sign" with higher server load and
2506 // enough transactions to escalate the fee
2507 BEAST_EXPECT(feeTrackOuter.getLoadFactor() == 1220);
2508
2509 for (;;)
2510 {
2511 auto metrics = env.app().getTxQ().getMetrics(*env.current());
2512 if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel)
2513 break;
2514 env(noop(env.master), fee(47));
2515 }
2516
2517 Env_ss envs(env);
2518
2519 Json::Value toSign;
2520 toSign[jss::tx_json] = noop(env.master);
2521 toSign[jss::secret] = "masterpassphrase";
2522 // Max fee = 7000 drops
2523 toSign[jss::fee_mult_max] = 700;
2524 auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2525 auto result = rpcResult[jss::result];
2526
2527 BEAST_EXPECT(!RPC::contains_error(result));
2528 BEAST_EXPECT(result[jss::tx_json].isMember(jss::Fee) && result[jss::tx_json][jss::Fee] == "6806");
2529 BEAST_EXPECT(
2530 result[jss::tx_json].isMember(jss::Sequence) &&
2531 result[jss::tx_json][jss::Sequence].isConvertibleTo(Json::ValueType::uintValue));
2532 }
2533 }
2534
2535 void
2538 testcase("autofill NetworkID");
2539 using namespace test::jtx;
2540 Env env{*this, envconfig([&](std::unique_ptr<Config> cfg) {
2541 cfg->NETWORK_ID = 1025;
2542 return cfg;
2543 })};
2544
2545 {
2546 Json::Value toSign;
2547 toSign[jss::tx_json] = noop(env.master);
2548
2549 BEAST_EXPECT(!toSign[jss::tx_json].isMember(jss::NetworkID));
2550 toSign[jss::secret] = "masterpassphrase";
2551 auto rpcResult = env.rpc("json", "sign", to_string(toSign));
2552 auto result = rpcResult[jss::result];
2553
2554 BEAST_EXPECT(!RPC::contains_error(result));
2555 BEAST_EXPECT(result[jss::tx_json].isMember(jss::NetworkID) && result[jss::tx_json][jss::NetworkID] == 1025);
2556 }
2557 }
2558
2559 // A function that can be called as though it would process a transaction.
2560 static void
2562 {
2563 ;
2564 }
2565
2566 void
2568 {
2569 testcase("sign/submit RPCs");
2570 using namespace std::chrono_literals;
2571 using namespace test::jtx;
2572 // Use jtx to set up a ledger so the tests will do the right thing.
2573 Account const a{"a"}; // rnUy2SHTrB9DubsPmkJZUXTf5FcNDGrYEA
2574 Account const g{"g"}; // rLPwWB1itaUGMV8kbMLLysjGkEpTM2Soy4
2575 auto const USD = g["USD"];
2576
2577 // Account: rJrxi4Wxev4bnAGVNP9YCdKPdAoKfAmcsi
2578 // seed: sh1yJfwoi98zCygwijUzuHmJDeVKd
2579 Account const ed{"ed", KeyType::ed25519};
2580 // master is rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh.
2581 // "b" (not in the ledger) is rDg53Haik2475DJx8bjMDSDPj4VX7htaMd.
2582 // "c" (phantom signer) is rPcNzota6B8YBokhYtcTNqQVCngtbnWfux.
2583
2584 Env env(*this, envconfig([](std::unique_ptr<Config> cfg) {
2585 cfg->FEES.reference_fee = 10;
2586 return cfg;
2587 }));
2588 env.fund(XRP(100000), a, ed, g);
2589 env.close();
2590
2591 env(trust(a, USD(1000)));
2592 env(trust(env.master, USD(1000)));
2593 env(pay(g, a, USD(50)));
2594 env(pay(g, env.master, USD(50)));
2595 env.close();
2596
2598
2599 // A list of all the functions we want to test.
2600 using signFunc = Json::Value (*)(
2601 Json::Value params,
2602 unsigned int apiVersion,
2603 NetworkOPs::FailHard failType,
2604 Role role,
2605 std::chrono::seconds validatedLedgerAge,
2606 Application& app);
2607
2608 using submitFunc = Json::Value (*)(
2609 Json::Value params,
2610 unsigned int apiVersion,
2611 NetworkOPs::FailHard failType,
2612 Role role,
2613 std::chrono::seconds validatedLedgerAge,
2614 Application& app,
2615 ProcessTransactionFn const& processTransaction);
2616
2618
2619 static TestStuff const testFuncs[] = {
2620 TestStuff{transactionSign, nullptr, "sign", 0},
2621 TestStuff{nullptr, transactionSubmit, "submit", 1},
2622 TestStuff{transactionSignFor, nullptr, "sign_for", 2},
2623 TestStuff{nullptr, transactionSubmitMultiSigned, "submit_multisigned", 3}};
2624
2625 for (auto testFunc : testFuncs)
2626 {
2627 // For each JSON test.
2628 for (auto const& txnTest : txnTestArray)
2629 {
2630 Json::Value req;
2631 Json::Reader().parse(txnTest.json, req);
2632 if (RPC::contains_error(req))
2633 Throw<std::runtime_error>("Internal JSONRPC_test error. Bad test JSON.");
2634
2635 static Role const testedRoles[] = {Role::GUEST, Role::USER, Role::ADMIN, Role::FORBID};
2636
2637 for (Role testRole : testedRoles)
2638 {
2639 Json::Value result;
2640 auto const signFn = get<0>(testFunc);
2641 if (signFn != nullptr)
2642 {
2643 assert(get<1>(testFunc) == nullptr);
2644 result = signFn(req, 1, NetworkOPs::FailHard::yes, testRole, 1s, env.app());
2645 }
2646 else
2647 {
2648 auto const submitFn = get<1>(testFunc);
2649 assert(submitFn != nullptr);
2650 result = submitFn(req, 1, NetworkOPs::FailHard::yes, testRole, 1s, env.app(), processTxn);
2651 }
2652
2653 std::string errStr;
2654 if (RPC::contains_error(result))
2655 errStr = result["error_message"].asString();
2656
2657 if (errStr == txnTest.expMsg[get<3>(testFunc)])
2658 {
2659 pass();
2660 }
2661 else
2662 {
2663 std::ostringstream description;
2664 description << txnTest.description << " Called " << get<2>(testFunc) << "(). Got \'" << errStr
2665 << "\'";
2666 fail(description.str(), __FILE__, txnTest.line);
2667 }
2668 }
2669 }
2670 }
2671 }
2672
2673 void
2674 run() override
2675 {
2682 }
2683};
2684
2685BEAST_DEFINE_TESTSUITE(JSONRPC, rpc, xrpl);
2686
2687} // namespace RPC
2688} // namespace xrpl
Unserialize a JSON document into a Value.
Definition json_reader.h:18
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Represents a JSON value.
Definition json_value.h:131
UInt size() const
Number of values in array or object.
std::string asString() const
Returns the unquoted string value.
A testsuite class.
Definition suite.h:52
void pass()
Record a successful test condition.
Definition suite.h:495
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:148
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:517
virtual TxQ & getTxQ()=0
virtual Config & config()=0
virtual LoadFeeTrack & getFeeTrack()=0
Manages the current fee schedule.
std::uint32_t getLoadFactor() const
void run() override
Runs the suite.
static void fakeProcessTransaction(std::shared_ptr< Transaction > &, bool, bool, NetworkOPs::FailHard)
Immutable cryptographic account descriptor.
Definition Account.h:20
std::string const & human() const
Returns the human readable public key.
Definition Account.h:95
A transaction testing environment.
Definition Env.h:98
Application & app()
Definition Env.h:230
Account const & master
Definition Env.h:102
Json::Value rpc(unsigned apiVersion, std::unordered_map< std::string, std::string > const &headers, std::string const &cmd, Args &&... args)
Execute an RPC command.
Definition Env.h:749
std::shared_ptr< OpenView const > current() const
Returns the current ledger.
Definition Env.h:298
@ uintValue
unsigned integer value
Definition json_value.h:22
Json::Value transactionSubmit(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
std::function< void(std::shared_ptr< Transaction > &transaction, bool bUnlimited, bool bLocal, NetworkOPs::FailHard failType)> ProcessTransactionFn
Json::Value transactionSign(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
static constexpr TxnTestData txnTestArray[]
Json::Value checkFee(Json::Value &request, Role const role, bool doAutoFill, Config const &config, LoadFeeTrack const &feeTrack, TxQ const &txQ, Application const &app)
Fill in the fee on behalf of the client.
Json::Value transactionSignFor(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app)
Returns a Json::objectValue.
static constexpr std::integral_constant< unsigned, Version > apiVersion
Definition ApiVersion.h:39
Json::Value transactionSubmitMultiSigned(Json::Value jvRequest, unsigned apiVersion, NetworkOPs::FailHard failType, Role role, std::chrono::seconds validatedLedgerAge, Application &app, ProcessTransactionFn const &processTransaction)
Returns a Json::objectValue.
bool contains_error(Json::Value const &json)
Returns true if the json contains an rpc error specification.
Json::Value trust(AccountID const &account, STAmount const &amount, std::uint32_t flags=0)
Definition AMM.cpp:650
Json::Value pay(Account const &account, AccountID const &to, STAmount const &amount)
Definition AMM.cpp:662
XRP_t const XRP
Converts to XRP Issue or STAmount.
Definition amount.cpp:90
std::unique_ptr< Config > envconfig()
creates and initializes a default configuration for jtx::Env
Definition envconfig.h:35
Json::Value acctdelete(Account const &account, Account const &dest)
Delete account.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
bool set(T &target, std::string const &name, Section const &section)
Set a value from a configuration Section If the named value is not found or doesn't parse as a T,...
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:598
constexpr std::uint32_t tfAllOrNothing
Definition TxFlags.h:257
Role
Indicates the level of administrative permission to grant.
Definition Role.h:25
T str(T... args)
TxnTestData(TxnTestData const &)=delete
TxnTestData(TxnTestData &&)=delete
char const *const description
constexpr TxnTestData(char const *description_, int line_, char const *json_, std::array< char const *const, 4 > const &expMsg_)
TxnTestData & operator=(TxnTestData &&)=delete
char const *const json
TxnTestData & operator=(TxnTestData const &)=delete
std::array< char const *const, 4 > const expMsg