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