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