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